Files
RedBear-OS/.gitea/workflows/build-system.yml
T
kellito 0f8ad8a50d build: ship scratch-rebuild skeleton + 21 tests (improvement #10 partial)
L-sized improvement #10 (cookbook scratch-rebuild) is now
PARTIALLY shipped: the M-sized foundation is a runnable
script that does the right thing in the common case.
Verification against real cascades + integration with
rebuild-cascade.sh remains for a separate session.

local/scripts/scratch-rebuild.sh (190 lines, +x):
  Step 1: discover autotools-using recipes by content regex
    (aclocal|autoreconf|libtoolize|automake|autoconf|gettextize|./configure)
    PLUS the AUTOTOOLS_CORE list (m4, autoconf, automake,
    libtool, bison, flex, gettext) which are always-included
    because they are autotools infrastructure even if they
    don't directly invoke aclocal.
  Step 2: compute transitive closure via BFS over the recipe
    TOML dep graph, including both [build].dependencies and
    [build].dev_dependencies. Found 6 autotools users in the
    live tree: bison, diffutils, flex, grub, libtool, m4.
  Step 3: for each recipe in the closure, delete
    target/<arch>/{build,sysroot,stage.tmp}/ — PRESERVE source/
    so we don't re-fetch the upstream tar.
  Step 4: re-cook in dep order with --jobs=N (default 4) so
    the rebuild itself runs in parallel via the dep-aware
    scheduler (#1).

Cook errors during Step 4 do NOT abort the script with
exit 1 — a failed cook may indicate a missing upstream dep
(legitimate on a fresh checkout) rather than a real bug.
The user inspects the log and re-runs after addressing the
dep. This is documented in the header + Step 4 comment.

Supports --dry-run, --jobs=N, --help. Env overrides for
RECIPES_DIR + LOG_DIR (mirroring the migration script's
test escape hatch pattern, used by the test suite below).

21 unit tests in local/scripts/tests/test_scratch_rebuild.py:
  TestAutotoolsCoreList (3)         — m4, libtool, bison/flex
                                     in AUTOTOOLS_CORE
  TestAutotoolsContentRegex (8)     — catches each canonical
                                     autotools command; does
                                     NOT match cmake/make/meson
  TestRecipeDepParsing (4)          — parses dependencies and
                                     dev_dependencies; both;
                                     neither
  TestScriptHelp (1)                — --help describes the
                                     script
  TestScriptStructure (5)           — executable bit; uses
                                     ./target/release/repo;
                                     PRESERVES source/; uses
                                     --jobs=N; dry-run safe

Test count: 99 -> 120 (all in <1s).

The test file also surfaces a real Python regex gotcha:
`^[[:space:]]*` (POSIX char class with quantifier) silently
fails to match the empty string under Python's regex
engine, while `^[\s]*` (shorthand) works correctly. The
test regex uses the shorthand to avoid this.

Wired into:
  make test-scratch-dry-run  ->  scratch-rebuild.sh --dry-run
  Gitea Actions job scratch-dry-run (job 6 of 10, every PR)

With this commit, 9 of 10 build-system improvements in
BUILD-SYSTEM-IMPROVEMENTS.md are DONE (1 PARTIAL on #10);
the remaining 1 is #7A (QML gate, Qt6 engine fix, not a
cookbook improvement).

Verified: `./local/scripts/scratch-rebuild.sh --dry-run`
correctly discovers 6 autotools users and computes the
6-recipe closure. `make test-lint-scripts` still passes
120/120 tests in <1s. Gitea workflow YAML validates with
10 jobs total (was 9).
2026-06-12 16:12:49 +03:00

276 lines
11 KiB
YAML

# Red Bear OS — Gitea Actions (host-execution)
#
# Per local/docs/BUILD-SYSTEM-V6-HARDENING-POSTMORTEM.md, this is
# the v6.0 canonical CI pipeline. Runs on a host-based Gitea Actions
# runner (no Docker) against a Manjaro/Arch host. Three stages:
#
# 1. lint: build-system lint + test suite. Cheap, offline, no
# QEMU. Required to pass before a merge.
# 2. build: the actual build, gated on lint passing. Heavy
# (30-120 min on a fresh cache).
# 3. smoke: boot the produced image in QEMU, verify the login
# prompt. Nightly only.
#
# Exit-code contract for the lint stage:
# audit scripts return 0=clean, 1=failures, 2=all-skipped
# `make lint-build-system` returns 2 in --no-fetch mode (no
# audit was performed) — this is CI-safe: a fresh runner has
# no network, so the "all skipped" signal is the expected
# steady state. We accept 0 OR 2 as "pass" and 1 as "fail".
name: build-system
on:
push:
branches:
- 0.2.3
pull_request:
branches:
- 0.2.3
schedule:
# Nightly full audit + smoke test at 04:00 UTC
- cron: "0 4 * * *"
env:
REDBEAR_RELEASE: "0.2.3"
jobs:
# ---------------------------------------------------------------------------
# Stage 1a: unit tests
# ---------------------------------------------------------------------------
unit-tests:
name: Unit tests (120 cases, <1s)
runs-on: self-hosted
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 10
- name: Run unit tests
run: python3 -m unittest discover -s local/scripts/tests -v # ---------------------------------------------------------------------------
# Stage 1b: offline lint (every PR + branch push)
# ---------------------------------------------------------------------------
lint-offline:
name: Lint build system (offline, no network)
runs-on: self-hosted
needs: [unit-tests]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 10
- name: Lint (offline)
# Returns exit 2 in --no-fetch mode when all entries are
# skipped; the audit script is HONEST about it. CI-safe:
# 0 = clean, 1 = bug found, 2 = no audit performed.
# The conditional below treats both 0 and 2 as success.
run: |
make lint-build-system && rc=0 || rc=$?
case $rc in
0|2) echo "Lint result: $rc (clean or no-op)" ;;
1) echo "Lint FAILED with bugs"; exit 1 ;;
*) echo "Lint exited unexpectedly with $rc"; exit $rc ;;
esac
# ---------------------------------------------------------------------------
# Stage 1c: full lint with network (nightly only)
# ---------------------------------------------------------------------------
lint-network:
name: Lint build system (full, with network)
runs-on: self-hosted
needs: [unit-tests]
if: github.event_name == 'schedule'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 10
- name: Lint (full, with network)
# Clones each upstream tree at the pinned rev and validates
# the patch is durable. Slow: 5-15 minutes. Network-dependent.
run: make lint-build-system-full
continue-on-error: true # tolerate transient network flakes
# ---------------------------------------------------------------------------
# Stage 1d: per-recipe policy lint (R1/R2 violations)
# ---------------------------------------------------------------------------
lint-recipe:
name: Lint recipes (R1/R2 policy, every PR)
runs-on: self-hosted
needs: [unit-tests]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 10
- name: Lint recipes (R1/R2)
# Per build-system improvement #5. Catches:
# - R1-NO-PATCH-FILE (overlay patches missing)
# - R1-PATH-SOURCE (in-tree component not using path=)
# - R2-INLINE-SED (sed -i without cookbook_apply_patches)
# - R2-PATCHES-DIR-UNUSED (patches dir but no apply call)
# - NO-LEGACY-MAKE (make all/live CONFIG_NAME=)
# - R1-LEGACY-APPLY-PATCHES (apply-patches.sh reference)
# - DEP-NOT-FOUND (dep doesn't resolve to a recipe)
# 1.1s for 171 recipes. Offline.
run: make lint-recipe
# ---------------------------------------------------------------------------
# Stage 1e: KF6 sed migration dry-run (smoke test)
# ---------------------------------------------------------------------------
migration-dry-run:
name: Migration dry-run (C-7 smoke test)
runs-on: self-hosted
needs: [unit-tests]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 10
- name: Run migration script --dry-run
# Smoke test: the C-7 KF6 sed migration script must
# discover at least one candidate recipe and emit the
# per-recipe plan. --limit=1 keeps the test under 5s.
# Does NOT do any real fetches, cooks, or patch writes.
# Catches: discovery regression, path/name argument
# mixup, permission issues.
run: make test-migration-dry-run
# ---------------------------------------------------------------------------
# Stage 1f: scratch-rebuild dry-run (smoke test for #10)
# ---------------------------------------------------------------------------
scratch-dry-run:
name: Scratch rebuild dry-run (build-system improvement #10 smoke test)
runs-on: self-hosted
needs: [unit-tests]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 10
- name: Run scratch-rebuild --dry-run
# Smoke test: the autotools-detection + dep-closure BFS
# must discover at least one autotools recipe and compute
# a non-empty closure. <2s wall-clock.
# Does NOT do any real rm, fetch, or cook.
# Catches: autotools regex regression, dep parser regression,
# BFS fixpoint regression, permission issues.
run: make test-scratch-dry-run
# ---------------------------------------------------------------------------
# Stage 1g: docs regression check
# ---------------------------------------------------------------------------
lint-docs:
name: Lint docs (no legacy build commands)
runs-on: self-hosted
needs: [unit-tests]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 10
- name: Verify no doc still points at apply-patches.sh
# In all .md files outside /source/ and /boot-logs/, the
# only allowed references to legacy build commands are in
# clearly framed "warning" or "advanced/unsafe" contexts
# documented in local/AGENTS.md, the SCRIPT-BEHAVIOR-MATRIX,
# and the BUILD-SYSTEM-V6-HARDENING-POSTMORTEM (which
# documents the v5.x-to-v6.0 transition historically). The
# canonical build entry is local/scripts/build-redbear.sh.
run: |
if grep -rn 'apply-patches\.sh' --include='*.md' . \
| grep -v '/source/' \
| grep -v '/boot-logs/' \
| grep -v 'AGENTS\.md' \
| grep -v 'local/docs/SCRIPT-BEHAVIOR-MATRIX\.md' \
| grep -v 'local/docs/BUILD-SYSTEM-V6-HARDENING-POSTMORTEM\.md' \
| grep -v 'local/docs/BUILD-SYSTEM-IMPROVEMENTS\.md' \
| grep -vE 'VERIFIED|DEPRECATED|ARCHIVED|legacy|historical' ; then
echo "ERROR: docs still reference apply-patches.sh as a primary path"
grep -rn 'apply-patches\.sh' --include='*.md' . \
| grep -v '/source/' \
| grep -v '/boot-logs/' \
| grep -v 'AGENTS\.md' \
| grep -v 'local/docs/SCRIPT-BEHAVIOR-MATRIX\.md' \
| grep -v 'local/docs/BUILD-SYSTEM-V6-HARDENING-POSTMORTEM\.md' \
| grep -v 'local/docs/BUILD-SYSTEM-IMPROVEMENTS\.md' \
| grep -vE 'VERIFIED|DEPRECATED|ARCHIVED|legacy|historical'
exit 1
fi
# ---------------------------------------------------------------------------
# Stage 2a: build redbear-mini (every PR touching build-system code)
# ---------------------------------------------------------------------------
build-mini:
name: Build redbear-mini (30-45 min)
runs-on: self-hosted
needs: [lint-offline, lint-docs]
# Only run on changes that could affect the build:
# - the build-system scripts under local/scripts/
# - the AGENTS.md / local/AGENTS.md knowledge bases
# - mk/ and src/ (cookbook internals)
# - the root Makefile
if: |
contains(github.event.pull_request.title, '[build]') ||
contains(github.event.pull_request.body, '[build]') ||
github.event_name == 'schedule' ||
contains(toJSON(github.event.commits.*.added), 'mk/') ||
contains(toJSON(github.event.commits.*.modified), 'mk/') ||
contains(toJSON(github.event.commits.*.added), 'src/') ||
contains(toJSON(github.event.commits.*.modified), 'src/') ||
contains(toJSON(github.event.commits.*.added), 'local/scripts/') ||
contains(toJSON(github.event.commits.*.modified), 'local/scripts/')
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Build redbear-mini
# Heavy build; allow_failure lets MRs merge even if a
# cook flakes (the next nightly will catch it).
run: ./local/scripts/build-redbear.sh redbear-mini
continue-on-error: true
# ---------------------------------------------------------------------------
# Stage 2b: build redbear-full (nightly only)
# ---------------------------------------------------------------------------
build-full:
name: Build redbear-full (60-120 min)
runs-on: self-hosted
needs: [lint-offline, lint-docs]
if: github.event_name == 'schedule'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Build redbear-full
run: ./local/scripts/build-redbear.sh redbear-full
continue-on-error: true
# ---------------------------------------------------------------------------
# Stage 3: smoke test (nightly only)
# ---------------------------------------------------------------------------
smoke:
name: Smoke test (QEMU boot, nightly)
runs-on: self-hosted
needs: [build-mini]
if: github.event_name == 'schedule'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Boot redbear-mini in QEMU
# QEMU may not be installed on the host runner. Tolerate.
run: make qemu CONFIG_NAME=redbear-mini
continue-on-error: true
timeout-minutes: 15