This commit is contained in:
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user