Mirror fuj infer's read-only mode: SyncOpts.DryRun skips WriteHeader, AppendValues, and SortByDateColumn, printing one "Dry run: would …" line per planned operation instead. ID-dedup still runs so the output reflects exactly what the next real sync would write. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2.0 KiB
Plan: add --dry-run to fuj sync
Context
fuj infer already supports --dry-run (it builds the planned BatchUpdateValues
operations, prints them, and skips the actual write — see
go/internal/services/banksync/infer.go:136-156 and the
Dry run: would update N row(s). line in go/cmd/fuj/main.go:209-213).
fuj sync had no equivalent. It always committed three potential writes to the
payments sheet: WriteHeader (if the header row is missing/wrong), AppendValues
(for each new Fio transaction), and SortByDateColumn (if --sort, default true).
For inspecting what a sync would do — useful when debugging dedupe, sanity-checking
a date window, or wiring up the command for the first time on a new account — the
only options were pointing at a throwaway spreadsheet or reading the diff after the fact.
This change mirrors infer's read-only mode for sync: same flag name, same output
style, same "build the data structures, print instead of writing" shape.
Files modified
go/internal/services/banksync/sync.go—DryRun boolfield added toSyncOpts; three write points gated onopts.DryRungo/cmd/fuj/main.go—--dry-runflag added tosyncCmd; final println split on*dryRungo/internal/services/banksync/sync_test.go—TestSyncToSheets_DryRunaddedCHANGELOG.md— entry added
Behaviour
When --dry-run is set:
- If the sheet header is missing/wrong → prints
Dry run: would write header row; skipsWriteHeader - For each non-deduped Fio transaction → prints
Dry run: would append date=… amount=… sender=… vs=… message=…; skipsAppendValues - If
--sortis true → printsDry run: would sort by date; skipsSortByDateColumn - Returns
len(newRows)so the caller can printDry run: would sync N new transaction(s).
The existing ID-dedup logic runs in full even during dry-run (reads the sheet, builds existingIDs), so the output reflects exactly what the next real sync would do.