diff --git a/local/scripts/sync-versions.sh b/local/scripts/sync-versions.sh index 4c4ef1e015..11a8f4e592 100755 --- a/local/scripts/sync-versions.sh +++ b/local/scripts/sync-versions.sh @@ -5,26 +5,36 @@ # Handles BOTH version categories (local/AGENTS.md § "Version conventions"): # # Cat 1 — In-house Red Bear crates (local/recipes/*/source/): -# version = "" (e.g. "0.2.5") +# version = "" (e.g. "0.3.1") # # Cat 2 — Upstream Redox forks (local/sources/*/): -# version = "+rb" (e.g. "0.9.0+rb0.2.5") +# version = "+rb" (e.g. "0.9.0+rb0.3.1") +# +# Cookbook root — Cargo.toml at repo root: +# version = "" (e.g. "0.3.1") # # The `+rb` suffix is Cargo build metadata, not a pre-release identifier. # Using `-rb` is a policy violation. # -# When the branch changes (e.g. 0.2.5 → 0.2.6), running this script bumps +# When the branch changes (e.g. 0.3.1 → 0.3.2), running this script bumps # Cat 1 versions to the new branch version AND updates Cat 2 suffixes to # match the new branch version. The upstream base versions are preserved. # +# Phase 3 (new in Phase 1.4): regenerate Cargo.lock files after the +# version bump completes. 'cargo generate-lockfile' is run in each fork +# and the root cookbook. This ensures Cargo.lock drift NEVER accumulates +# across branch bumps. +# # Exclusions: # - zbus (upstream zbus fork, keeps its own versioning) # - tlc (established project with independent versioning) # - Meson test-case crates # # Usage: -# ./local/scripts/sync-versions.sh # Apply -# ./local/scripts/sync-versions.sh --check # Check only, exit 1 if drift found +# ./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 set -euo pipefail @@ -32,7 +42,17 @@ ROOT="$(cd "$(dirname "$0")/../.." && pwd)" cd "$ROOT" CHECK_ONLY=0 -[[ "${1:-}" == "--check" ]] && CHECK_ONLY=1 +REGEN_LOCKFILES=1 +for arg in "$@"; do + case "$arg" in + --check) CHECK_ONLY=1 ;; + --no-regen) REGEN_LOCKFILES=0 ;; + --regen-only) REGEN_LOCKFILES=1; mode_regen_only=1 ;; + -h|--help) sed -n '1,40p' "$0"; exit 0 ;; + *) echo "Unknown option: $arg" >&2; exit 1 ;; + esac +done +[[ "${mode_regen_only:-0}" == "1" ]] && CHECK_ONLY=1 # skip version sync # ---- Determine target version from branch ---- @@ -88,6 +108,33 @@ strip_rb_suffix() { echo "$1" | sed -E 's/(\+rb[0-9]+(\.[0-9]+\.[0-9]+)?)+$//' } +# ---- Phase 0: Cookbook root Cargo.toml ---- +# Per Phase 0 audit: root Cargo.toml's [package] version was historically +# outside this script's scope and drifted to 0.2.5 on branch 0.3.1. The +# cookbook crate (redbear_cookbook) now sets its version to the current +# git branch — keep it in lock-step. + +if [[ "${mode_regen_only:-0}" != "1" ]]; then +echo "=== Phase 0: Cookbook root Cargo.toml ===" + +ROOT_DRIFT=0 +ROOT_CTL="Cargo.toml" +if [ -f "$ROOT_CTL" ] && grep -qE '^\[package\]' "$ROOT_CTL" 2>/dev/null; then + root_name=$(grep '^name = ' "$ROOT_CTL" | head -1 | sed 's/name = "//;s/"//') + root_current=$(grep -E '^version\s*=' "$ROOT_CTL" | head -1 | sed 's/.*"\(.*\)".*/\1/' || true) + if [ -n "$root_current" ] && [ "$root_current" != "$TARGET_VERSION" ]; then + if [[ $CHECK_ONLY -eq 1 ]]; then + echo " DRIFT: $root_name $root_current -> $TARGET_VERSION ($ROOT_CTL)" + ROOT_DRIFT=1 + else + sed -i "s/^version = \"${root_current}\"/version = \"${TARGET_VERSION}\"/" "$ROOT_CTL" + echo " UPDATED: $root_name $root_current -> $TARGET_VERSION ($ROOT_CTL)" + fi + fi +fi +echo "" +fi + # ---- Phase 1: Cat 1 — In-house crates (local/recipes/*/source/) ---- echo "=== Phase 1: Cat 1 — In-house crates ===" @@ -201,16 +248,86 @@ echo "" # ---- Summary ---- if [[ $CHECK_ONLY -eq 1 ]]; then + echo "Cat 0 (cookbook root): drift $ROOT_DRIFT" echo "Cat 1: checked $CAT1_CHECKED, drift $CAT1_DRIFT" echo "Cat 2: checked $CAT2_CHECKED, drift $CAT2_DRIFT" - TOTAL_DRIFT=$((CAT1_DRIFT + CAT2_DRIFT)) + TOTAL_DRIFT=$((CAT1_DRIFT + CAT2_DRIFT + ROOT_DRIFT)) if [[ $TOTAL_DRIFT -gt 0 ]]; then echo "FAIL: $TOTAL_DRIFT version drift(s) found" exit 1 fi echo "All crates are at correct versions for branch '$BRANCH'" else + echo "Cat 0 (cookbook root): updated if drifted" echo "Cat 1: updated $CAT1_CHANGED (checked $CAT1_CHECKED)" echo "Cat 2: updated $CAT2_CHANGED" echo "All versions synced to branch '$BRANCH'" fi + +# ---- Phase 3: regenerate Cargo.lock files ---- +# Per Phase 0 audit: Cargo.lock files accumulated drift after branch bumps. +# Each fork's Cargo.lock failed to refresh when its Cargo.toml's `+rb` +# suffix changed, producing version-resolution bugs. +# +# Run `cargo generate-lockfile` in the root cookbook and each Cat 2 fork +# 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. + +if [[ $CHECK_ONLY -eq 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)" + exit 0 +fi + +echo "" +echo "=== Phase 3: Regenerate Cargo.lock files ===" + +REGEN_DONE=0 +REGEN_FAILED=0 + +regen_lockfile_for() { + local dir="$1" + local label="$2" + if [ ! -d "$dir" ]; then return; fi + cd "$dir" + if [ ! -f "Cargo.toml" ]; then + cd "$ROOT" + return + fi + if [ -f "Cargo.lock" ]; then + rm -f Cargo.lock + fi + if cargo generate-lockfile >/dev/null 2>&1; then + echo " $label: regenerated Cargo.lock" + REGEN_DONE=$((REGEN_DONE + 1)) + else + echo " $label: FAILED to regenerate Cargo.lock" >&2 + REGEN_FAILED=$((REGEN_FAILED + 1)) + fi + cd "$ROOT" +} + +# Regenerate root cookbook lockfile +regen_lockfile_for "$ROOT" "root" + +# Regenerate each Cat 2 fork's lockfile (skipped if not a Cargo crate) +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 + +echo "" +echo "Lockfile regeneration: $REGEN_DONE ok, $REGEN_FAILED failed" +if [[ $REGEN_FAILED -gt 0 ]]; then + echo " Run ./local/scripts/sync-versions.sh --no-regen and inspect failure logs." + exit 1 +fi