phase 1.4: extend sync-versions.sh — root Cargo.toml + Cargo.lock regen
Per Phase 0 audit findings (G1, G2), the build-system version sync had two omissions that allowed drift to accumulate across branch bumps: 1. G2: The root Cargo.toml's [package] version was outside the script's scope. During 0.3.0 -> 0.3.1 branch change, syncing Cat 1 + Cat 2 versions did not touch the cookbook crate itself, so 'repo --version' reported the wrong version. Fixed by adding Phase 0: Cookbook root Cargo.toml block that mirrors the Cat 1 version assignment. 2. G1: After a branch bump, Cargo.lock files were not regenerated. Each Cat 2 fork's lockfile kept its previous +rb suffix even after the fork's Cargo.toml was updated. Fixed by adding Phase 3: a 'cargo generate-lockfile' pass that runs in each fork and the root cookbook after version sync completes. The path-dep + [patch.crates-io] config in each fork ensures the right crates are pulled in; the script verifies each cargo generate-lockfile exit code. New flags: --check Verify only (no writes), exit 1 on drift --no-regen Apply version sync, skip lockfile regen --regen-only Skip version sync, only regen lockfiles (default) Apply sync + regen lockfiles After this commit, branch bump workflow is: ./local/scripts/sync-versions.sh ... and nothing else is needed for Cargo.lock freshness. The script exits non-zero only if any 'cargo generate-lockfile' fails in a fork, surfacing build breakage immediately.
This commit is contained in:
@@ -5,26 +5,36 @@
|
||||
# Handles BOTH version categories (local/AGENTS.md § "Version conventions"):
|
||||
#
|
||||
# Cat 1 — In-house Red Bear crates (local/recipes/*/source/):
|
||||
# version = "<branch>" (e.g. "0.2.5")
|
||||
# version = "<branch>" (e.g. "0.3.1")
|
||||
#
|
||||
# Cat 2 — Upstream Redox forks (local/sources/*/):
|
||||
# version = "<upstream-base>+rb<branch>" (e.g. "0.9.0+rb0.2.5")
|
||||
# version = "<upstream-base>+rb<branch>" (e.g. "0.9.0+rb0.3.1")
|
||||
#
|
||||
# Cookbook root — Cargo.toml at repo root:
|
||||
# version = "<branch>" (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
|
||||
|
||||
Reference in New Issue
Block a user