This commit is contained in:
@@ -1,5 +1,36 @@
|
|||||||
# Changelog
|
# 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)
|
## 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
|
**`scripts/catalog_gates.py --fast`** selects only gates that touch no network and no container
|
||||||
|
|||||||
@@ -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
|
**Overwritten** per the standing rule. The prior contents (`catalog_gates --fast` + the pre-push hook, same day) have their durable record in `CHANGELOG.md`.
|
||||||
day) have their durable record in `CHANGELOG.md`; nothing was lost.
|
|
||||||
|
|
||||||
**Change:** `scripts/catalog_gates.py` gains `--fast`; `.githooks/pre-push` (new) runs it;
|
**No version bump, no build, no deploy.** This adds `.gitea/workflows/gates.yml` and a CHANGELOG
|
||||||
`scripts/test_catalog_gates.py` (new) pins its behaviour; `CLAUDE.md` records the hook.
|
entry. Nothing compiled changed.
|
||||||
**No gate logic changed, no template touched, no version** (this repo carries none).
|
|
||||||
|
|
||||||
| Commit | What |
|
## What changed
|
||||||
|---|---|
|
|
||||||
| `c3e4bb1` | `scripts/catalog_gates.py` `--fast` + `scripts/test_catalog_gates.py` + `.githooks/pre-push` + `CLAUDE.md` |
|
|
||||||
| `340ff2a` | `CHANGELOG.md` |
|
|
||||||
|
|
||||||
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,
|
**A failed run emails the operator.** Probe P5 measured that Gitea itself sends **nothing** on a
|
||||||
`check-image-pins.py`. `check-image-resolvable.py` (network) and `check-volume-persistence.py`
|
failed run — no mail, no notification row, no log line — so the workflow sends its own via Resend and
|
||||||
(Docker, minutes per app) are **not** in it.
|
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
|
**`--fast` only, and that is the point.** `check-image-pins.py` runs; `check-image-resolvable.py`
|
||||||
bypassed within a week, and the bypass becomes the habit.** Both remain deliberate periodic runs — at
|
(network) and `check-volume-persistence.py` (Docker, minutes per app) do **not**. CI that pulls 53
|
||||||
the start of a catalog campaign, before a publish train that vouches the catalog, and whenever a
|
images on every push gets disabled, and the bypass becomes the habit. No sibling clone is needed
|
||||||
template's `volumes:` block or image tag changes — on a scratch host, never a customer box.
|
here — unlike the controller and the agent, `catalog_gates --fast` does not invoke the shared reuse
|
||||||
|
checker.
|
||||||
**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.
|
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
|
|
||||||
```
|
First run: run #1, id=10, conclusion **success**, sha `aa57588f`.
|
||||||
catalog_gates — static gate only [--fast]
|
|
||||||
--fast SKIPPED: image-resolvable, volume-persistence — …
|
|
||||||
|
|
||||||
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)
|
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,
|
||||||
all catalog gates OK
|
the security posture and the teardown: `felhom.eu/documentation/audits/SPIKE-ci-runner-2026-08-02.md`
|
||||||
```
|
and `felhom.eu/REPORT.md`.
|
||||||
|
|
||||||
`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`.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user