phase 16.1: sync-versions.sh safe-by-default + --dry-run + fix --regen-only

- REGEN_LOCKFILES defaults to 0 (opt-in, not opt-out)
- New --regen flag explicitly enables lockfile regeneration
- New --dry-run flag: snapshots each Cargo.lock, regens, diffs, reverts
  Reports per-lockfile changes that WOULD occur without modifying files
- Fixed --regen-only deadlock: CHECK_ONLY=1 no longer blocks regen
  (mode_regen_only gate added to CHECK_ONLY skip check)
- Fixed ROOT_DRIFT unbound variable in --regen-only mode
  (: ${ROOT_DRIFT:=0} before summary section)
- Moved regen_lockfile_for() definition before dry-run block
  (was defined after call site — caused 'command not found')
This commit is contained in:
2026-07-12 15:22:28 +03:00
parent a4adf22d29
commit 24b58896c8
+86 -11
View File
@@ -31,10 +31,12 @@
# - Meson test-case crates
#
# Usage:
# ./local/scripts/sync-versions.sh # Apply
# ./local/scripts/sync-versions.sh --check # Check only
# ./local/scripts/sync-versions.sh --no-regen # Apply without Cargo.lock regen
# ./local/scripts/sync-versions.sh --regen-only # Only regenerate Cargo.lock files
# ./local/scripts/sync-versions.sh # Apply version sync (SAFE — no lockfile regen by default)
# ./local/scripts/sync-versions.sh --regen # Apply sync + regen Cargo.lock files
# ./local/scripts/sync-versions.sh --check # Check only, exit 1 if drift
# ./local/scripts/sync-versions.sh --dry-run # Preview: show what --regen WOULD change in lockfiles
# ./local/scripts/sync-versions.sh --no-regen # Same as default (for backward compat)
# ./local/scripts/sync-versions.sh --regen-only # Only regenerate Cargo.lock files
set -euo pipefail
@@ -42,13 +44,16 @@ ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$ROOT"
CHECK_ONLY=0
REGEN_LOCKFILES=1
REGEN_LOCKFILES=0 # Phase 16.0: safe-by-default — lockfile regen is opt-in
DRY_RUN=0
for arg in "$@"; do
case "$arg" in
--check) CHECK_ONLY=1 ;;
--regen) REGEN_LOCKFILES=1 ;;
--no-regen) REGEN_LOCKFILES=0 ;;
--regen-only) REGEN_LOCKFILES=1; mode_regen_only=1 ;;
-h|--help) sed -n '1,40p' "$0"; exit 0 ;;
--dry-run) DRY_RUN=1; REGEN_LOCKFILES=1 ;; # dry-run implies --regen
-h|--help) sed -n '1,45p' "$0"; exit 0 ;;
*) echo "Unknown option: $arg" >&2; exit 1 ;;
esac
done
@@ -247,6 +252,8 @@ echo ""
# ---- Summary ----
: ${ROOT_DRIFT:=0}
if [[ $CHECK_ONLY -eq 1 ]]; then
echo "Cat 0 (cookbook root): drift $ROOT_DRIFT"
echo "Cat 1: checked $CAT1_CHECKED, drift $CAT1_DRIFT"
@@ -273,21 +280,21 @@ fi
# that has a Cargo.lock. The path-dep + [patch.crates-io] config in each
# fork ensures the right crates are pulled in.
#
# Skip when --check or --no-regen; only --regen-only also opts in.
# Skip when --check (but NOT --regen-only: that opts IN to regen)
# Skip when --no-regen is active (REGEN_LOCKFILES=0)
if [[ $CHECK_ONLY -eq 1 ]]; then
if [[ $CHECK_ONLY -eq 1 && "${mode_regen_only:-0}" != "1" ]]; then
echo ""
echo "(lockfile regeneration skipped in --check mode)"
exit 0
fi
if [[ $REGEN_LOCKFILES -eq 0 ]]; then
echo ""
echo "(lockfile regeneration skipped via --no-regen)"
echo "(lockfile regeneration skipped — use --regen to enable)"
exit 0
fi
echo ""
echo "=== Phase 3: Regenerate Cargo.lock files ==="
# ---- Phase 3: Lockfile regeneration utilities ----
REGEN_DONE=0
REGEN_FAILED=0
@@ -314,6 +321,74 @@ regen_lockfile_for() {
cd "$ROOT"
}
# ---- Phase 3: Dry-run preview (--dry-run flag) ----
# Snapshot each fork's Cargo.lock, regen, show diff, then revert.
# This lets operators preview lockfile changes before a real branch bump.
if [[ $DRY_RUN -eq 1 ]]; then
echo ""
echo "=== Phase 3: Dry-run lockfile regen preview ==="
echo "(existing Cargo.lock files will NOT be modified)"
echo ""
# Preserve all lockfiles first
declare -A LOCK_BACKUPS=()
for lock_dir in "$ROOT" local/sources/*/; do
[ -d "$lock_dir" ] || continue
[ -f "${lock_dir}/Cargo.toml" ] || continue
lock="${lock_dir}/Cargo.lock"
if [ -f "$lock" ]; then
LOCK_BACKUPS["$lock"]=$(cat "$lock")
fi
done
# Regenerate in-place (reverts after)
regen_lockfile_for "$ROOT" "root"
for fork_dir in local/sources/*/; do
[ -d "$fork_dir" ] || continue
fork_name=$(basename "$fork_dir")
[ -f "${fork_dir}Cargo.toml" ] || continue
regen_lockfile_for "${fork_dir%/}" "local/sources/${fork_name}"
done
# Show diffs
changed=0
echo ""
echo "--- Lockfile changes that would occur on a real bump ---"
for lock in "${!LOCK_BACKUPS[@]}"; do
backup="${LOCK_BACKUPS[$lock]}"
current=""
[ -f "$lock" ] && current=$(cat "$lock")
if [ "$backup" != "$current" ]; then
changed=$((changed + 1))
label="${lock#$ROOT/}"
echo ""
echo " === $label ==="
diff <(echo "$backup") <(echo "$current") | head -30
if [ $(diff <(echo "$backup") <(echo "$current") | wc -l) -gt 30 ]; then
echo " ... (truncated — $(( $(diff <(echo "$backup") <(echo "$current") | wc -l) - 30 )) more lines)"
fi
fi
done
# Restore originals
for lock in "${!LOCK_BACKUPS[@]}"; do
echo "${LOCK_BACKUPS[$lock]}" > "$lock"
done
echo ""
echo "=== Dry-run summary ==="
echo " Lockfiles with changes: $changed"
if [ $changed -eq 0 ]; then
echo " ALL CLEAR — no lockfile changes would occur. Safe to --regen."
else
echo " Review the diffs above. If acceptable, run: $0 --regen"
fi
exit 0
fi
echo ""
echo "=== Phase 3: Regenerate Cargo.lock files ==="
# Regenerate root cookbook lockfile
regen_lockfile_for "$ROOT" "root"