feat: manifest-driven pull — nested restore, restore-time rail, empty no-op (EGB-677 stage 1)

- pull with .secrets.json restores exactly the declared entries (nested
  paths get mkdir -p); stray store blobs are not restored
- dotenv rail re-runs at restore time: unsafe entries warn+skip (pull
  never dies on one bad entry), missing blobs warn with a directed hint
- empty manifest = warn no-op instead of a confusing 'not found' death
- manifest-less projects keep the legacy glob pull verbatim
This commit is contained in:
Brian Majewski 2026-06-07 08:49:02 -07:00
parent d7e1400487
commit 0049584d9b
2 changed files with 131 additions and 0 deletions

View file

@ -379,3 +379,86 @@ m_file_src() { mkdir -p "$HOME/keystores"; printf 'KS\x00\x01\x02\xffDATA\n' >
[ "$status" -eq 0 ]
[[ "$output" == *"superseded"* ]] || false
}
# ─── D: pull from manifest — nested restore, restore-time rail ─────────
@test "pull restores manifest-declared nested file (mkdir -p)" {
init_with_remote
create_project_dir nestpull
mkdir -p packages/web
echo "K=v" > packages/web/.env.development
"$SECRETS_BIN" add packages/web/.env.development >/dev/null
"$SECRETS_BIN" push >/dev/null 2>&1
rm -rf packages
run "$SECRETS_BIN" pull
[ "$status" -eq 0 ]
[ -f packages/web/.env.development ]
[ "$(cat packages/web/.env.development)" = "K=v" ]
}
@test "pull with manifest restores only declared entries" {
init_with_remote
create_project_dir onlydecl
"$SECRETS_BIN" push >/dev/null 2>&1
# plant an undeclared stray blob in the store
local pubkey; pubkey=$(age-keygen -y "$SECRETS_DIR/key.txt")
echo "S=1" | age -r "$pubkey" -o "$SECRETS_DIR/onlydecl/.env.stray.age"
rm -f .env .env.staging
run "$SECRETS_BIN" pull
[ "$status" -eq 0 ]
[ -f .env ]
[ ! -f .env.stray ]
}
@test "pull warns and skips an unsafe manifest entry, restores the rest" {
init_with_remote
create_project_dir unsafepull
"$SECRETS_BIN" add .env >/dev/null
"$SECRETS_BIN" push >/dev/null 2>&1
printf '{"version":2,"dotenv":[".env","../escape/.env"]}\n' > .secrets.json
rm -f .env
run "$SECRETS_BIN" pull
[ "$status" -eq 0 ]
[[ "$output" == *"skipping unsafe"* ]] || false
[ -f .env ]
[ ! -f "$WORK_DIR/escape/.env" ]
}
@test "pull on an empty manifest is a warn no-op" {
init_with_remote
mkdir -p "$WORK_DIR/emptypull"
cd "$WORK_DIR/emptypull"
printf '{"version":2,"dotenv":[]}\n' > .secrets.json
run "$SECRETS_BIN" pull emptypull
[ "$status" -eq 0 ]
[[ "$output" == *"declares nothing"* ]] || false
}
@test "pull warns when a declared entry has no blob in the store" {
init_with_remote
create_project_dir nopullblob
"$SECRETS_BIN" push >/dev/null 2>&1
jq '.dotenv += [".env.missing"]' .secrets.json > .secrets.json.tmp && mv .secrets.json.tmp .secrets.json
run "$SECRETS_BIN" pull
[ "$status" -eq 0 ]
[[ "$output" == *".env.missing"* ]] || false
[[ "$output" == *"no encrypted data"* ]] || false
}
@test "machine-2 flow: committed manifest + pull restores everything" {
init_with_remote
create_project_dir machine1
mkdir -p packages/api
echo "API=1" > packages/api/.dev.vars
"$SECRETS_BIN" add packages/api/.dev.vars >/dev/null
"$SECRETS_BIN" push m2proj >/dev/null 2>&1
# simulate machine 2: fresh dir, only the committed manifest present
mkdir -p "$WORK_DIR/machine2"
cp .secrets.json "$WORK_DIR/machine2/"
cd "$WORK_DIR/machine2"
run "$SECRETS_BIN" pull m2proj
[ "$status" -eq 0 ]
[ -f .env ]
[ -f packages/api/.dev.vars ]
[ "$(cat packages/api/.dev.vars)" = "API=1" ]
}