Skip to content

feat(google): add import google and export google commands - #90

Merged
AnnatarHe merged 3 commits into
mainfrom
claude/great-feynman-cznfoy
Oct 7, 2026
Merged

AnnatarHe merged 3 commits into
mainfrom
claude/great-feynman-cznfoy

Conversation

@AnnatarHe

@AnnatarHe AnnatarHe commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds Google Photos import and export to the CLI, built on the SDK's new services.googlePhotos (rawback-app/sdk#53), which wraps the server API from rawback-app/server#478.

  • rawback import google [--yes] [--no-wait] [--json]: connects the Google account if needed (it prints the authorization link to stderr, opens the browser, and waits for the server to report the account). It then opens Google Photos' own picker, waits for the picks, and shows what was picked, including how many videos will be skipped and the fact that Google withholds GPS. After you confirm, it starts the server-side import and watches it to the end. The import runs through the same pipeline as an SFTP upload, and its upload session appears as GOOGLE PHOTOS in rawback uploads.
  • rawback export google: takes one selection, either --all (the default), --from/--to, --album <id> (repeatable), or --image <id> (repeatable) together with --images-file <path|->. Options are --include-secret, --include-archived, --no-skip-exported, --album-title <title> (default "Rawback"), --no-album, --file original|fullsize, --dry-run, --yes, --no-wait and --json. It shows the server's preview (count, size, already exported), confirms, starts, and watches.
  • rawback google status | connect | disconnect [--yes] | jobs [--kind --limit --offset] | job <id> [--items --limit --offset --watch] | cancel <id>: connect is the command the error messages point to, and job <id> --watch resumes a --no-wait run.

Behaviour:

  • --json keeps stdout to machine-readable output only; links, progress and prompts go to stderr.
  • Because --json can't prompt, it requires --yes (--dry-run also works for export).
  • Both that check and the interactive-terminal check run before any network call or browser window.
  • Server reasons map to the next command to run, for example "Google Photos is not enabled on this server." and "Run rawback google connect to reconnect."
  • Ctrl-C exits 130. While picking, it ends the unused picker session; while watching, it reports the job and leaves it running.
  • A job that fails, is cancelled, or finishes with failed items exits 1.

Docs: README quick start and docs/commands.md. docs/configuration.md is unchanged, because nothing new is stored under ~/.rawback/; the Google grant lives on the server.

⚠️ Blocked on the SDK release

package.json now pins @rawback/sdk to 0.3.5. That is the version release-please will publish from the SDK PR's feat commit (bump-patch-for-minor-pre-major), and it doesn't exist on npm yet. CI's bun install --frozen-lockfile will fail until 0.3.5 is published. After that, run bun install and commit the regenerated bun.lock. I deliberately didn't commit a lockfile that points at a local path or a guessed integrity hash.

Note: bun.lock at main already pins @rawback/sdk@0.3.3 while package.json says 0.3.4, so the frozen install was already out of step before this change. Regenerating the lockfile after 0.3.5 is published fixes both.

Testing

  • To validate locally, I built the SDK from the SDK PR branch and overlaid it into the git-ignored node_modules/@rawback/sdk, without changing the committed files. Against that build, bun run check (typecheck, test, lint, format:check, build) exits 0 with 715 tests across 46 files.
  • New tests: 54 unit tests with injected dependencies in test/google-photos.test.ts, covering the connect, pick, import and export flows, cancellation, reason mapping and output. There are also 8 spawn-based help and validation tests in test/commands-cli.test.ts, covering selection conflicts, an invalid --file, bad dates, and --json without --yes.
  • ./dist/rawback --version prints 1.0.15, matching package.json, so the CI version assertion holds.
  • Nothing was run against a live server.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Nohf85JZVW1mqMnV17KH6o


Generated by Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

`rawback import google` connects the Google account when needed, opens
Google Photos' own picker, shows what was picked (videos are skipped),
confirms, starts the server-side import and watches it to the end.
`rawback export google` validates one selection (--all, --from/--to,
--album, --image/--images-file) with --include-secret,
--include-archived, --no-skip-exported, --album-title/--no-album and
--file original|fullsize, previews it (--dry-run stops there), confirms
and watches the export. Both take --yes, --no-wait and --json.

`rawback google` adds status, connect, disconnect, jobs, job <id>
(--watch, --items) and cancel <id>.

Links, progress and prompts never touch stdout under --json, which
needs --yes because it cannot prompt. The server's Google Photos
reasons map to the command to run next. Ctrl-C while waiting ends an
unused picker session, or stops watching and leaves the job running,
and exits 130. Upload sessions from an import are labelled GOOGLE
PHOTOS in `rawback uploads`.

Requires @rawback/sdk 0.3.5 for its Google Photos service; bun.lock is
regenerated once that version is published.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nohf85JZVW1mqMnV17KH6o
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copy link
Copy Markdown
Contributor Author

/claude review


Generated by Claude Code

@claude

claude Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Claude finished @AnnatarHe's task in 25s —— View job


Review of #90: import google / export google / google *

