Files
RedBear-OS/local/scripts/apply-rb-suffix.sh
T
vasilito f7c3d504dd policy: enforce no-fake-version-label rule for Cat 2 forks
Per local/AGENTS.md \xC2\xA7 'No-fake-version-label rule':

  Every Cat 2 fork version MUST match the source content from the
  corresponding upstream release + documented Red Bear patches.
  A `-rbN` label on stale content is a fake label and a policy
  violation.

Changes:

  * local/AGENTS.md: documented the no-fake-version-label rule,
    including what counts as a fake label, the enforcement contract,
    and what a real Red Bear fork looks like.

  * local/fork-upstream-map.toml: authoritative mapping of each
    Cat 2 fork (syscall, libredox, redoxfs, redox-scheme, relibc,
    kernel, bootloader, installer, userutils) to its upstream Git
    URL and release tag.

  * local/scripts/refresh-fork-upstream-map.sh: auto-update the
    fork-upstream-map by querying each upstream repo for the
    current latest stable release tag.

  * local/scripts/verify-fork-versions.sh: preflight enforcement
    script. For each Cat 2 fork with a `-rbN` version field:
      1. Compare fork's file list and content against the upstream
         release tag from the map. Reject the build if files are
         missing (would be a fake label).
      2. Reject the build if files exist in local that don't exist
         in upstream (must be moved to local/patches/<fork>/ as
         documented Red Bear patches).
      3. Reject the build if shared files diverge in content.

  * local/scripts/apply-rb-suffix.sh: invokes
    verify-fork-versions.sh after applying the `-rbN` label so the
    build fails fast if the labelled content is fake.

  * local/scripts/build-preflight.sh: invokes
    verify-fork-versions.sh at the start of every build. Bypassed
    only with REDBEAR_SKIP_FORK_VERIFY=1 (emergency only).

  * local/patches/bottom/0001-ratui-0.30-braille-compat.patch: the
    ratatui 0.30+ compatibility shim for bottom 0.11.2.

This is the structural enforcement that prevents fake labels from
ever reaching the build again. The current 6 forks with `-rbN`
labels are flagged by the verifier — they must be rebased onto
their actual upstream release before the build can succeed.
2026-07-05 02:37:54 +03:00

299 lines
11 KiB
Bash
Executable File

#!/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).
#
# Policy (local/AGENTS.md § "Category 2 — Local forks of upstream packages"
# and § "No-fake-version-label rule"):
#
# 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.
#
# 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.
#
# 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).
#
# 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 --check # Verify only
# ./local/scripts/apply-rb-suffix.sh --rollback # Revert to stable X.Y.Z
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
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 ;;
--rollback) ROLLBACK=1 ;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
esac
shift
done
if [[ $CHECK_ONLY -eq 1 ]] && [[ $ROLLBACK -eq 1 ]]; then
echo "ERROR: --check and --rollback are mutually exclusive" >&2
exit 1
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
relibc
kernel
base
bootloader
installer
redoxfs
userutils
)
# ---- 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/'
}
# ---- 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
}
# ---- Update a single fork's Cargo.toml version ----
update_fork_version() {
local fork="$1"
local f="local/sources/${fork}/Cargo.toml"
if [[ ! -f "$f" ]]; then
echo " SKIP ${fork}: no Cargo.toml"
return 0
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
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")"
if [[ "$current" == "$target" ]]; then
echo " OK ${fork}: version already ${target}"
return 0
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))
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"
exit 1
fi
echo "PASS: all Cat 2 forks have -rb<RB_N> 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"
fi