feat(gmail): add gmail extract-attachments command - #1512
Merged
Conversation
CoverageTotal: 97.31% 🔴 0 pp vs Comparing
Patch coveragePatch: 96.9% (562/580 new lines covered)
Uncovered new lines (18)
|
newhoggy
force-pushed
the
issue-1510-gmail-extract-attachments
branch
2 times, most recently
from
August 6, 2026 12:28
212368d to
3f7cb8a
Compare
Retroactively extracts attachments for messages `gmail sync`/`sync-all` already archived, without re-fetching from Gmail. Presence-on-disk is the archive's idempotence mechanism (ADR-0064), so turning on `--extract-attachments` never backfilled already-archived mail even under `--full` — a gap ADR-0065 documented and, until now, the only fix was deleting the affected `.eml` files and re-running `--full --extract-attachments`. - New `src/cli/gmail/extract_attachments/` module (`engine.rs` + `report.rs`), mirroring `sync.rs`'s own file+dir split and ADR-0064's compute-render-decide report pattern. For each non-deleted manifest record it trusts `attachment_count > 0` as a fast-path filter, skips a record whose `attachments/` directory already exists (idempotent, safe to re-run), and otherwise re-runs the exact same MIME-parse-and-write path `gmail sync --extract-attachments` uses against the on-disk `.eml` bytes. A missing/unreadable `.eml` accumulates a per-message error without aborting the batch; a real-parser/heuristic disagreement (finds nothing) is a silent no-op. - `sync/engine.rs`'s `manifest_path`/`write_atomic` helpers promoted to `pub(crate)` so no new writing logic was needed, just a new caller. - Two small additive `Manifest` API changes: `ManifestRecord::internal_date_utc()` (mirrors `Message::internal_date_utc`) and `Manifest::records_not_deleted()` (the full-record counterpart of the existing id-only `ids_not_deleted`). - Dispatched in `GmailCommand::execute`/`GmailSubcommands` (`src/cli/gmail.rs`) alongside `sync-all`'s no-client special case, before credential resolution — purely local and offline, so `--account` is rejected the same way `sync-all` rejects it. - `--dry-run` parses every candidate for an accurate count rather than echoing the heuristic, and writes nothing; `-o table|json|yaml|yamls|jsonl` output mirrors `sync`'s own report formats.
- ADR-0065's "No retroactive backfill, for free" consequence gains a backward-reference noting the gap is now closed by `gmail extract-attachments` (#1510), mirroring how ADR-0064 already backward-references ADR-0065 for the same kind of follow-up. - docs/gmail.md gains a new "Extract attachments" section (Table of Contents included) following the Sync/Sync all accounts style; the `--extract-attachments` paragraph's old delete-and-refetch advice now points at the new command instead. - CHANGELOG.md's Unreleased/Added section gains an entry for #1510, matching the existing Gmail feature entries.
newhoggy
force-pushed
the
issue-1510-gmail-extract-attachments
branch
from
August 6, 2026 13:02
3f7cb8a to
3a67d71
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #1510.
omni-dev gmail extract-attachments --output-dir PATH [--dry-run] [--quiet] [-o FORMAT], retroactively extracting attachments for messagesgmail sync/sync-allalready archived, without re-fetching from Gmail.gmail sync --extract-attachmentsonly ever extracted attachments for messages fetched during that run — presence-on-disk is the archive's idempotence mechanism (ADR-0064), so turning the flag on never backfilled already-archived mail, even under--full. ADR-0065 named this gap explicitly; until now the only fix was deleting the affected.emlfiles and re-running--full --extract-attachments..emlfiles already under--output-dir, never resolves aGmailClient— dispatched alongsidesync-all's no-client special case inGmailCommand::execute, before credential resolution.--accountis rejected the same waysync-allrejects it.gmail sync --extract-attachments's extraction path verbatim against on-disk bytes: trustsattachment_count > 0as a fast-path filter (the same cheap heuristic scansyncalways runs), skips any record whoseattachments/directory already exists (idempotent, safe to re-run), and calls the exact same MIME-parse-and-write helpersfetch_and_write_oneuses (manifest_path/write_atomicpromoted topub(crate)— no new writing logic needed).ExtractAttachmentsReport, mirroringSyncReport's compute-render-decide shape from ADR-0064 Decision 4); a real-parser/heuristic disagreement is a silent no-op, not an error.--dry-runparses every candidate for an accurate count rather than echoing the heuristic, and writes nothing.docs/gmail.md#extract-attachmentsfor the operator-facing docs.Commit-by-commit:
feat(gmail): add gmail extract-attachments command— the engine, report types, CLI wiring, and reusedsync/manifesthelpers.docs(docs,release): document gmail extract-attachments, amend ADR-0065— ADR amendment, docs, changelog.Test plan
cargo buildcargo fmt --checkcargo clippy --all-targets -- -D warningscargo test(full suite) — all pass except the two pre-existing, unrelateddaemon_test.rslifecycle-flake tests (zero files touched by this branch overlap withdaemon)cargo insta test --test integration_test—help_all_outputsnapshot reviewed and accepted for the new subcommand--dry-runaccuracy, per-message error accumulation, heuristic/parser disagreement, and CLI dispatch routing (no client resolved,--accountrejected)