Files
RedBear-OS/.gitea/workflows/build-system.yml
T
kellito 975cda686f build: add make lint-build-system-all aggregate + 11-job CI
Add a single-target aggregate `make lint-build-system-all`
that runs every offline-safe lint + every test + every
smoke test in one shot. Per the user request to make the
'build system healthy?' question easy to answer.

New `make lint-build-system-all` target chains:
  make test-lint-scripts        (120 Python unit tests)
  make test-migration-dry-run   (C-7 KF6 sed migration)
  make test-scratch-dry-run     (improvement #10 skeleton)
All exit 0 in offline mode; <3s wall-clock total.

The existing `make lint-build-system` chain was
incomplete — it ran lint-patches, lint-kf6-deps, and
lint-cook-recipe but not lint-recipe, test-migration-dry-run,
or test-scratch-dry-run. This commit fixes that:
  make lint-build-system: lint-patches lint-kf6-deps \
                        lint-cook-recipe lint-recipe

The two aggregates serve different purposes:
  - `lint-build-system` is the historical aggregate
    including lint-patches. lint-patches returns 2 in
    --no-fetch mode (all skipped) so the Gitea workflow
    wraps it in a case statement. The original use case was
    'is the project build-system clean?', which is
    network-dependent.
  - `lint-build-system-all` is the new offline-only
    aggregate. It does NOT include lint-patches, so it
    always exits 0 on a healthy tree. The new Gitea job
    depends on unit-tests + lint-recipe + migration-dry-run
    + scratch-dry-run (so it can run after the four per-step
    lints have already validated the individual layers).

Wired into:
  Makefile:
    - `make lint-build-system-all` + `make lint-build-system`
      both now include lint-recipe.
    - Both targets added to .PHONY.
  Gitea Actions:
    - New job `lint-build-system-all` (job 7 of 11, depends
      on the four per-step lint jobs).
    - Renumbered the docs stage to 1i.
  BUILD-SYSTEM-IMPROVEMENTS.md:
    - Make targets table: added scratch-rebuild, lint-build-system-all.
  BUILD-SYSTEM-V6-HARDENING-POSTMORTEM.md:
    - Durability caveat: 11 most recent commits -> 12 most
      recent (added e1c2e7958); updated flow description to
      include 'postmortem rebalance in e1c2e7958'.

Verified:
  `make lint-build-system-all` passes in <3s.
  11-job Gitea Actions pipeline YAML validates.
  120/120 Python tests pass.
2026-06-12 16:38:22 +03:00

298 lines
12 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: aggregate build-system health check
# ---------------------------------------------------------------------------
lint-build-system-all:
name: Lint build system (aggregate, all offline-safe targets)
runs-on: self-hosted
needs: [unit-tests, lint-recipe, migration-dry-run, scratch-dry-run]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 10
- name: Run aggregate
# Single-target aggregate of every offline-safe lint +
# every test + every smoke test. If this passes, the
# build system is healthy.
# Excludes `lint-patches` (returns 2 in --no-fetch mode
# when all entries are skipped — that path is gated
# separately by lint-offline + the conditional case).
run: make lint-build-system-all
# ---------------------------------------------------------------------------
# Stage 1i: 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