docs: CHANGELOG + REPORT for the CI workflow
gates / gates (push) Successful in 1s

This commit is contained in:
2026-08-02 16:35:45 +02:00
parent aa57588f55
commit 7cb58ecdf8
2 changed files with 62 additions and 54 deletions
+31
View File
@@ -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
+31 -54
View File
@@ -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`.