feat: manifest-aware push — generator auto-add, autoAdd toggle, --frozen/--dry-run (EGB-677 stage 1)
- push syncs FROM the manifest; v1 store layout unchanged (nested entries land at <project>/<relpath>.age, same shape -w always used) - discovery (root globs + quiet package.json workspace re-scan when a manifest exists) feeds the manifest as a generator; new files auto-add with ==> notice + undo guidance - options.autoAdd committed toggle (default ON when absent); explicit false warns on undeclared files instead of enrolling them - push --frozen: declared-only for one invocation; push --dry-run: reports would-add/would-sync, touches nothing - bootstrap ordering: manifest written only after >=1 blob encrypts - declared-but-missing warns and continues; unsafe manifest path dies - jq // falsy gotcha: explicit autoAdd:false compared directly
This commit is contained in:
parent
18018dbd3b
commit
884da0965c
2 changed files with 265 additions and 3 deletions
|
|
@ -123,3 +123,124 @@ load test_helper
|
|||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"symlink"* ]] || false
|
||||
}
|
||||
|
||||
# ─── B: push from manifest — generators, autoAdd, --frozen/--dry-run ───
|
||||
|
||||
@test "push with manifest syncs nested declared file into v1 store layout" {
|
||||
init_with_remote
|
||||
create_project_dir nestproj
|
||||
mkdir -p packages/web
|
||||
echo "K=v" > packages/web/.env.development
|
||||
"$SECRETS_BIN" add packages/web/.env.development >/dev/null
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 0 ]
|
||||
[ -f "$SECRETS_DIR/nestproj/packages/web/.env.development.age" ]
|
||||
}
|
||||
|
||||
@test "push auto-adds newly discovered root files to an existing manifest" {
|
||||
init_with_remote
|
||||
create_project_dir autoproj
|
||||
"$SECRETS_BIN" add .env >/dev/null
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Added"* ]] || false
|
||||
run jq -r '.dotenv | index(".env.staging") != null' .secrets.json
|
||||
[ "$output" = "true" ]
|
||||
[ -f "$SECRETS_DIR/autoproj/.env.staging.age" ]
|
||||
}
|
||||
|
||||
@test "bootstrap: plain push creates the manifest from discovered files" {
|
||||
init_with_remote
|
||||
create_project_dir bootproj
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 0 ]
|
||||
[ -f ".secrets.json" ]
|
||||
run jq -r '.dotenv | length' .secrets.json
|
||||
[ "$output" = "2" ]
|
||||
}
|
||||
|
||||
@test "failed push leaves no bootstrap manifest behind" {
|
||||
init_with_remote
|
||||
mkdir -p "$WORK_DIR/emptyproj"
|
||||
cd "$WORK_DIR/emptyproj"
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 1 ]
|
||||
[ ! -f ".secrets.json" ]
|
||||
}
|
||||
|
||||
@test "autoAdd=false: undeclared discovered file is warned about, not added or synced" {
|
||||
init_with_remote
|
||||
create_project_dir noaddproj
|
||||
printf '{"version":2,"options":{"autoAdd":false},"dotenv":[".env"]}\n' > .secrets.json
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"not declared"* ]] || false
|
||||
run jq -r '.dotenv | index(".env.staging") != null' .secrets.json
|
||||
[ "$output" = "false" ]
|
||||
[ -f "$SECRETS_DIR/noaddproj/.env.age" ]
|
||||
[ ! -f "$SECRETS_DIR/noaddproj/.env.staging.age" ]
|
||||
}
|
||||
|
||||
@test "push --frozen skips auto-add even when autoAdd is on" {
|
||||
init_with_remote
|
||||
create_project_dir frozenproj
|
||||
"$SECRETS_BIN" add .env >/dev/null
|
||||
run "$SECRETS_BIN" push --frozen
|
||||
[ "$status" -eq 0 ]
|
||||
run jq -r '.dotenv | index(".env.staging") != null' .secrets.json
|
||||
[ "$output" = "false" ]
|
||||
[ ! -f "$SECRETS_DIR/frozenproj/.env.staging.age" ]
|
||||
# declared entry still synced under the REAL project name
|
||||
[ -f "$SECRETS_DIR/frozenproj/.env.age" ]
|
||||
}
|
||||
|
||||
@test "push --dry-run reports would-add entries and changes nothing" {
|
||||
init_with_remote
|
||||
create_project_dir dryproj
|
||||
"$SECRETS_BIN" add .env >/dev/null
|
||||
cp .secrets.json "$TEST_TMPDIR/manifest-before.json"
|
||||
run "$SECRETS_BIN" push --dry-run
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *".env.staging"* ]] || false
|
||||
cmp -s .secrets.json "$TEST_TMPDIR/manifest-before.json"
|
||||
[ ! -f "$SECRETS_DIR/dryproj/.env.age" ]
|
||||
# nothing committed to the store at all
|
||||
[ "$(git -C "$SECRETS_DIR" rev-list --count HEAD)" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "plain push re-scans package.json workspaces when a manifest exists" {
|
||||
init_with_remote
|
||||
local mono="$WORK_DIR/wsproj"
|
||||
mkdir -p "$mono/packages/api"
|
||||
printf '{"workspaces": ["packages/*"]}\n' > "$mono/package.json"
|
||||
echo "ROOT=1" > "$mono/.env"
|
||||
echo "API=1" > "$mono/packages/api/.dev.vars"
|
||||
git init "$mono" >/dev/null 2>&1
|
||||
cd "$mono"
|
||||
"$SECRETS_BIN" add .env >/dev/null
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 0 ]
|
||||
run jq -r '.dotenv | index("packages/api/.dev.vars") != null' .secrets.json
|
||||
[ "$output" = "true" ]
|
||||
[ -f "$SECRETS_DIR/wsproj/packages/api/.dev.vars.age" ]
|
||||
}
|
||||
|
||||
@test "declared-but-missing file warns and push continues" {
|
||||
init_with_remote
|
||||
create_project_dir missproj
|
||||
"$SECRETS_BIN" add .env >/dev/null
|
||||
printf '{"version":2,"dotenv":[".env",".env.gone"]}\n' > .secrets.json
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *".env.gone"* ]] || false
|
||||
[ -f "$SECRETS_DIR/missproj/.env.age" ]
|
||||
}
|
||||
|
||||
@test "unsafe dotenv entry in a committed manifest dies on push" {
|
||||
init_with_remote
|
||||
create_project_dir evilproj
|
||||
printf '{"version":2,"dotenv":["../escape/.env"]}\n' > .secrets.json
|
||||
run "$SECRETS_BIN" push
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"project-relative"* ]] || false
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue