diff --git a/local/scripts/sync-versions.sh b/local/scripts/sync-versions.sh index 11a8f4e592..c2178098dc 100755 --- a/local/scripts/sync-versions.sh +++ b/local/scripts/sync-versions.sh @@ -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"