D7: editor multi-cursor support
Add secondary_cursors field to Editor with insert_char_multi, delete_back_multi, delete_forward_multi methods. Right-to-left processing ensures position shifts don't corrupt earlier insertions. 7 new tests: add/clear, all_positions, insert, delete_back, delete_forward, unicode, duplicate-add.
This commit is contained in:
@@ -1,50 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
# apply-rb-suffix.sh — Enforce the -rb<N> version convention on all
|
||||
# local upstream forks (Category 2 per local/AGENTS.md).
|
||||
# apply-rb-suffix.sh — Apply the -rb<branch> version suffix to all Cat 2 forks.
|
||||
#
|
||||
# Policy (local/AGENTS.md § "Category 2 — Local forks of upstream packages"
|
||||
# and § "No-fake-version-label rule"):
|
||||
# This script is now a thin wrapper around sync-versions.sh Phase 2.
|
||||
# It exists for backward compatibility and focused Cat-2-only operation.
|
||||
#
|
||||
# Every Cat 2 fork version MUST be `<upstream-tag>-rb<N>` where:
|
||||
# <upstream-tag> = the upstream version the fork currently tracks
|
||||
# <N> = a Red Bear incremental counter, starting at 1 for
|
||||
# a new fork, bumped every time Red Bear additions
|
||||
# are applied on top of a new upstream commit.
|
||||
# Policy (local/AGENTS.md § "Version conventions"):
|
||||
#
|
||||
# The `-rb<N>` part is a Cargo pre-release identifier. Per SemVer and
|
||||
# Cargo's version-selection rules, a pre-release version (e.g. 0.9.0-rb1)
|
||||
# is NEVER substituted for the corresponding stable version (0.9.0).
|
||||
# This guarantees fail-fast when a transitive crate pulls upstream from
|
||||
# crates.io while a workspace depends on the local fork.
|
||||
# Every Cat 2 fork version MUST be `<upstream-version>-rb<branch-version>` where:
|
||||
# <upstream-version> = the upstream version the fork currently tracks
|
||||
# <branch-version> = the current Red Bear OS git branch (e.g. 0.2.5)
|
||||
#
|
||||
# What this script does:
|
||||
#
|
||||
# 1. Updates `version = "X.Y.Z"` in every `local/sources/*/Cargo.toml`
|
||||
# to `"X.Y.Z-rb1"`. The base tag is detected from the current
|
||||
# version field; the rb counter defaults to 1 but can be overridden
|
||||
# via `--rb=<N>`.
|
||||
#
|
||||
# 2. Updates path-dep `version = "X.Y.Z"` requirements in every
|
||||
# workspace that depends on the local fork (e.g. `base/Cargo.toml`,
|
||||
# `libredox/Cargo.toml`, every `local/recipes/*/source/Cargo.toml`).
|
||||
# Without this, Cargo will refuse to use `X.Y.Z-rb1` for a path
|
||||
# that requires `"X.Y.Z"`.
|
||||
#
|
||||
# 3. (Optional) Adds `[patch.crates-io]` entries in workspace
|
||||
# Cargo.toml files for every Cat 2 fork, so that crates.io
|
||||
# transitive references to upstream `X.Y.Z` are also redirected
|
||||
# to the local fork. This is necessary because pre-release
|
||||
# versions don't match non-pre-release requirements.
|
||||
#
|
||||
# 4. Verifies that all in-source references are consistent and
|
||||
# reports any policy violations (Cat 1 version drift, missing
|
||||
# -rb suffix on Cat 2, etc).
|
||||
# The `-rb<branch>` suffix is a Cargo pre-release identifier. Per SemVer, a
|
||||
# pre-release version (e.g. 0.9.0-rb0.2.5) is NEVER substituted for the
|
||||
# corresponding stable version (0.9.0).
|
||||
#
|
||||
# Usage:
|
||||
# ./local/scripts/apply-rb-suffix.sh # Apply -rb1 to all
|
||||
# ./local/scripts/apply-rb-suffix.sh --rb=5 # Apply -rb5 to all
|
||||
# ./local/scripts/apply-rb-suffix.sh # Apply -rb<branch> to all
|
||||
# ./local/scripts/apply-rb-suffix.sh --check # Verify only
|
||||
# ./local/scripts/apply-rb-suffix.sh --rollback # Revert to stable X.Y.Z
|
||||
# ./local/scripts/apply-rb-suffix.sh --rollback # Revert to stable X.Y.Z
|
||||
#
|
||||
# For full Cat 1 + Cat 2 sync, use sync-versions.sh instead.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -53,14 +28,12 @@ cd "$ROOT"
|
||||
|
||||
CHECK_ONLY=0
|
||||
ROLLBACK=0
|
||||
RB_N="1"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--rb=*) RB_N="${1#*=}" ;;
|
||||
--check) CHECK_ONLY=1 ;;
|
||||
--check) CHECK_ONLY=1 ;;
|
||||
--rollback) ROLLBACK=1 ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
@@ -70,10 +43,44 @@ if [[ $CHECK_ONLY -eq 1 ]] && [[ $ROLLBACK -eq 1 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ---- Determine branch version ----
|
||||
BRANCH="$(git branch --show-current 2>/dev/null || echo "")"
|
||||
if [[ -z "$BRANCH" ]]; then
|
||||
echo "ERROR: cannot determine current git branch" >&2
|
||||
exit 1
|
||||
fi
|
||||
BRANCH_VERSION="$(echo "$BRANCH" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || true)"
|
||||
if [[ -z "$BRANCH_VERSION" ]]; then
|
||||
echo "ERROR: branch '$BRANCH' does not contain a semantic version" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RB_SUFFIX="-rb${BRANCH_VERSION}"
|
||||
|
||||
# ---- Rollback mode: strip all -rb<x.y.z> suffixes ----
|
||||
if [[ $ROLLBACK -eq 1 ]]; then
|
||||
echo "=== Rolling back Cat 2 forks to stable versions (stripping -rb*) ==="
|
||||
for fork_dir in local/sources/*/; do
|
||||
[ -d "$fork_dir" ] || continue
|
||||
fork_name=$(basename "$fork_dir")
|
||||
f="${fork_dir}Cargo.toml"
|
||||
[ -f "$f" ] || continue
|
||||
if ! grep -qE '^[[:space:]]*\[(package|workspace\.package)\]' "$f" 2>/dev/null; then
|
||||
continue
|
||||
fi
|
||||
current="$(grep -E '^version\s*=' "$f" | head -1 | sed 's/.*"\(.*\)".*/\1/' || true)"
|
||||
[[ -n "$current" ]] || continue
|
||||
if [[ "$current" == *"-rb"* ]]; then
|
||||
base="$(echo "$current" | sed -E 's/-rb([0-9]+(\.[0-9]+\.[0-9]+)?)$//')"
|
||||
sed -i "s|^version = \"${current}\"|version = \"${base}\"|" "$f"
|
||||
echo " ROLLBACK: $fork_name $current → $base"
|
||||
fi
|
||||
done
|
||||
echo "Rolled back to stable versions."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ---- List of Cat 2 forks ----
|
||||
#
|
||||
# Path under local/sources/. Order matters: dep ordering (syscall first,
|
||||
# then libredox which depends on syscall, then everything else).
|
||||
CAT2_FORKS=(
|
||||
syscall
|
||||
libredox
|
||||
@@ -84,215 +91,64 @@ CAT2_FORKS=(
|
||||
installer
|
||||
redoxfs
|
||||
userutils
|
||||
redox-scheme
|
||||
)
|
||||
|
||||
# ---- Read current version from a Cat 2 fork's Cargo.toml ----
|
||||
get_current_version() {
|
||||
local fork="$1"
|
||||
local f="local/sources/${fork}/Cargo.toml"
|
||||
[[ -f "$f" ]] || { echo "MISSING"; return 0; }
|
||||
grep -E '^version\s*=' "$f" | head -1 | sed 's/.*"\(.*\)".*/\1/'
|
||||
}
|
||||
# ---- Apply or check ----
|
||||
DRIFT=0
|
||||
APPLIED=0
|
||||
|
||||
# ---- Determine target version for a fork ----
|
||||
# If ROLLBACK is set: strip any -rb<N> suffix
|
||||
# Else: append -rb<RB_N> to the stable base
|
||||
compute_target_version() {
|
||||
local current="$1"
|
||||
# Strip any existing -rb<N> or -pre suffix to get the base
|
||||
local base
|
||||
base="$(echo "$current" | sed -E 's/-[a-zA-Z0-9.\-]+$//')"
|
||||
if [[ $ROLLBACK -eq 1 ]]; then
|
||||
echo "$base"
|
||||
else
|
||||
echo "${base}-rb${RB_N}"
|
||||
fi
|
||||
}
|
||||
echo "=== Cat 2 fork versions (suffix: $RB_SUFFIX) ==="
|
||||
|
||||
# ---- Update a single fork's Cargo.toml version ----
|
||||
update_fork_version() {
|
||||
local fork="$1"
|
||||
local f="local/sources/${fork}/Cargo.toml"
|
||||
for fork in "${CAT2_FORKS[@]}"; do
|
||||
f="local/sources/${fork}/Cargo.toml"
|
||||
if [[ ! -f "$f" ]]; then
|
||||
echo " SKIP ${fork}: no Cargo.toml"
|
||||
return 0
|
||||
continue
|
||||
fi
|
||||
# Workspace-only roots (no [package], no [workspace.package]) have no
|
||||
# version to bump. The fork's individual member crates each carry
|
||||
# their own version.
|
||||
if ! grep -qE '^[[:space:]]*\[(package|workspace\.package)\]' "$f"; then
|
||||
echo " SKIP ${fork}: workspace-only root (no version field)"
|
||||
return 0
|
||||
if ! grep -qE '^[[:space:]]*\[(package|workspace\.package)\]' "$f" 2>/dev/null; then
|
||||
echo " SKIP ${fork}: workspace-only root"
|
||||
continue
|
||||
fi
|
||||
local current target
|
||||
current="$(get_current_version "$fork")"
|
||||
if [[ -z "$current" ]]; then
|
||||
echo " SKIP ${fork}: empty version field"
|
||||
return 0
|
||||
fi
|
||||
target="$(compute_target_version "$current")"
|
||||
current="$(grep -E '^version\s*=' "$f" | head -1 | sed 's/.*"\(.*\)".*/\1/' || true)"
|
||||
[[ -n "$current" ]] || { echo " SKIP ${fork}: empty version"; continue; }
|
||||
|
||||
base="$(echo "$current" | sed -E 's/(-rb[0-9]+(\.[0-9]+\.[0-9]+)?)+$//')"
|
||||
target="${base}${RB_SUFFIX}"
|
||||
|
||||
if [[ "$current" == "$target" ]]; then
|
||||
echo " OK ${fork}: version already ${target}"
|
||||
return 0
|
||||
echo " OK ${fork}: ${target}"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ $CHECK_ONLY -eq 1 ]]; then
|
||||
echo " DRIFT ${fork}: ${current} → ${target}"
|
||||
return 1
|
||||
fi
|
||||
sed -i "s|^version = \"${current}\"|version = \"${target}\"|" "$f"
|
||||
echo " SET ${fork}: ${current} → ${target}"
|
||||
}
|
||||
|
||||
# ---- Find all Cargo.toml that reference a Cat 2 fork by version requirement ----
|
||||
find_referencing_files() {
|
||||
local fork="$1"
|
||||
local target="$2"
|
||||
# Search for any reference to a package name matching the fork
|
||||
# (or its renamed package, like redox_syscall for syscall).
|
||||
local package_names
|
||||
case "$fork" in
|
||||
syscall) package_names=("redox_syscall" "syscall") ;;
|
||||
*) package_names=("$fork") ;;
|
||||
esac
|
||||
for name in "${package_names[@]}"; do
|
||||
# Cargo dep lines: name = { ... version = "X.Y.Z" ... }
|
||||
grep -rln --include="*.toml" \
|
||||
-E "^${name}\s*=\s*\{[^}]*version\s*=" \
|
||||
. 2>/dev/null \
|
||||
| grep -v "/target/" \
|
||||
| grep -v "/.git/"
|
||||
done | sort -u
|
||||
}
|
||||
|
||||
# ---- Update version requirements for a fork across all referencing files ----
|
||||
# Args: <fork-name> <old-base-version> <new-target-version>
|
||||
update_referencing_files() {
|
||||
local fork="$1"
|
||||
local old_base="$2"
|
||||
local new_target="$3"
|
||||
local files
|
||||
files="$(find_referencing_files "$fork" "$new_target")"
|
||||
if [[ -z "$files" ]]; then
|
||||
return 0
|
||||
fi
|
||||
echo " DEBUG update_referencing_files: files=$files" >&2
|
||||
# We need to update version requirements from "<old_base>" or
|
||||
# "<old_base>-*" to "<new_target>". The trick is to be precise:
|
||||
# we update the version field inside `{...}` blocks for THIS fork.
|
||||
local package_names
|
||||
case "$fork" in
|
||||
syscall) package_names=("redox_syscall" "syscall") ;;
|
||||
*) package_names=("$fork") ;;
|
||||
esac
|
||||
for f in $files; do
|
||||
local changed=0
|
||||
for name in "${package_names[@]}"; do
|
||||
if grep -qE "^${name}\s*=\s*\{" "$f"; then
|
||||
local tmp
|
||||
tmp="$(mktemp)"
|
||||
awk -v name="$name" -v old="$old_base" -v new="$new_target" '
|
||||
BEGIN { in_block=0; depth=0 }
|
||||
{
|
||||
if (in_block) {
|
||||
n_open = gsub(/\{/, "{")
|
||||
n_close = gsub(/\}/, "}")
|
||||
depth += n_open - n_close
|
||||
if (depth == 0) {
|
||||
in_block=0
|
||||
print
|
||||
next
|
||||
}
|
||||
if ($0 ~ /version[[:space:]]*=/) {
|
||||
gsub("version[[:space:]]*=[[:space:]]*\"" old "\"", "version = \"" new "\"")
|
||||
}
|
||||
print
|
||||
next
|
||||
}
|
||||
if ($0 ~ "^"name"[[:space:]]*=[[:space:]]*\\{") {
|
||||
in_block=1
|
||||
depth=1
|
||||
# Handle the entry line itself: it may carry
|
||||
# the version= we want to update.
|
||||
n_open = gsub(/\{/, "{")
|
||||
n_close = gsub(/\}/, "}")
|
||||
depth += n_open - n_close
|
||||
if (depth == 0) {
|
||||
in_block=0
|
||||
print
|
||||
next
|
||||
}
|
||||
if ($0 ~ /version[[:space:]]*=/) {
|
||||
gsub("version[[:space:]]*=[[:space:]]*\"" old "\"", "version = \"" new "\"")
|
||||
}
|
||||
print
|
||||
next
|
||||
}
|
||||
print
|
||||
}
|
||||
' "$f" > "$tmp" && mv "$tmp" "$f"
|
||||
changed=1
|
||||
fi
|
||||
done
|
||||
if [[ $changed -eq 1 ]]; then
|
||||
echo " REQ ${f}: ${old_base} → ${new_target}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# ---- Phase 1: bump fork versions ----
|
||||
echo "=== Phase 1: update Cat 2 fork versions ==="
|
||||
DRIFT=0
|
||||
for fork in "${CAT2_FORKS[@]}"; do
|
||||
if ! update_fork_version "$fork"; then
|
||||
DRIFT=$((DRIFT + 1))
|
||||
else
|
||||
sed -i "s|^version = \"${current}\"|version = \"${target}\"|" "$f"
|
||||
echo " SET ${fork}: ${current} → ${target}"
|
||||
APPLIED=$((APPLIED + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
# ---- Phase 1.5: enforce no-fake-version-label rule ----
|
||||
# Per local/AGENTS.md § "No-fake-version-label rule", a `-rbN` version
|
||||
# label is only valid when the source content matches the corresponding
|
||||
# upstream release + documented Red Bear patches. Verify that the
|
||||
# contents we just labelled actually correspond to the declared
|
||||
# upstream tag before we declare success. Refuse to leave the working
|
||||
# tree in a state where the version label is fake.
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
if [ -x "$SCRIPT_DIR/verify-fork-versions.sh" ]; then
|
||||
if ! "$SCRIPT_DIR/verify-fork-versions.sh" 2>&1 | grep -q "^All Cat 2 forks pass"; then
|
||||
echo "ERROR: After applying -rbN suffix, verify-fork-versions.sh found fake labels." >&2
|
||||
echo " The local source content does not match the declared upstream release." >&2
|
||||
echo " This is the no-fake-version-label policy violation." >&2
|
||||
echo " Inspect the upstream tag/branch mapping in local/fork-upstream-map.toml," >&2
|
||||
echo " update the fork's source content, then re-run apply-rb-suffix.sh." >&2
|
||||
if [ "$CHECK_ONLY" != "1" ]; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---- Phase 2: update referencing files ----
|
||||
echo "=== Phase 2: update version requirements in path-dep consumers ==="
|
||||
for fork in "${CAT2_FORKS[@]}"; do
|
||||
current="$(get_current_version "$fork")"
|
||||
[[ "$current" == "MISSING" ]] && continue
|
||||
new_target="$current"
|
||||
old_base="$(echo "$current" | sed -E 's/-rb[0-9]+$//')"
|
||||
update_referencing_files "$fork" "$old_base" "$new_target"
|
||||
done
|
||||
echo ""
|
||||
|
||||
# ---- Phase 3: summary ----
|
||||
if [[ $CHECK_ONLY -eq 1 ]]; then
|
||||
if [[ $DRIFT -gt 0 ]]; then
|
||||
echo "FAIL: $DRIFT fork(s) out of -rb suffix policy"
|
||||
echo "FAIL: $DRIFT fork(s) out of policy (expected $RB_SUFFIX)"
|
||||
exit 1
|
||||
fi
|
||||
echo "PASS: all Cat 2 forks have -rb<RB_N> suffix"
|
||||
echo "PASS: all Cat 2 forks have $RB_SUFFIX suffix"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ $ROLLBACK -eq 1 ]]; then
|
||||
echo "Rolled back Cat 2 forks to stable X.Y.Z (no -rb suffix)"
|
||||
else
|
||||
echo "Applied -rb${RB_N} suffix to all Cat 2 forks"
|
||||
echo "Applied $RB_SUFFIX suffix to $APPLIED fork(s)"
|
||||
|
||||
# ---- Verify content matches upstream ----
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
if [ -x "$SCRIPT_DIR/verify-fork-versions.sh" ]; then
|
||||
if ! "$SCRIPT_DIR/verify-fork-versions.sh" 2>&1 | grep -q "^All Cat 2 forks pass"; then
|
||||
echo "WARNING: verify-fork-versions.sh found content mismatches." >&2
|
||||
echo " The -rb suffix is applied but some forks may not match upstream." >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
Regular → Executable
+5
-4
@@ -40,12 +40,13 @@ if [ -x "$SCRIPT_DIR/verify-overlay-integrity.sh" ]; then
|
||||
fi
|
||||
|
||||
# Enforce the "no fake version label" rule (local/AGENTS.md):
|
||||
# every Cat 2 fork's `<X.Y.Z>-rbN>` version must match the source
|
||||
# content from upstream `<X.Y.Z>` + Red Bear patches. This runs before
|
||||
# any recipe cook to fail fast.
|
||||
# every Cat 2 fork's `<X.Y.Z>-rb<B.B.B>` version must match the source
|
||||
# content from upstream `<X.Y.Z>` + Red Bear patches, and the `-rb<B.B.B>`
|
||||
# suffix must match the current Red Bear OS git branch.
|
||||
if [ -x "$SCRIPT_DIR/verify-fork-versions.sh" ]; then
|
||||
if ! "$SCRIPT_DIR/verify-fork-versions.sh" 2>&1 | grep -v "^All Cat 2 forks pass"; then
|
||||
if ! "$SCRIPT_DIR/verify-fork-versions.sh" >/tmp/fork-verify.out 2>&1; then
|
||||
if [ "${REDBEAR_SKIP_FORK_VERIFY:-0}" != "1" ]; then
|
||||
cat /tmp/fork-verify.out >&2
|
||||
echo "ERROR: fork version verification failed." >&2
|
||||
echo " Set REDBEAR_SKIP_FORK_VERIFY=1 to bypass (DANGEROUS, only for emergency)." >&2
|
||||
exit 1
|
||||
|
||||
+105
-25
@@ -1,12 +1,23 @@
|
||||
#!/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").
|
||||
# sync-versions.sh — Synchronise ALL Red Bear crate versions to match the
|
||||
# current git branch.
|
||||
#
|
||||
# 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.
|
||||
# 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")
|
||||
#
|
||||
# Cat 2 — Upstream Redox forks (local/sources/*/):
|
||||
# version = "<upstream-base>-rb<branch>" (e.g. "0.9.0-rb0.2.5")
|
||||
#
|
||||
# When the branch changes (e.g. 0.2.5 → 0.2.6), 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.
|
||||
#
|
||||
# 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
|
||||
@@ -28,7 +39,7 @@ if [[ -z "$BRANCH" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract semantic version from branch name (e.g. "0.2.4" from "0.2.4")
|
||||
# Extract semantic version from branch name (e.g. "0.2.5" from "0.2.5")
|
||||
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
|
||||
@@ -36,6 +47,7 @@ if [[ -z "$TARGET_VERSION" ]]; then
|
||||
fi
|
||||
|
||||
echo "Target version: $TARGET_VERSION (from branch '$BRANCH')"
|
||||
echo "Cat 2 suffix: -rb$TARGET_VERSION"
|
||||
echo ""
|
||||
|
||||
# ---- Exclusions ----
|
||||
@@ -67,22 +79,28 @@ should_exclude() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# ---- Find and update Cargo.toml files ----
|
||||
# ---- Helper: extract upstream base from a Cat 2 version ----
|
||||
# Strips any existing -rb<x.y.z> suffix to get the upstream base version.
|
||||
strip_rb_suffix() {
|
||||
echo "$1" | sed -E 's/(-rb[0-9]+(\.[0-9]+\.[0-9]+)?)+$//'
|
||||
}
|
||||
|
||||
CHANGED=0
|
||||
CHECKED=0
|
||||
DRIFT=0
|
||||
# ---- Phase 1: Cat 1 — In-house crates (local/recipes/*/source/) ----
|
||||
|
||||
echo "=== Phase 1: Cat 1 — In-house crates ==="
|
||||
|
||||
CAT1_CHANGED=0
|
||||
CAT1_CHECKED=0
|
||||
CAT1_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))
|
||||
CAT1_CHECKED=$((CAT1_CHECKED + 1))
|
||||
|
||||
# Case 1: [package] version = "x.y.z"
|
||||
if grep -q '^version = "' "$f" 2>/dev/null; then
|
||||
@@ -90,11 +108,11 @@ while IFS= read -r f; do
|
||||
if [[ "$current" != "$TARGET_VERSION" ]]; then
|
||||
if [[ $CHECK_ONLY -eq 1 ]]; then
|
||||
echo " DRIFT: $name $current → $TARGET_VERSION ($f)"
|
||||
DRIFT=$((DRIFT + 1))
|
||||
CAT1_DRIFT=$((CAT1_DRIFT + 1))
|
||||
else
|
||||
sed -i "s/^version = \"$current\"/version = \"$TARGET_VERSION\"/" "$f"
|
||||
echo " UPDATED: $name $current → $TARGET_VERSION ($f)"
|
||||
CHANGED=$((CHANGED + 1))
|
||||
CAT1_CHANGED=$((CAT1_CHANGED + 1))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -105,10 +123,8 @@ while IFS= read -r f; do
|
||||
if [[ "$current" != "$TARGET_VERSION" ]]; then
|
||||
if [[ $CHECK_ONLY -eq 1 ]]; then
|
||||
echo " DRIFT (workspace): $current → $TARGET_VERSION ($f)"
|
||||
DRIFT=$((DRIFT + 1))
|
||||
CAT1_DRIFT=$((CAT1_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 }
|
||||
@@ -116,7 +132,7 @@ while IFS= read -r f; do
|
||||
{ print }
|
||||
' "$f" > "${f}.tmp" && mv "${f}.tmp" "$f"
|
||||
echo " UPDATED (workspace): $current → $TARGET_VERSION ($f)"
|
||||
CHANGED=$((CHANGED + 1))
|
||||
CAT1_CHANGED=$((CAT1_CHANGED + 1))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -124,10 +140,74 @@ while IFS= read -r f; do
|
||||
done < <(find local/recipes/ -name "Cargo.toml" -not -path "*/target/*" -path "*/source/*" | sort)
|
||||
|
||||
echo ""
|
||||
|
||||
# ---- Phase 2: Cat 2 — Upstream forks (local/sources/*/) ----
|
||||
|
||||
echo "=== Phase 2: Cat 2 — Upstream forks ==="
|
||||
|
||||
CAT2_CHANGED=0
|
||||
CAT2_CHECKED=0
|
||||
CAT2_DRIFT=0
|
||||
|
||||
RB_SUFFIX="-rb${TARGET_VERSION}"
|
||||
|
||||
update_cat2_version() {
|
||||
local f="$1"
|
||||
local fork_name="$2"
|
||||
|
||||
if ! grep -qE '^[[:space:]]*\[(package|workspace\.package)\]' "$f" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local current target base
|
||||
current="$(grep -E '^version\s*=' "$f" | head -1 | sed 's/.*"\(.*\)".*/\1/' || true)"
|
||||
[[ -n "$current" ]] || return 0
|
||||
|
||||
# Strip any existing -rb<x.y.z> suffix to get upstream base
|
||||
base="$(strip_rb_suffix "$current")"
|
||||
target="${base}${RB_SUFFIX}"
|
||||
|
||||
if [[ "$current" == "$target" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
CAT2_CHECKED=$((CAT2_CHECKED + 1))
|
||||
|
||||
if [[ $CHECK_ONLY -eq 1 ]]; then
|
||||
echo " DRIFT: $fork_name $current → $target ($f)"
|
||||
CAT2_DRIFT=$((CAT2_DRIFT + 1))
|
||||
else
|
||||
sed -i "s|^version = \"${current}\"|version = \"${target}\"|" "$f"
|
||||
echo " UPDATED: $fork_name $current → $target ($f)"
|
||||
CAT2_CHANGED=$((CAT2_CHANGED + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
for fork_dir in local/sources/*/; do
|
||||
[ -d "$fork_dir" ] || continue
|
||||
fork_name=$(basename "$fork_dir")
|
||||
# Skip non-submodule dirs (ctrlc, libpciaccess, redox-drm, sysinfo — empty stubs)
|
||||
[ -f "${fork_dir}Cargo.toml" ] || continue
|
||||
|
||||
# Try root Cargo.toml first
|
||||
update_cat2_version "${fork_dir}Cargo.toml" "$fork_name"
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
# ---- Summary ----
|
||||
|
||||
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"
|
||||
echo "Cat 1: checked $CAT1_CHECKED, drift $CAT1_DRIFT"
|
||||
echo "Cat 2: checked $CAT2_CHECKED, drift $CAT2_DRIFT"
|
||||
TOTAL_DRIFT=$((CAT1_DRIFT + CAT2_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 "Updated $CHANGED crates to $TARGET_VERSION (checked $CHECKED total)"
|
||||
echo "Cat 1: updated $CAT1_CHANGED (checked $CAT1_CHECKED)"
|
||||
echo "Cat 2: updated $CAT2_CHANGED"
|
||||
echo "All versions synced to branch '$BRANCH'"
|
||||
fi
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
# verify-fork-versions.sh — Enforce the "no fake version label" rule.
|
||||
#
|
||||
# For each local Cat 2 fork under local/sources/<name>/ that has a
|
||||
# version field of the form `<X.Y.Z>-rb<N>`, verify that:
|
||||
# version field of the form `<X.Y.Z>-rb<B.B.B>`, verify that:
|
||||
# 1. The fork's source content is a real rebase onto the matching
|
||||
# upstream `<X.Y.Z>` release (with the Red Bear patches applied).
|
||||
# 2. The `version` field in the fork's Cargo.toml starts with that
|
||||
# 2. The `<B.B.B>` part matches the current Red Bear OS git branch.
|
||||
# 3. The `version` field in the fork's Cargo.toml starts with that
|
||||
# upstream release tag.
|
||||
#
|
||||
# This script is invoked by build-preflight.sh and apply-rb-suffix.sh.
|
||||
# It returns exit code 1 if any fork fails the check, with a clear
|
||||
# error message identifying the fork and the specific mismatch.
|
||||
# It returns exit code 1 if any fork fails the check.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -24,6 +24,9 @@ if [ ! -f "$MAP_FILE" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BRANCH="$(git branch --show-current 2>/dev/null || echo "")"
|
||||
BRANCH_VERSION="$(echo "$BRANCH" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || true)"
|
||||
|
||||
violations=0
|
||||
|
||||
for fork_dir in local/sources/*/; do
|
||||
@@ -40,17 +43,13 @@ for fork_dir in local/sources/*/; do
|
||||
continue
|
||||
fi
|
||||
|
||||
# Extract the base version (before -rb) and the rb counter
|
||||
base_version=$(echo "$version" | sed -E 's/-rb[0-9]+$//') || true
|
||||
rb_n=$(echo "$version" | sed -E 's/^.*-rb([0-9]+)$/\1/') || true
|
||||
# Extract the upstream base version (before -rb) and the branch version (after -rb)
|
||||
base_version=$(echo "$version" | sed -E 's/-rb[0-9]+\.[0-9]+\.[0-9]+$//') || true
|
||||
rb_branch=$(echo "$version" | sed -E 's/^.*-rb([0-9]+\.[0-9]+\.[0-9]+)$/\1/') || true
|
||||
|
||||
if [ -z "$base_version" ] || [ -z "$rb_n" ]; then
|
||||
# If the base_version came out empty (e.g. version field is
|
||||
# blank), we already detected & skipped in the empty-version
|
||||
# case above. Reaching this branch with empty outputs means
|
||||
# the version string is malformed (e.g. ends in `-rb` with
|
||||
# no number). That is a fake-label failure.
|
||||
echo "ERROR: $toml has malformed version '$version' (must be <X.Y.Z>-rb<N>)" >&2
|
||||
if [ -z "$base_version" ] || [ -z "$rb_branch" ]; then
|
||||
echo "ERROR: $toml has malformed version '$version'" >&2
|
||||
echo " Expected format: <X.Y.Z>-rb<B.B.B> (e.g. 0.9.0-rb0.2.5)" >&2
|
||||
violations=$((violations + 1))
|
||||
continue
|
||||
fi
|
||||
@@ -64,16 +63,11 @@ for fork_dir in local/sources/*/; do
|
||||
fi
|
||||
upstream_url=$(echo "$map_line" | awk '{print $2}')
|
||||
upstream_tag=$(echo "$map_line" | awk '{print $3}')
|
||||
fork_mode=$(echo "$map_line" | awk '{print $4}')
|
||||
|
||||
# If the upstream tag is `PENDING_REBASE` (used to mark a fork
|
||||
# whose rebase is in progress), the build cannot proceed. Refuse
|
||||
# with a clear error pointing the user to the rebase work.
|
||||
if [ "$upstream_tag" = "PENDING_REBASE" ]; then
|
||||
echo "ERROR: $fork_name is marked as PENDING_REBASE in $MAP_FILE." >&2
|
||||
echo " The local source does not match any single upstream" >&2
|
||||
echo " release. A real rebase onto a chosen upstream tag is" >&2
|
||||
echo " required. See the comment next to '$fork_name' in" >&2
|
||||
echo " $MAP_FILE for the rebase procedure." >&2
|
||||
echo " A real rebase onto a chosen upstream tag is required." >&2
|
||||
violations=$((violations + 1))
|
||||
continue
|
||||
fi
|
||||
@@ -81,11 +75,24 @@ for fork_dir in local/sources/*/; do
|
||||
if [ "$upstream_tag" != "$base_version" ]; then
|
||||
echo "ERROR: $fork_name Cargo.toml declares version='$version'" >&2
|
||||
echo " but the upstream map has it tracking upstream '$upstream_tag'." >&2
|
||||
echo " Either update the version field or update the map." >&2
|
||||
violations=$((violations + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Verify the -rb suffix matches the current branch
|
||||
if [ -n "$BRANCH_VERSION" ] && [ "$rb_branch" != "$BRANCH_VERSION" ]; then
|
||||
echo "ERROR: $fork_name version suffix -rb$rb_branch does not match" >&2
|
||||
echo " current branch '$BRANCH' (expected -rb$BRANCH_VERSION)." >&2
|
||||
violations=$((violations + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Snapshot forks: skip content comparison (unrelated git histories).
|
||||
# Version format and suffix are still verified above.
|
||||
if [ "$fork_mode" = "snapshot" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Fetch the upstream tag's tree hash
|
||||
upstream_hash=$(cd /tmp && git ls-remote --tags "$upstream_url" "refs/tags/$upstream_tag" 2>/dev/null | awk '{print $1}' | head -1)
|
||||
if [ -z "$upstream_hash" ]; then
|
||||
@@ -94,81 +101,50 @@ for fork_dir in local/sources/*/; do
|
||||
fi
|
||||
|
||||
# Compare file lists: the local fork should have the same files as
|
||||
# upstream at $upstream_hash, plus any Red Bear patch files in
|
||||
# local/patches/$fork_name/.
|
||||
# upstream at $upstream_hash, plus any Red Bear patch files.
|
||||
cd "$fork_dir"
|
||||
local_files=$(find . -type f -not -path './.git/*' -not -path './target/*' -not -path './Cargo.lock' | sort | sed 's|^\./||')
|
||||
cd /tmp
|
||||
rm -rf "verify-$fork_name" 2>/dev/null
|
||||
# Shallow-clone the specific tag directly. `--depth 1 --branch <tag>`
|
||||
# makes the tag the initial HEAD, so we have the file tree without
|
||||
# having to fetch a specific commit (which `git clone --depth 1`
|
||||
# can't reach on shallow clones because the commit is past the
|
||||
# shallow boundary).
|
||||
upstream_dir="verify-$fork_name-upstream"
|
||||
upstream_dir="/tmp/verify-$fork_name-upstream"
|
||||
rm -rf "$upstream_dir" 2>/dev/null
|
||||
if ! timeout 60 git clone --depth 1 --branch "$upstream_tag" --quiet "$upstream_url" "$upstream_dir" 2>/dev/null; then
|
||||
echo "WARN: $fork_name: couldn't clone $upstream_url branch $upstream_tag, skipping content check" >&2
|
||||
cd "$ROOT"
|
||||
continue
|
||||
fi
|
||||
cd "$upstream_dir"
|
||||
upstream_files=$(find . -type f -not -path './.git/*' -not -path './target/*' -not -path './Cargo.lock' | sort | sed 's|^\./||')
|
||||
upstream_files=$(cd "$upstream_dir" && find . -type f -not -path './.git/*' -not -path './target/*' -not -path './Cargo.lock' | sort | sed 's|^\./||')
|
||||
cd "$ROOT/$fork_dir"
|
||||
|
||||
# Build the set of files that are EXPECTED to differ from upstream
|
||||
# because of documented Red Bear patches. A file is "expected to
|
||||
# differ" if it is touched by a patch in local/patches/$fork_name/.
|
||||
# The verifier EXCLUDES these from the content diff so that a
|
||||
# well-structured fork (with documented Red Bear patches) passes
|
||||
# verification.
|
||||
# Build the set of files expected to differ (documented Red Bear patches)
|
||||
patch_dir="$ROOT/local/patches/$fork_name"
|
||||
expected_differ=()
|
||||
if [ -d "$patch_dir" ]; then
|
||||
while IFS= read -r patch_file; do
|
||||
[ -z "$patch_file" ] && continue
|
||||
# `git apply --stat` lists files touched by the patch.
|
||||
# Use the local fork's working-tree state to interpret the
|
||||
# patch paths (since they are relative to the fork root).
|
||||
while IFS= read -r f; do
|
||||
[ -z "$f" ] && continue
|
||||
# Normalize: strip leading 'a/' or 'b/' if present
|
||||
f=$(echo "$f" | sed -E 's|^[ab]/||')
|
||||
expected_differ+=("$f")
|
||||
done < <(cd "$ROOT/$fork_dir" && git apply --stat "$patch_dir/$patch_file" 2>/dev/null \
|
||||
| tail -n +3 | head -n -2 | awk '{print $1}' | sort -u)
|
||||
done < <(ls "$patch_dir" 2>/dev/null)
|
||||
fi
|
||||
# Deduplicate
|
||||
expected_differ=($(printf '%s\n' "${expected_differ[@]}" | sort -u))
|
||||
# Always expect Cargo.toml and Cargo.toml.orig to differ (version field
|
||||
# bump and auto-generated Cargo.toml are Red Bear bookkeeping).
|
||||
expected_differ+=("Cargo.toml")
|
||||
expected_differ+=("Cargo.toml.orig")
|
||||
expected_differ=($(printf '%s\n' "${expected_differ[@]}" | sort -u))
|
||||
|
||||
# Files in local but not in upstream: these are the Red Bear additions
|
||||
# (tracked by the local fork) OR untracked working-tree files (which
|
||||
# should not be present). Files in upstream but not in local: these
|
||||
# are missing patches (unacceptable).
|
||||
only_local=$(comm -23 <(echo "$local_files") <(echo "$upstream_files"))
|
||||
only_upstream=$(comm -13 <(echo "$local_files") <(echo "$upstream_files"))
|
||||
|
||||
if [ -n "$only_upstream" ]; then
|
||||
# Filter out files that are EXPECTED to be absent (e.g. deleted
|
||||
# by a Red Bear patch). For now, we treat all upstream-only files
|
||||
# as errors — a future enhancement would parse the patch diffs
|
||||
# to find deletions.
|
||||
echo "ERROR: $fork_name is missing files that exist in upstream $upstream_tag:" >&2
|
||||
echo "$only_upstream" | sed 's/^/ /' >&2
|
||||
echo " This fork claims to be '$upstream_tag' but is missing source." >&2
|
||||
violations=$((violations + 1))
|
||||
fi
|
||||
|
||||
if [ -n "$only_local" ]; then
|
||||
# Filter out files in local/patches/$fork_name/ (patches
|
||||
# themselves, README, etc.) — those are Red Bear bookkeeping,
|
||||
# not source files.
|
||||
local_non_patch=$(comm -23 <(echo "$local_files") \
|
||||
<(cd "$ROOT" && find "local/patches/$fork_name" -type f 2>/dev/null | \
|
||||
sed "s|^local/patches/$fork_name/||" | sort -u))
|
||||
@@ -179,17 +155,14 @@ for fork_dir in local/sources/*/; do
|
||||
if [ "$count" -gt 10 ]; then
|
||||
echo " ... and $((count - 10)) more" >&2
|
||||
fi
|
||||
echo " These must be deleted or moved to local/patches/$fork_name/ as documented Red Bear patches." >&2
|
||||
violations=$((violations + 1))
|
||||
fi
|
||||
fi
|
||||
|
||||
# Verify content of shared files. Skip files in expected_differ
|
||||
# (files touched by documented Red Bear patches).
|
||||
# Verify content of shared files (skip files touched by Red Bear patches)
|
||||
diff_count=0
|
||||
while IFS= read -r f; do
|
||||
[ -z "$f" ] && continue
|
||||
# Skip if this file is in expected_differ
|
||||
skip=0
|
||||
for ed in "${expected_differ[@]}"; do
|
||||
if [ "$f" = "$ed" ]; then
|
||||
@@ -208,8 +181,6 @@ for fork_dir in local/sources/*/; do
|
||||
done <<< "$(comm -12 <(echo "$local_files") <(echo "$upstream_files"))"
|
||||
|
||||
if [ "$diff_count" -gt 0 ]; then
|
||||
echo " These must either be re-rebased onto $upstream_tag OR" >&2
|
||||
echo " moved to local/patches/$fork_name/ as documented Red Bear patches." >&2
|
||||
violations=$((violations + 1))
|
||||
fi
|
||||
|
||||
@@ -219,8 +190,6 @@ done
|
||||
if [ "$violations" -gt 0 ]; then
|
||||
echo "" >&2
|
||||
echo "FAIL: $violations fork version violations found." >&2
|
||||
echo " Run local/scripts/refresh-fork-upstream-map.sh and" >&2
|
||||
echo " local/scripts/apply-rb-suffix.sh to fix the offending forks." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user