I read src/google-photos.ts in full. I did not review src/cli.ts, src/features/google-photos/view.ts, the tests or the docs, and I did not run bun run check. SDK behaviour (GooglePhotosService) is assumed from the PR description.

Overall the flow is well structured. Validation runs before any network call, --json keeps prose on stderr, picker-session cleanup is handled, and Ctrl-C leaves the job running and says so. Findings, most important first:

1. Merge blocker: @rawback/sdk is pinned to an unpublished version (package.json)
The PR already says this. 0.3.5 doesn't exist yet, so bun install --frozen-lockfile fails in CI. Don't merge until the SDK is released and bun.lock is regenerated and committed on this branch.

2. Ctrl-C during startImport can leak the picker session (src/google-photos.ts:553-561)
The catch sets owned = error instanceof RawbackGraphqlError. An abort mid-request isn't a RawbackGraphqlError, so owned becomes false and the session isn't deleted. The comment explains why this is deliberate for dropped connections, since the server may have started the import. For an abort the outcome is also unknown, so the choice is defensible. Still, the user gets "Stopped…" with no hint that an import may be running. Consider checking rawback google jobs in that message, or treating signal.aborted as its own case.

3. --to date-only bound drops the last second (src/google-photos.ts:617)
T23:59:59Z excludes photos taken in 23:59:59.001–.999. If the server's to is inclusive, T23:59:59.999Z is more accurate. If it's exclusive, use the next day's T00:00:00Z. Please check which the server uses.

4. --dry-run skips the maxExportItems check (src/google-photos.ts:787-806)
The dry run returns before the "one export sends at most N" error. So --dry-run reports success for a selection that the real run rejects. Since dry-run is the way to check a selection, it should either run that check or print a warning.

5. Plain job --watch on an already-finished job doesn't re-check status in the same way (src/google-photos.ts:1051-1058)
assertJobSucceeded runs on the re-read detail, which is correct. But when --watch isn't set, a failed job exits 0. That seems intentional for a read command, but docs/commands.md should say so. I couldn't confirm that it does.

6. Minor

  • readImagesFile splits on whitespace and commas, so # comment lines in an IDs file fail with a parse error. Fine if intended; the help text says "one per line".
  • defaultPrompts checks process.stdout.isTTY. With --json and --yes that's never reached, but rawback import google > out.txt without --yes gives the non-interactive error only after the status call. The PR says the check runs before network calls, so it's worth confirming this ordering in cli.ts.

Tests: The 54 unit tests plus 8 spawn tests look like they cover the main paths from the PR description. I'd add cases for items 2–4 once decided.

No code changes were made.
· branch claude/great-feynman-cznfoy

…e starting

- `export google --dry-run` now refuses a selection over maxExportItems
  exactly as the real run does, since a dry run is how a selection is
  checked.
- Ctrl-C while the server starts an import or export now says the job may
  have started anyway and points at `rawback google jobs`; the picker
  session is still left alone, because the import may be using it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nohf85JZVW1mqMnV17KH6o

AnnatarHe commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Going through each point:

  1. Unpublished @rawback/sdk 0.3.5: still the merge blocker. Once rawback-app/sdk#53 is merged and released, I'll regenerate bun.lock on this branch.
  2. Ctrl-C during startImport/startExport: fixed in e1dffb1. Both calls now run under stage(), so an interrupt reports "Stopped while starting the import; it may have started anyway. See 'rawback google jobs'." The picker session is still left alone, because the import may already be using it. New tests cover both kinds.
  3. --to drops the last second: fixed on the server (rawback-app/server#478, e2edce7). to now includes the whole second it names, so T23:59:59Z keeps a photo taken at 23:59:59.5. The web and iOS clients send the same bound, so the fix belongs there. Date-only bounds stay UTC days, as the help text and docs/commands.md say.
  4. --dry-run skips the limit check: real, fixed in e1dffb1. The maxExportItems check now runs before the dry run returns, so a dry run fails exactly as the real run would. This is covered in the existing limit test.
  5. job without --watch exits 0 on a failed job: intended. A plain job is a read, and the options table in docs/commands.md scopes the exit-1 behaviour to --watch.
  6. Minor:
    • IDs file: the help text documents one ID per line. The parser also accepts spaces and commas, which is more lenient than documented. Comment lines are not supported, and a stray # gets an explicit parse error instead of being skipped silently, so I'm leaving it as it is.
    • TTY check: it runs after the status call by design. --dry-run never prompts, and the status call is what reports an unconnected account first.

bun run check passes (717 tests), run against the locally built SDK from the companion PR.


Generated by Claude Code

0.3.5 is published, so the frozen install resolves again. The lockfile
also stops pinning 0.3.3 under a package.json that already asked for
0.3.4 on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nohf85JZVW1mqMnV17KH6o
@AnnatarHe
AnnatarHe merged commit 6255151 into main Oct 7, 2026
7 checks passed
@AnnatarHe
AnnatarHe deleted the claude/great-feynman-cznfoy branch October 7, 2026 06:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants