#!/usr/bin/env bash # sync-versions.sh — Synchronise all in-house Red Bear crate versions to match # the current git branch name (e.g. branch "0.2.4" → version "0.2.4"). # # Policy: # - In-house Red Bear crates (local/recipes/) MUST use the branch version. # - Upstream Redox forks (local/sources/) keep their upstream versioning. # - Established projects with their own versioning (tlc, zbus) are excluded. # - Meson test-case crates are excluded. # # Usage: # ./local/scripts/sync-versions.sh # Apply # ./local/scripts/sync-versions.sh --check # Check only, exit 1 if drift found set -euo pipefail ROOT="$(cd "$(dirname "$0")/../.." && pwd)" cd "$ROOT" CHECK_ONLY=0 [[ "${1:-}" == "--check" ]] && CHECK_ONLY=1 # ---- Determine target version from branch ---- BRANCH="$(git branch --show-current 2>/dev/null || echo "")" if [[ -z "$BRANCH" ]]; then echo "ERROR: cannot determine current git branch" >&2 exit 1 fi # Extract semantic version from branch name (e.g. "0.2.4" from "0.2.4") TARGET_VERSION="$(echo "$BRANCH" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || true)" if [[ -z "$TARGET_VERSION" ]]; then echo "ERROR: branch '$BRANCH' does not contain a semantic version" >&2 exit 1 fi echo "Target version: $TARGET_VERSION (from branch '$BRANCH')" echo "" # ---- Exclusions ---- # Crates that keep their own versioning (not branch-tracked) EXCLUDE_NAMES=( "zbus" # upstream zbus fork "tlc" # established project with independent versioning ) # Paths to exclude (substring match) EXCLUDE_PATHS=( "test cases" # meson test crates "/target/" # build artifacts ) should_exclude() { local name="$1" local path="$2" for ex in "${EXCLUDE_NAMES[@]}"; do [[ "$name" == "$ex" ]] && return 0 done for ex in "${EXCLUDE_PATHS[@]}"; do [[ "$path" == *"$ex"* ]] && return 0 done return 1 } # ---- Find and update Cargo.toml files ---- CHANGED=0 CHECKED=0 DRIFT=0 # Find all Cargo.toml in local/recipes/ (source dirs only, skip target/) while IFS= read -r f; do # Extract crate name (if present) name="$(grep '^name = ' "$f" 2>/dev/null | head -1 | sed 's/name = "//;s/"//' || true)" if should_exclude "$name" "$f"; then continue fi CHECKED=$((CHECKED + 1)) # Case 1: [package] version = "x.y.z" if grep -q '^version = "' "$f" 2>/dev/null; then current="$(grep '^version = "' "$f" | head -1 | sed 's/version = "//;s/"//' || true)" if [[ "$current" != "$TARGET_VERSION" ]]; then if [[ $CHECK_ONLY -eq 1 ]]; then echo " DRIFT: $name $current → $TARGET_VERSION ($f)" DRIFT=$((DRIFT + 1)) else sed -i "s/^version = \"$current\"/version = \"$TARGET_VERSION\"/" "$f" echo " UPDATED: $name $current → $TARGET_VERSION ($f)" CHANGED=$((CHANGED + 1)) fi fi fi # Case 2: [workspace.package] version = "x.y.z" if grep -A1 '^\[workspace\.package\]' "$f" 2>/dev/null | grep -q '^version = "' 2>/dev/null; then current="$(grep -A1 '^\[workspace\.package\]' "$f" | grep '^version = "' | sed 's/version = "//;s/"//' || true)" if [[ "$current" != "$TARGET_VERSION" ]]; then if [[ $CHECK_ONLY -eq 1 ]]; then echo " DRIFT (workspace): $current → $TARGET_VERSION ($f)" DRIFT=$((DRIFT + 1)) else # Only replace the version line that appears under [workspace.package] # Use awk for precision awk -v new="$TARGET_VERSION" ' /^\[workspace\.package\]/ { in_wp=1 } /^\[/ && !/^\[workspace\.package\]/ { in_wp=0 } in_ws==0 && in_wp==1 && /^version = / { print "version = \"" new "\""; next } { print } ' "$f" > "${f}.tmp" && mv "${f}.tmp" "$f" echo " UPDATED (workspace): $current → $TARGET_VERSION ($f)" CHANGED=$((CHANGED + 1)) fi fi fi done < <(find local/recipes/ -name "Cargo.toml" -not -path "*/target/*" -path "*/source/*" | sort) echo "" if [[ $CHECK_ONLY -eq 1 ]]; then echo "Checked $CHECKED crates, found $DRIFT with version drift" [[ $DRIFT -gt 0 ]] && exit 1 echo "All in-house crates are at $TARGET_VERSION" else echo "Updated $CHANGED crates to $TARGET_VERSION (checked $CHECKED total)" fi