feat: secrets migrate --status surveys per-project v2 readiness (EGB-710)
This commit is contained in:
parent
e75627deff
commit
469b08203b
2 changed files with 81 additions and 4 deletions
51
secrets
51
secrets
|
|
@ -2186,6 +2186,46 @@ _migrate_project() {
|
||||||
# Store-wide finalize: the only destructive step. Refuses unless verify --all
|
# Store-wide finalize: the only destructive step. Refuses unless verify --all
|
||||||
# is green and every v1 properties blob has a v2 twin. Cuts a recovery tag,
|
# is green and every v1 properties blob has a v2 twin. Cuts a recovery tag,
|
||||||
# stamps the marker, then drops v1 blobs.
|
# stamps the marker, then drops v1 blobs.
|
||||||
|
# Read-only survey: walk every project dir in the store and report each one's
|
||||||
|
# v2-readiness from the blobs on disk (no manifest, no decryption). Exits
|
||||||
|
# non-zero when any v1 properties blob lacks a v2 twin (i.e. the store is not
|
||||||
|
# yet finalize-ready) so it can gate scripting, mirroring `verify`'s posture.
|
||||||
|
_migrate_status() {
|
||||||
|
if [ "$(_store_format)" = "2" ]; then
|
||||||
|
info "Store format: v2 (finalized) — nothing to migrate."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "Store format: v1 (not finalized). Per-project migration status:"
|
||||||
|
local any_untwinned=0 dir project v1 untwinned f new
|
||||||
|
for dir in "$SECRETS_DIR"/*/; do
|
||||||
|
[ -d "$dir" ] || continue
|
||||||
|
project=$(basename "$dir")
|
||||||
|
case "$project" in .*) continue ;; esac
|
||||||
|
v1=0; untwinned=0
|
||||||
|
while IFS= read -r f; do
|
||||||
|
[ -f "$f" ] || continue
|
||||||
|
v1=$((v1 + 1))
|
||||||
|
new="${f%.gradle-properties.age}.properties.age"
|
||||||
|
[ -f "$new" ] || untwinned=$((untwinned + 1))
|
||||||
|
done < <(find "$dir" -type f -name '*.gradle-properties.age' 2>/dev/null)
|
||||||
|
if [ "$v1" -eq 0 ]; then
|
||||||
|
echo " $project: v2-ready (no v1 properties blobs)"
|
||||||
|
elif [ "$untwinned" -eq 0 ]; then
|
||||||
|
echo " $project: migrated ($v1 v1 blob(s), all twinned)"
|
||||||
|
else
|
||||||
|
echo " $project: NEEDS MIGRATE ($untwinned of $v1 v1 blob(s) un-twinned) — cd into the project and run 'secrets migrate'"
|
||||||
|
any_untwinned=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
if [ "$any_untwinned" -eq 1 ]; then
|
||||||
|
echo "Not finalize-ready: migrate the projects marked NEEDS MIGRATE, then run 'secrets migrate --finalize'."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "Finalize-ready: every v1 properties blob has a v2 twin. Run 'secrets migrate --finalize' once every machine is upgraded."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
_migrate_finalize() {
|
_migrate_finalize() {
|
||||||
local force="$1"
|
local force="$1"
|
||||||
check_key
|
check_key
|
||||||
|
|
@ -2260,17 +2300,20 @@ $untwinned cd into each project and run 'secrets migrate', then re-run 'secrets
|
||||||
cmd_migrate() {
|
cmd_migrate() {
|
||||||
resolve_store
|
resolve_store
|
||||||
check_initialized
|
check_initialized
|
||||||
local dry_run=false finalize=false force=false
|
local dry_run=false finalize=false force=false status=false
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--dry-run) dry_run=true; shift ;;
|
--dry-run) dry_run=true; shift ;;
|
||||||
--finalize) finalize=true; shift ;;
|
--finalize) finalize=true; shift ;;
|
||||||
|
--status) status=true; shift ;;
|
||||||
--yes) force=true; shift ;;
|
--yes) force=true; shift ;;
|
||||||
-*) die "Unknown migrate flag: $1. Usage: secrets migrate [--dry-run | --finalize] [--yes]" ;;
|
-*) die "Unknown migrate flag: $1. Usage: secrets migrate [--dry-run | --status | --finalize] [--yes]" ;;
|
||||||
*) die "migrate takes no project argument. Run it from inside a project (copy-forward) or use --finalize (store-wide)." ;;
|
*) die "migrate takes no project argument. Run it from inside a project (copy-forward), or use --status / --finalize (store-wide)." ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
if [ "$finalize" = true ]; then
|
if [ "$status" = true ]; then
|
||||||
|
_migrate_status
|
||||||
|
elif [ "$finalize" = true ]; then
|
||||||
_migrate_finalize "$force"
|
_migrate_finalize "$force"
|
||||||
else
|
else
|
||||||
_migrate_project "$dry_run"
|
_migrate_project "$dry_run"
|
||||||
|
|
|
||||||
|
|
@ -346,6 +346,40 @@ m_file_src() { mkdir -p "$HOME/keystores"; printf 'KS\x00\x01\x02\xffDATA\n' >
|
||||||
[[ "$output" == *"already format v2"* ]] || false
|
[[ "$output" == *"already format v2"* ]] || false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "migrate --status flags a project that needs migrating" {
|
||||||
|
make_v1_store
|
||||||
|
m_gradle_src $'beaconClerkPkTest=pk_test_abc\n'
|
||||||
|
create_project_dir needsmig
|
||||||
|
printf 'gradle-properties ~/.gradle/gradle.properties beaconClerkPkTest\n' > .secrets-files
|
||||||
|
"$SECRETS_BIN" push needsmig >/dev/null 2>&1 # v1 blob, no twin yet
|
||||||
|
run "$SECRETS_BIN" migrate --status
|
||||||
|
[ "$status" -ne 0 ] # not finalize-ready
|
||||||
|
[[ "$output" == *"needsmig"* ]] || false
|
||||||
|
[[ "$output" == *"NEEDS MIGRATE"* ]] || false
|
||||||
|
[[ "$output" == *"Not finalize-ready"* ]] || false
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "migrate --status reports finalize-ready once every blob is twinned" {
|
||||||
|
make_v1_store
|
||||||
|
m_gradle_src $'beaconClerkPkTest=pk_test_abc\n'
|
||||||
|
create_project_dir readymig
|
||||||
|
printf 'gradle-properties ~/.gradle/gradle.properties beaconClerkPkTest\n' > .secrets-files
|
||||||
|
"$SECRETS_BIN" push readymig >/dev/null 2>&1
|
||||||
|
"$SECRETS_BIN" migrate >/dev/null 2>&1 # create the twin
|
||||||
|
run "$SECRETS_BIN" migrate --status
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "$output" == *"Finalize-ready"* ]] || false
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "migrate --status on an already-v2 store says nothing to do" {
|
||||||
|
init_with_remote
|
||||||
|
create_project_dir v2status
|
||||||
|
run "$SECRETS_BIN" migrate --status
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "$output" == *"v2"* ]] || false
|
||||||
|
[[ "$output" == *"nothing to migrate"* ]] || false
|
||||||
|
}
|
||||||
|
|
||||||
@test "_store_format reads a garbage marker as v1 (strict parse)" {
|
@test "_store_format reads a garbage marker as v1 (strict parse)" {
|
||||||
make_v1_store
|
make_v1_store
|
||||||
create_project_dir garbagemarker
|
create_project_dir garbagemarker
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue