From 7cb58ecdf8e78fe3544dddce8e88c07b96b5b7f4 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Sun, 2 Aug 2026 16:35:45 +0200 Subject: [PATCH] docs: CHANGELOG + REPORT for the CI workflow --- CHANGELOG.md | 31 +++++++++++++++++++ REPORT.md | 85 +++++++++++++++++++--------------------------------- 2 files changed, 62 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee28569..b2cbafa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,36 @@ # Changelog +## CI — the static catalog gate runs on every push (2026-08-02, R-168) + +**No version bump, no build, no deploy** — this adds a workflow file only. Stated explicitly so the +omission reads as a decision rather than a miss. + +**`.gitea/workflows/gates.yml` (new).** Triggers on `push`, `runs-on: felhom-gates`, obtains the +source with a shallow `git fetch` of the **exact pushed SHA** from the in-cluster Gitea Service, and +runs this repo's entry point with `--fast` — nothing else. **No `uses:` step anywhere**: JavaScript +actions need a node runtime the host-mode runner does not have, and probe P3 measured a plain +`git fetch` as sufficient. No `|| true`; the entry point's exit code IS the job's result. + +**It REPORTS, it cannot REFUSE**, and the workflow header says so: this repo pushes straight to +`main` with no pull request, so there is no merge for a status check to stand at. The refusing half +is `.githooks/pre-push`, which is per-clone and `--no-verify`-able; this half notices when that was +skipped. Making CI blocking needs branch protection plus a PR workflow → felhom.eu `OPEN-ITEMS.md` +R-169, an operator decision. + +**A failed run emails the operator** via Resend and prints the provider's accepted id, because probe +P5 measured that Gitea itself sends nothing at all on a failed run. Demonstrated end to end on a real +red run (`RESEND-ACCEPTED id=…`), not assumed. Full detail: +`felhom.eu/documentation/audits/SPIKE-ci-runner-2026-08-02.md`. + +**`--fast` only, and that is the point.** `check-image-pins.py` runs; `check-image-resolvable.py` +(network) and `check-volume-persistence.py` (Docker, minutes per app) do **not**. CI that pulls 53 +images on every push gets disabled, and the bypass becomes the habit. Measured in the first run: +`image-pin gate OK — 53 templates, 0 unpinned images`, with both runtime gates announced as skipped +and their own output absent from the log. They remain deliberate periodic runs. + +No sibling clone is needed here — unlike the controller and the agent, `catalog_gates --fast` does +not invoke the shared reuse checker. + ## 2026-08-02 — `--fast` for the pre-push hook (no version: this repo carries none) **`scripts/catalog_gates.py --fast`** selects only gates that touch no network and no container diff --git a/REPORT.md b/REPORT.md index 65ddf51..cdd1c34 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,66 +1,43 @@ -# REPORT — `catalog_gates --fast` and the pre-push hook (2026-08-02) +# REPORT — CI runs the static catalog gate on every push (R-168, 2026-08-02) -**Overwritten** per the standing rule. The prior contents (the entry point itself, earlier the same -day) have their durable record in `CHANGELOG.md`; nothing was lost. +**Overwritten** per the standing rule. The prior contents (`catalog_gates --fast` + the pre-push hook, same day) have their durable record in `CHANGELOG.md`. -**Change:** `scripts/catalog_gates.py` gains `--fast`; `.githooks/pre-push` (new) runs it; -`scripts/test_catalog_gates.py` (new) pins its behaviour; `CLAUDE.md` records the hook. -**No gate logic changed, no template touched, no version** (this repo carries none). +**No version bump, no build, no deploy.** This adds `.gitea/workflows/gates.yml` and a CHANGELOG +entry. Nothing compiled changed. -| Commit | What | -|---|---| -| `c3e4bb1` | `scripts/catalog_gates.py` `--fast` + `scripts/test_catalog_gates.py` + `.githooks/pre-push` + `CLAUDE.md` | -| `340ff2a` | `CHANGELOG.md` | +## What changed -Baseline on arrival: `fd7747d1293c`, clean, `HEAD == origin/main` — matched the spec's anchor. +`.gitea/workflows/gates.yml` — on every push, a Gitea Actions runner obtains this repo at the +**exact pushed SHA** (shallow `git fetch`, no `uses:` step anywhere) and runs +`python3 scripts/catalog_gates.py --fast` and nothing else. The exit code is the job's result: no `|| true`, no +pipe that could swallow it. -## What `--fast` selects, and why the other two are excluded +**It REPORTS, it cannot REFUSE**, and the workflow header says so in the pre-push hook's voice: this +repo pushes straight to `main` with no pull request, so there is no merge for a status check to stand +at. The refusing half is `.githooks/pre-push` (per-clone, `--no-verify`-able); this half notices when +that was skipped. Making CI blocking needs branch protection plus a PR workflow — an operator +decision, tracked as `felhom.eu` `OPEN-ITEMS.md` **R-169**. -`--fast` runs only gates that touch **no network and no container runtime**. Today that is gate 1, -`check-image-pins.py`. `check-image-resolvable.py` (network) and `check-volume-persistence.py` -(Docker, minutes per app) are **not** in it. +**A failed run emails the operator.** Probe P5 measured that Gitea itself sends **nothing** on a +failed run — no mail, no notification row, no log line — so the workflow sends its own via Resend and +prints the provider's accepted id, making "a message left the machine" an observable. Demonstrated on +a real red run in `felhom.eu`: `RESEND-ACCEPTED id=5ff34766-c5f8-4588-8104-08296aeb45ab`. -That exclusion is the point, not a compromise: **a push that pulls images and starts containers gets -bypassed within a week, and the bypass becomes the habit.** Both remain deliberate periodic runs — at -the start of a catalog campaign, before a publish train that vouches the catalog, and whenever a -template's `volumes:` block or image tag changes — on a scratch host, never a customer box. - -**The skip is announced**, with its reason and with what still owes a run. A silently narrowed run -reads as "covered everything" when it did not. Default behaviour with no flag is **unchanged**: all -three gates, exactly as before. - -## `.githooks/pre-push` - -Runs `catalog_gates.py --fast` and refuses the push, printing a line before and after because an -absent log line is not evidence a hook ran. Limits, written into the hook: **per-clone** -(`core.hooksPath` is local config; this clone is armed — `git config --get core.hooksPath` → -`.githooks`) and **`git push --no-verify` bypasses it on purpose**. It was **not** used against this -repo; both real pushes ran the hook and passed. - -This makes R-161's convention half automatic-ish, not automatic. The unbypassable half is CI, which -has no execution mechanism here yet and is now tracked as `felhom.eu` `OPEN-ITEMS.md` **R-168**. - -## `test_catalog_gates.py` — pins the CONTENT, not just the exit code - -Five tests: the static gate's own stdout must appear (an inert runner prints the summary while -calling nothing), the runtime gates' must **not**, the skip must be announced, and the no-flag path -must still select all three. **Red-proof:** replacing `run_gate`'s body with `return 0` turns it red. +**`--fast` only, and that is the point.** `check-image-pins.py` runs; `check-image-resolvable.py` +(network) and `check-volume-persistence.py` (Docker, minutes per app) do **not**. CI that pulls 53 +images on every push gets disabled, and the bypass becomes the habit. No sibling clone is needed +here — unlike the controller and the agent, `catalog_gates --fast` does not invoke the shared reuse +checker. ## Verification -``` -catalog_gates — static gate only [--fast] - --fast SKIPPED: image-resolvable, volume-persistence — … +First run: run #1, id=10, conclusion **success**, sha `aa57588f`. -image-pin gate OK — 53 templates, 0 unpinned images +**Scenario F proven from the run log:** `image-pin gate OK — 53 templates, 0 unpinned images`, the +skip announced (`--fast SKIPPED: image-resolvable, volume-persistence …`), and **zero** lines of +resolvability output, volume-persistence output, canary output or image pulls. - image-pins OK (exit 0) - -all catalog gates OK -``` - -`python3 scripts/test_catalog_gates.py` → **5 tests, OK**. -`python3 scripts/test_check_volume_persistence.py` — untouched, still green. - -Full cross-repo detail, every red-proof, the `core.hooksPath` probe measurements and the hub -deployment: `felhom.eu/REPORT.md`. +The runner is unprivileged host-mode, shared with the other three repos on a single owner-scoped +registration (measured: all four repos' tasks claimed by `felhom-gates-runner`). Full probe detail, +the security posture and the teardown: `felhom.eu/documentation/audits/SPIKE-ci-runner-2026-08-02.md` +and `felhom.eu/REPORT.md`.