Files
RedBear-OS/local/scripts/verify-fork-versions.sh
T
vasilito b8aac3c9bc 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.
2026-07-05 22:29:19 +03:00

198 lines
7.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# 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<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 `<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.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$ROOT"
MAP_FILE="$ROOT/local/fork-upstream-map.toml"
if [ ! -f "$MAP_FILE" ]; then
echo "ERROR: $MAP_FILE not found." >&2
echo " Run local/scripts/refresh-fork-upstream-map.sh to generate it." >&2
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
[ -d "$fork_dir" ] || continue
fork_name=$(basename "$fork_dir")
toml="$fork_dir/Cargo.toml"
[ -f "$toml" ] || continue
version=$(grep -E '^version\s*=' "$toml" | head -1 | sed -E 's/.*"([^"]+)".*/\1/') || true
[ -n "$version" ] || continue
# Only check Cat 2 forks (those with -rb suffix)
if [[ "$version" != *"-rb"* ]]; then
continue
fi
# 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_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
# Look up the fork in the upstream map
map_line=$(grep "^$fork_name\b" "$MAP_FILE" || true)
if [ -z "$map_line" ]; then
echo "ERROR: $fork_name is not in $MAP_FILE. Run refresh-fork-upstream-map.sh." >&2
violations=$((violations + 1))
continue
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 [ "$upstream_tag" = "PENDING_REBASE" ]; then
echo "ERROR: $fork_name is marked as PENDING_REBASE in $MAP_FILE." >&2
echo " A real rebase onto a chosen upstream tag is required." >&2
violations=$((violations + 1))
continue
fi
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
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
echo "WARN: $fork_name: couldn't ls-remote $upstream_url tag $upstream_tag, skipping content check" >&2
continue
fi
# Compare file lists: the local fork should have the same files as
# 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
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
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 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
while IFS= read -r f; do
[ -z "$f" ] && continue
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
expected_differ=($(printf '%s\n' "${expected_differ[@]}" | sort -u))
expected_differ+=("Cargo.toml")
expected_differ+=("Cargo.toml.orig")
expected_differ=($(printf '%s\n' "${expected_differ[@]}" | sort -u))
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
echo "ERROR: $fork_name is missing files that exist in upstream $upstream_tag:" >&2
echo "$only_upstream" | sed 's/^/ /' >&2
violations=$((violations + 1))
fi
if [ -n "$only_local" ]; then
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))
if [ -n "$local_non_patch" ]; then
echo "ERROR: $fork_name has files that don't exist in upstream $upstream_tag:" >&2
echo "$local_non_patch" | sed 's/^/ /' | head -10 >&2
count=$(echo "$local_non_patch" | wc -l)
if [ "$count" -gt 10 ]; then
echo " ... and $((count - 10)) more" >&2
fi
violations=$((violations + 1))
fi
fi
# 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=0
for ed in "${expected_differ[@]}"; do
if [ "$f" = "$ed" ]; then
skip=1
break
fi
done
[ "$skip" = "1" ] && continue
if ! diff -q "$f" "$upstream_dir/$f" >/dev/null 2>&1; then
if [ "$diff_count" -eq 0 ]; then
echo "ERROR: $fork_name has files that diverge from upstream $upstream_tag:" >&2
fi
echo " $f" >&2
diff_count=$((diff_count + 1))
fi
done <<< "$(comm -12 <(echo "$local_files") <(echo "$upstream_files"))"
if [ "$diff_count" -gt 0 ]; then
violations=$((violations + 1))
fi
rm -rf "/tmp/verify-$fork_name"
done
if [ "$violations" -gt 0 ]; then
echo "" >&2
echo "FAIL: $violations fork version violations found." >&2
exit 1
fi
echo "All Cat 2 forks pass the no-fake-version-label check."
exit 0