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.
This commit is contained in:
@@ -492,6 +492,79 @@ The only acceptable reason to keep a `version = "X.Y.Z"` (exact-pin) is when:
|
||||
- The build is being done against a release archive that pins specific versions for
|
||||
reproducibility (see `local/scripts/provision-release.sh`).
|
||||
|
||||
### No-fake-version-label rule (STRICT)
|
||||
|
||||
The `version = "X.Y.Z-rbN"` field in a Cat 2 fork's `Cargo.toml` MUST accurately
|
||||
describe the underlying source code. Specifically:
|
||||
|
||||
- The `<X.Y.Z>` part of the version **MUST be the upstream release tag** that the
|
||||
source code is based on. Setting `version = "0.9.0-rb1"` on a fork whose
|
||||
source code is actually upstream `0.8.x` (or any other version) is a **fake
|
||||
label** and a **policy violation**. The `-rbN` suffix is meaningful only when
|
||||
applied to the correct upstream base.
|
||||
- The fork's source code **MUST be a real rebase onto upstream `<X.Y.Z>`** plus
|
||||
Red Bear patches, NOT a mislabeled old version of the upstream code with the
|
||||
new version number stamped on it.
|
||||
- Each Red Bear patch's purpose is to add **new** functionality on top of the
|
||||
matching upstream release. A "patch" that simply renames the version field is
|
||||
a fake and is rejected.
|
||||
|
||||
**Enforcement:**
|
||||
|
||||
- `local/scripts/build-preflight.sh` calls `local/scripts/verify-fork-versions.sh`
|
||||
before every build. That script:
|
||||
- For each `local/sources/<name>/` with `-rb` in its `Cargo.toml` version field,
|
||||
computes the `<base>` (everything before `-rb`) and `<N>` (after `-rb`).
|
||||
- Fetches the upstream `<base>` release tag from the corresponding upstream
|
||||
repository (configured by a new `local/fork-upstream-map.toml` file).
|
||||
- Compares the local fork's source-tree file list and content hashes against
|
||||
the upstream `<base>` tag.
|
||||
- **Rejects the build** (exit code 1) if:
|
||||
- The local fork's content does NOT match upstream `<base>` exactly
|
||||
**except for the documented Red Bear patch files** (those in
|
||||
`local/patches/<name>/`).
|
||||
- The `version` field in the local fork's `Cargo.toml` does not start with
|
||||
`<base>-rb<N>` where `<base>` is the upstream tag.
|
||||
- `local/scripts/apply-rb-suffix.sh --check` runs the same verification, and
|
||||
also refuses to apply a `-rbN` label unless the local content is a real
|
||||
rebase onto the matching upstream release.
|
||||
- `local/fork-upstream-map.toml` is the authoritative configuration of which
|
||||
upstream repo / release tag each `local/sources/<name>/` corresponds to. It
|
||||
is updated only via `local/scripts/refresh-fork-upstream-map.sh` which fetches
|
||||
the canonical upstream URL from each fork's `local/AGENTS.md` entry and
|
||||
writes the mapping in a deterministic, reviewable format.
|
||||
|
||||
**What a real Red Bear fork looks like:**
|
||||
|
||||
```
|
||||
local/sources/redoxfs/
|
||||
├── Cargo.toml # version = "0.9.0-rb1" (matches upstream 0.9.0)
|
||||
├── Cargo.toml.orig # mirrors upstream, regenerated for cargo consistency
|
||||
├── src/... # the upstream 0.9.0 source tree, byte-for-byte
|
||||
└── local/patches/redoxfs/ # Red Bear patches, each one small and reviewable
|
||||
├── P0-foo.patch
|
||||
├── P1-bar.patch
|
||||
└── README.md # what each patch does and why
|
||||
```
|
||||
|
||||
The fork's `git log` shows: upstream tag as the first parent commit, then a
|
||||
series of small, focused Red Bear commits, each carrying exactly one patch.
|
||||
The build system NEVER commits a version-field-only "bump" commit as the
|
||||
only difference from upstream — that would be a fake label.
|
||||
|
||||
**What a fake label looks like (REJECTED):**
|
||||
|
||||
- A fork whose `Cargo.toml` says `0.9.0-rb1` but whose source content is
|
||||
upstream `0.8.6` (a different version).
|
||||
- A fork whose `Cargo.toml` says `0.9.0-rb1` but whose dep constraints
|
||||
(`redox_syscall = "0.7.0"`, `libredox = "0.1.12"`, etc.) reference versions
|
||||
that don't match upstream 0.9.0's ecosystem.
|
||||
- A fork whose only commit is "fork: bump to -rb1 version suffix" with no
|
||||
actual rebasing onto the matching upstream tag.
|
||||
|
||||
All of these are caught by the enforcement script and the build aborts
|
||||
with a clear error message pointing to the offending fork.
|
||||
|
||||
### Upstream-first rule for fast-moving components
|
||||
|
||||
Some components, especially relibc, are actively evolving upstream. For those areas, Red Bear must
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# fork-upstream-map.toml — Authoritative mapping of local Cat 2 fork
|
||||
# directories to their upstream Redox repositories and the upstream
|
||||
# release tag each fork is based on. Updated by
|
||||
# local/scripts/refresh-fork-upstream-map.sh. Consumed by
|
||||
# local/scripts/verify-fork-versions.sh to enforce the "no fake version
|
||||
# label" rule (local/AGENTS.md § "No-fake-version-label rule").
|
||||
#
|
||||
# Format: one line per fork:
|
||||
# <fork-name> <upstream-git-url> <upstream-release-tag>
|
||||
#
|
||||
# A fork with `<X.Y.Z>-rbN>` in its Cargo.toml MUST have its
|
||||
# `<X.Y.Z>` part match the upstream tag listed here, and the source
|
||||
# content MUST be a real rebase onto that upstream tag plus documented
|
||||
# Red Bear patches (in local/patches/<name>/).
|
||||
|
||||
syscall https://gitlab.redox-os.org/redox-os/syscall.git 0.9.0
|
||||
libredox https://gitlab.redox-os.org/redox-os/libredox.git 0.1.18
|
||||
redoxfs https://gitlab.redox-os.org/redox-os/redoxfs.git 0.9.0
|
||||
redox-scheme https://gitlab.redox-os.org/redox-os/redox-scheme.git 0.11.2
|
||||
relibc https://gitlab.redox-os.org/redox-os/relibc.git 0.2.5
|
||||
kernel https://gitlab.redox-os.org/redox-os/kernel.git 0.5.12
|
||||
bootloader https://gitlab.redox-os.org/redox-os/bootloader.git 1.0.0
|
||||
installer https://gitlab.redox-os.org/redox-os/installer.git 0.2.42
|
||||
userutils https://gitlab.redox-os.org/redox-os/userutils.git 0.1.0
|
||||
@@ -0,0 +1,104 @@
|
||||
diff --git a/Cargo.toml b/Cargo.toml
|
||||
index 9b911691..0a1b73df 100644
|
||||
--- a/Cargo.toml
|
||||
+++ b/Cargo.toml
|
||||
@@ -91,7 +91,7 @@ starship-battery = { version = "0.10.2", optional = true }
|
||||
sysinfo = { git = "https://github.com/jackpot51/sysinfo.git" }
|
||||
timeless = "0.0.14-alpha"
|
||||
toml_edit = { version = "0.23.6", features = ["serde"] }
|
||||
-tui = { version = "0.30.0-alpha.5", package = "ratatui", features = ["unstable-rendered-line-info"] }
|
||||
+tui = { version = "0.30", package = "ratatui", features = ["unstable-rendered-line-info"] }
|
||||
unicode-ellipsis = "0.3.0"
|
||||
unicode-segmentation = "1.12.0"
|
||||
unicode-width = "0.2.0"
|
||||
diff --git a/src/canvas/components/time_graph/base/time_chart/canvas.rs b/src/canvas/components/time_graph/base/time_chart/canvas.rs
|
||||
index 4378bba6..ddf06358 100644
|
||||
--- a/src/canvas/components/time_graph/base/time_chart/canvas.rs
|
||||
+++ b/src/canvas/components/time_graph/base/time_chart/canvas.rs
|
||||
@@ -188,12 +188,16 @@ impl<'a> Context<'a> {
|
||||
pub fn new(
|
||||
width: u16, height: u16, x_bounds: [f64; 2], y_bounds: [f64; 2], marker: symbols::Marker,
|
||||
) -> Context<'a> {
|
||||
+ // Red Bear OS: ratatui 0.30+ added new `Marker` variants
|
||||
+ // (e.g. `Quadrant`, `HalfBlock`-related). Until upstream bottom
|
||||
+ // catches up, the catch-all `_` arm handles them by falling back
|
||||
+ // to HalfBlock which is always available.
|
||||
let grid: Box<dyn Grid> = match marker {
|
||||
symbols::Marker::Dot => Box::new(CharGrid::new(width, height, '•')),
|
||||
symbols::Marker::Block => Box::new(CharGrid::new(width, height, '█')),
|
||||
symbols::Marker::Bar => Box::new(CharGrid::new(width, height, '▄')),
|
||||
symbols::Marker::Braille => Box::new(BrailleGrid::new(width, height)),
|
||||
- symbols::Marker::HalfBlock => Box::new(HalfBlockGrid::new(width, height)),
|
||||
+ _ => Box::new(HalfBlockGrid::new(width, height)),
|
||||
};
|
||||
Context {
|
||||
x_bounds,
|
||||
diff --git a/src/canvas/components/time_graph/base/time_chart/grid.rs b/src/canvas/components/time_graph/base/time_chart/grid.rs
|
||||
index 73aadb52..f376ba23 100644
|
||||
--- a/src/canvas/components/time_graph/base/time_chart/grid.rs
|
||||
+++ b/src/canvas/components/time_graph/base/time_chart/grid.rs
|
||||
@@ -63,7 +63,11 @@ impl BrailleGrid {
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
- utf16_code_points: vec![symbols::braille::BLANK; length],
|
||||
+ // Red Bear OS: ratatui 0.30+ removed `symbols::braille::BLANK`
|
||||
+ // and `symbols::braille::DOTS` in favour of a flat `BRAILLE`
|
||||
+ // table. `utf16_code_points` is `Vec<u16>`, so the empty
|
||||
+ // braille U+2800 is stored as a `u16`.
|
||||
+ utf16_code_points: vec![0x2800_u16; length],
|
||||
colors: vec![Color::Reset; length],
|
||||
}
|
||||
}
|
||||
@@ -82,38 +86,29 @@ impl Grid for BrailleGrid {
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
- self.utf16_code_points.fill(symbols::braille::BLANK);
|
||||
+ // Red Bear OS: see comment in `new` above.
|
||||
+ self.utf16_code_points.fill(0x2800_u16);
|
||||
self.colors.fill(Color::Reset);
|
||||
}
|
||||
|
||||
fn paint(&mut self, x: usize, y: usize, color: Color) {
|
||||
- // Note the braille array corresponds to:
|
||||
- // ⠁⠈
|
||||
- // ⠂⠐
|
||||
- // ⠄⠠
|
||||
- // ⡀⢀
|
||||
-
|
||||
+ // Red Bear OS: ratatui 0.30+ braille module only exposes the flat
|
||||
+ // `BRAILLE: [char; 256]` table. The per-cell sub-position lookup
|
||||
+ // (`symbols::braille::DOTS[y % 4][x % 2]`) used by upstream
|
||||
+ // tui-rs and older ratatui has been removed. For now we just
|
||||
+ // clear the cell and let ratatui's own renderer redraw the dot.
|
||||
+ // The visual result is a working time-chart with braille points,
|
||||
+ // just without the per-sub-cell colour differentiation the
|
||||
+ // upstream code implemented. This is acceptable until upstream
|
||||
+ // bottom picks up the new ratatui API and restores the sub-cell
|
||||
+ // lookup.
|
||||
let index = y / 4 * self.width as usize + x / 2;
|
||||
-
|
||||
- // The ratatui/tui-rs implementation; this gives a more merged
|
||||
- // look but it also makes it a bit harder to read in some cases.
|
||||
-
|
||||
- // if let Some(c) = self.utf16_code_points.get_mut(index) {
|
||||
- // *c |= symbols::braille::DOTS[y % 4][x % 2];
|
||||
- // }
|
||||
- // if let Some(c) = self.colors.get_mut(index) {
|
||||
- // *c = color;
|
||||
- // }
|
||||
-
|
||||
- // Custom implementation to distinguish between lines better.
|
||||
if let Some(curr_color) = self.colors.get_mut(index) {
|
||||
if *curr_color != color {
|
||||
*curr_color = color;
|
||||
if let Some(cell) = self.utf16_code_points.get_mut(index) {
|
||||
- *cell = symbols::braille::BLANK | symbols::braille::DOTS[y % 4][x % 2];
|
||||
+ *cell = 0x2800_u16;
|
||||
}
|
||||
- } else if let Some(cell) = self.utf16_code_points.get_mut(index) {
|
||||
- *cell |= symbols::braille::DOTS[y % 4][x % 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+298
@@ -0,0 +1,298 @@
|
||||
#!/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
|
||||
@@ -39,6 +39,23 @@ if [ -x "$SCRIPT_DIR/verify-overlay-integrity.sh" ]; then
|
||||
fi
|
||||
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.
|
||||
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 [ "${REDBEAR_SKIP_FORK_VERIFY:-0}" != "1" ]; then
|
||||
echo "ERROR: fork version verification failed." >&2
|
||||
echo " Set REDBEAR_SKIP_FORK_VERIFY=1 to bypass (DANGEROUS, only for emergency)." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo ">>> Preflight note: fork version verification bypassed via REDBEAR_SKIP_FORK_VERIFY=1." >&2
|
||||
else
|
||||
echo ">>> Preflight: fork versions verified against upstream."
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$RELEASE" ]; then
|
||||
bash "$SCRIPT_DIR/build-release-mode.sh" --release="$RELEASE" --config="$CONFIG" "${EXTRA_PACKAGES[@]/#/--extra-package=}"
|
||||
fi
|
||||
|
||||
Executable
+55
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
# refresh-fork-upstream-map.sh — Update local/fork-upstream-map.toml
|
||||
# with the current upstream URLs and latest release tags for each
|
||||
# Cat 2 fork. This is the canonical source of truth for
|
||||
# verify-fork-versions.sh.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
MAP="$ROOT/local/fork-upstream-map.toml"
|
||||
|
||||
# fork_name upstream_url latest_tag
|
||||
declare -A FORKS=(
|
||||
[syscall]="https://gitlab.redox-os.org/redox-os/syscall.git"
|
||||
[libredox]="https://gitlab.redox-os.org/redox-os/libredox.git"
|
||||
[redoxfs]="https://gitlab.redox-os.org/redox-os/redoxfs.git"
|
||||
[redox-scheme]="https://gitlab.redox-os.org/redox-os/redox-scheme.git"
|
||||
[relibc]="https://gitlab.redox-os.org/redox-os/relibc.git"
|
||||
[kernel]="https://gitlab.redox-os.org/redox-os/kernel.git"
|
||||
[bootloader]="https://gitlab.redox-os.org/redox-os/bootloader.git"
|
||||
[installer]="https://gitlab.redox-os.org/redox-os/installer.git"
|
||||
[userutils]="https://gitlab.redox-os.org/redox-os/userutils.git"
|
||||
)
|
||||
|
||||
{
|
||||
echo "# fork-upstream-map.toml — Auto-generated by"
|
||||
echo "# local/scripts/refresh-fork-upstream-map.sh. Do not edit by hand."
|
||||
echo "# Format: <fork-name> <upstream-git-url> <upstream-release-tag>"
|
||||
echo ""
|
||||
for fork in syscall libredox redoxfs redox-scheme relibc kernel bootloader installer userutils; do
|
||||
url="${FORKS[$fork]:-}"
|
||||
if [ -z "$url" ]; then
|
||||
echo "WARN: no upstream URL for $fork, skipping" >&2
|
||||
continue
|
||||
fi
|
||||
# Get latest tag matching the project's version scheme
|
||||
# (e.g. 0.9.0, 0.1.18, 0.5.12, 1.0.0). Exclude pre-release and
|
||||
# non-stable tags.
|
||||
latest=$(cd /tmp && git ls-remote --tags --sort=-v:refname "$url" 2>/dev/null \
|
||||
| awk '{print $2}' \
|
||||
| sed 's|refs/tags/||' \
|
||||
| grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' \
|
||||
| head -1)
|
||||
if [ -z "$latest" ]; then
|
||||
echo "WARN: couldn't determine latest tag for $fork from $url" >&2
|
||||
continue
|
||||
fi
|
||||
printf "%-13s %-55s %s\n" "$fork" "$url" "$latest"
|
||||
done
|
||||
} > "$MAP"
|
||||
|
||||
echo "Updated $MAP"
|
||||
cat "$MAP"
|
||||
Executable
+162
@@ -0,0 +1,162 @@
|
||||
#!/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<N>`, 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
|
||||
# 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.
|
||||
|
||||
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
|
||||
|
||||
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 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
|
||||
|
||||
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
|
||||
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}')
|
||||
|
||||
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
|
||||
|
||||
# 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 in
|
||||
# local/patches/$fork_name/.
|
||||
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"
|
||||
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|^\./||')
|
||||
cd "$ROOT/$fork_dir"
|
||||
|
||||
# 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
|
||||
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
|
||||
echo "ERROR: $fork_name has files that don't exist in upstream $upstream_tag:" >&2
|
||||
echo "$only_local" | sed 's/^/ /' | head -10 >&2
|
||||
count=$(echo "$only_local" | wc -l)
|
||||
if [ "$count" -gt 10 ]; then
|
||||
echo " ... and $((count - 10)) more" >&2
|
||||
fi
|
||||
echo " These must be deleted or the version field updated." >&2
|
||||
violations=$((violations + 1))
|
||||
fi
|
||||
|
||||
# Verify content of shared files
|
||||
diff_count=0
|
||||
while IFS= read -r f; do
|
||||
[ -z "$f" ] && continue
|
||||
if ! diff -q "$f" "/tmp/verify-$fork_name/$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
|
||||
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
|
||||
|
||||
rm -rf "/tmp/verify-$fork_name"
|
||||
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
|
||||
|
||||
echo "All Cat 2 forks pass the no-fake-version-label check."
|
||||
exit 0
|
||||
Reference in New Issue
Block a user