fix: surface store sync failures + add secrets sync (EGB-1230, EGB-1231)
EGB-1230: `cmd_pull` synced the store with `git pull >/dev/null 2>&1` under `set -euo pipefail`. A store that couldn't fast-forward killed the script at that line with git's exit 128 and nothing on stdout or stderr — a banner, no restored files, no reason, and invisible in a pipeline. The sync now routes through `_store_sync_pull`, which guards the pull, captures git's output as the diagnosis, and dies naming the store path and `secrets sync`. It is now `--ff-only` to match the push path, so a plain pull can no longer manufacture a merge commit in the store. EGB-1231: once a store diverged there was no way out — push demanded a fast-forward and pointed at pull, which couldn't fast-forward either, so the advice looped and recovery meant hand-running git next to encrypted blobs. Adds `secrets sync`: fetch, stash, rebase onto the remote, restore the stash, then a confirmation-gated push of local commits (`--yes` to skip the prompt, `--dry-run` to report only). Non-destructive by construction — no merge, no force-push, no `reset --hard`, no `stash drop`; a rebase conflict names the conflicting files and leaves the store exactly as found. `secrets which` gains a `remote:` line reporting ahead/behind/dirty, and push's dead-end message now points at `sync`. test/sync.bats: 25 new tests. Full suite 353/353 green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BrUoYuUMoTj91rzV4vxGPB
This commit is contained in:
parent
2558ea3c23
commit
dd6a025fa0
6 changed files with 681 additions and 8 deletions
29
README.md
29
README.md
|
|
@ -193,6 +193,9 @@ secrets clear
|
|||
| `secrets recipients add <age1…> [--name N]` | Add a recipient key to the store and immediately re-encrypt every blob to the new set |
|
||||
| `secrets recipients rm <key\|name> [--yes]` | Remove a recipient and re-encrypt the store; `--yes` required when removing your own key |
|
||||
| `secrets reencrypt` | Re-encrypt every blob to the current recipients (idempotent — useful after a manual edit or partial failure) |
|
||||
| `secrets sync` | Reconcile a store that has diverged from its remote: stash local blob edits, rebase onto the remote, restore the stash, then offer to publish your local commits. Never merges, force-pushes, or hard-resets |
|
||||
| `secrets sync --dry-run` | Report the store's ahead/behind/dirty state and what a reconcile would do; changes nothing |
|
||||
| `secrets sync --yes` | Reconcile and publish local commits without the confirmation prompt (for scripts) |
|
||||
| `secrets upgrade` | Self-update the tool: `git pull --ff-only` on the `secrets` checkout, report old → new version, then re-check store version-skew. No auto-update, no background checks |
|
||||
| `secrets upgrade --check` | Report whether an update is available (without pulling); changes nothing |
|
||||
|
||||
|
|
@ -211,6 +214,32 @@ Store-format v2 is **additive** — an upgraded client reads either blob suffix
|
|||
|
||||
And you'll be told when you're behind: if a store was last written by a newer `secrets` than the one you're running, any command prints a one-line nudge to stderr (non-fatal) — and `secrets which` shows the store's `written-by:` version. Stores written by older builds (no version stamp) stay silent.
|
||||
|
||||
### When the store diverges
|
||||
|
||||
The store is a git repo, so two machines pushing at once can leave your clone
|
||||
both ahead and behind its remote. `secrets push` needs a fast-forward and
|
||||
`secrets pull` won't silently merge, so both stop and tell you to run:
|
||||
|
||||
```bash
|
||||
secrets sync
|
||||
```
|
||||
|
||||
`sync` fetches, stashes any uncommitted blob edits, rebases your local commits
|
||||
onto the remote, restores the stash, and then *asks* before publishing your
|
||||
commits to the shared store (`--yes` skips the prompt; `--dry-run` just
|
||||
reports). If the rebase conflicts, it aborts, restores your stash, names the
|
||||
conflicting files, and leaves the store exactly as it found it — nothing in
|
||||
the path force-pushes, hard-resets, or drops a stash.
|
||||
|
||||
`secrets which` now reports the same state up front, so you can see it coming:
|
||||
|
||||
```
|
||||
store: /Users/you/.secrets
|
||||
source: default
|
||||
format: v2
|
||||
remote: ahead 1, behind 11, 3 modified (run: secrets sync)
|
||||
```
|
||||
|
||||
### Automatic project detection
|
||||
|
||||
When you run `secrets push` or `secrets pull` without specifying a project name, the tool figures out which project you're in by:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue