From d22ae0dea297cd35a4d765febeb2f398d59b01b7 Mon Sep 17 00:00:00 2001 From: vasilito Date: Sat, 1 Aug 2026 03:56:10 +0300 Subject: [PATCH] test: regression suite for vendored-source staleness invalidation + wire into CI Proves build-redbear.sh's vendored source/ staleness loop: seeds on first sight (no spurious rebuild), stays quiet unchanged, and invalidates target/ on committed/uncommitted/untracked source/ changes. Keeps the loop body in sync. --- .github/workflows/redbear-ci.yml | 3 + .../scripts/test-vendored-source-staleness.sh | 66 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100755 local/scripts/test-vendored-source-staleness.sh diff --git a/.github/workflows/redbear-ci.yml b/.github/workflows/redbear-ci.yml index aef56f4d81..e456aff7a4 100644 --- a/.github/workflows/redbear-ci.yml +++ b/.github/workflows/redbear-ci.yml @@ -35,6 +35,9 @@ jobs: - name: Versioning machinery unit tests run: ./local/scripts/test-versioning-machinery.sh + - name: Vendored-source staleness invalidation test + run: ./local/scripts/test-vendored-source-staleness.sh + - name: External recipe source-version consistency # Fails if any tar recipe's vendored source/ (or tracked source.tar) # diverges from its declared version — the Qt/KDE class of bug. Mirrors diff --git a/local/scripts/test-vendored-source-staleness.sh b/local/scripts/test-vendored-source-staleness.sh new file mode 100755 index 0000000000..bbfda04ef5 --- /dev/null +++ b/local/scripts/test-vendored-source-staleness.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# test-vendored-source-staleness.sh — regression test for build-redbear.sh's +# vendored-recipe source-staleness invalidation (the loop that rm's a recipe's +# target/ when its git-committed source/ tree changes). +# +# Why: cookbook keys its build cache on source.tar's hash, but the actual build +# input for a vendored recipe is source/. A version bump that changes source/ +# without changing source.tar (the Qt 6.11.0->6.11.1 case) otherwise reuses a +# stale stage and ships the wrong version. This test proves the loop SEEDs on +# first sight (no spurious rebuild), stays quiet when unchanged, and invalidates +# on both committed and uncommitted source/ changes. +# +# The loop body below MUST stay in sync with build-redbear.sh. Zero network. +set -u +PASS=0; FAIL=0 +ok(){ PASS=$((PASS+1)); printf ' ok %s\n' "$1"; } +no(){ FAIL=$((FAIL+1)); printf ' FAIL %s\n %s\n' "$1" "${2:-}"; } + +T="$(mktemp -d)"; trap 'rm -rf "$T"' EXIT +PROJECT_ROOT="$T"; C_INFO=">>>"; C_RESET="" +git -C "$T" init -q; git -C "$T" config user.email t@t; git -C "$T" config user.name t + +# --- loop body copied verbatim from build-redbear.sh (keep in sync) --- +run_loop(){ + while IFS= read -r _rt; do + _rdir="$(dirname "$_rt")"; _sdir="$_rdir/source" + [ -d "$_sdir" ] || continue + _tgt="$_rdir/target/x86_64-unknown-redox" + [ -f "$_tgt/stage.pkgar" ] || continue + _relsrc="${_sdir#"$PROJECT_ROOT"/}" + _tree="$(git -C "$PROJECT_ROOT" rev-parse "HEAD:$_relsrc" 2>/dev/null || echo "")" + [ -n "$_tree" ] || continue + _dirty="" + git -C "$PROJECT_ROOT" diff --quiet HEAD -- "$_relsrc" 2>/dev/null || _dirty="-dirty" + [ -n "$(git -C "$PROJECT_ROOT" ls-files --others --exclude-standard -- "$_relsrc" 2>/dev/null)" ] && _dirty="-dirty" + _cur="${_tree}${_dirty}"; _fp="$_tgt/.redbear-source-tree" + _last="$(cat "$_fp" 2>/dev/null || echo "")" + if [ -z "$_last" ]; then printf '%s\n' "$_cur" > "$_fp"; echo "SEED" + elif [ "$_cur" != "$_last" ]; then echo "INVALIDATE"; rm -rf "$_rdir/target" + else echo "UNCHANGED"; fi + done < <(find "$PROJECT_ROOT/local/recipes" -name recipe.toml -not -path '*/wip/*' 2>/dev/null) +} + +R="$T/local/recipes/foo" +mkdir -p "$R/source" "$R/target/x86_64-unknown-redox" +echo v1 > "$R/source/f.c"; printf '[package]\nname="foo"\n' > "$R/recipe.toml" +echo pkgar > "$R/target/x86_64-unknown-redox/stage.pkgar" +git -C "$T" add -A; git -C "$T" commit -qm init + +[ "$(run_loop)" = SEED ] && [ -d "$R/target" ] && ok "first observation seeds + keeps target" || no "seed" +[ "$(run_loop)" = UNCHANGED ] && [ -d "$R/target" ] && ok "unchanged keeps target" || no "unchanged" +echo v2 > "$R/source/f.c"; git -C "$T" add -A; git -C "$T" commit -qm change +[ "$(run_loop)" = INVALIDATE ] && [ ! -d "$R/target" ] && ok "committed source change invalidates target" || no "committed-change" +# rebuild target + reseed, then dirty (uncommitted) change +mkdir -p "$R/target/x86_64-unknown-redox"; echo pkgar > "$R/target/x86_64-unknown-redox/stage.pkgar" +run_loop >/dev/null +echo v3-dirty > "$R/source/f.c" # uncommitted +[ "$(run_loop)" = INVALIDATE ] && [ ! -d "$R/target" ] && ok "uncommitted (dirty) source change invalidates" || no "dirty-change" +# untracked new file in source/ also counts as dirty +mkdir -p "$R/target/x86_64-unknown-redox"; echo pkgar > "$R/target/x86_64-unknown-redox/stage.pkgar" +git -C "$T" add -A; git -C "$T" commit -qm v3; run_loop >/dev/null +echo new > "$R/source/added.c" # untracked +[ "$(run_loop)" = INVALIDATE ] && ok "untracked new file in source/ invalidates" || no "untracked-file" + +echo " PASS: $PASS FAIL: $FAIL" +[ "$FAIL" -eq 0 ] && { echo " ALL GREEN"; exit 0; } || exit 1