The gate job now runs in gitea.home.keithjackson.dev/home-network/toolbox@sha256:83a3501e... (the image images.yaml publishes from infra/devcontainer/Containerfile) instead of catthehacker/ubuntu:act-22.04 plus a per-run download of yamllint, check-jsonschema, kyverno, promtool and chainsaw. Local (dev.sh) and CI now gate on the identical toolchain. - actions/checkout (Node) replaced by a plain-git step with fetch-depth-0 semantics; the toolbox has no Node. The job token is not persisted into .git/config. - The install step is replaced by a check that every ci/versions.env pin equals the image's /etc/dev-toolchain-versions, so the pins stay enforced. - Renovate follows the digest through a `# renovate-digest:` marker; github-actions manager disabled for own images. - install-tools.sh kept (merge-gate.yaml blueprint + alert-rules-unit-tested check use it). Co-Authored-By: Claude Opus 5.5 <[email protected]>
81 lines
5.0 KiB
Markdown
81 lines
5.0 KiB
Markdown
---
|
|
date: 2026-11-08
|
|
branch: feat/gate-toolbox-container
|
|
title: The merge gate runs inside the CI-built toolbox image, pinned by digest — no per-run tool install
|
|
---
|
|
|
|
## What changed
|
|
|
|
`.gitea/workflows/gate.yml`, job `gate` (job name, the gate step, its env and its
|
|
`|| [ "$?" -eq 3 ]` tolerance are unchanged, so the required context `gate / gate (pull_request)`
|
|
is unchanged):
|
|
|
|
- **`container:`** `gitea.home.keithjackson.dev/home-network/toolbox@sha256:83a3501e…ef81` — the
|
|
image `images.yaml` published from `dd94101` (run 789; it is today's `main` and `sha-dd941016478c`
|
|
tag). The same image `infra/devcontainer/dev.sh` runs, so local and CI gate on the identical
|
|
toolchain. A `# renovate-digest: image=… tag=main` marker sits on the line above, the repo's
|
|
existing convention; `infra/gitea/registry-retention.sh` reads the same marker, so this digest
|
|
is protected from pruning.
|
|
- **`actions/checkout` replaced by a plain-git step.** The toolbox has no Node (measured:
|
|
`command -v node` → missing), and `actions/checkout` is a Node action. Chosen over adding Node to
|
|
the toolbox because that would need two sequential PRs (Containerfile, then a publish, then the
|
|
pin) and would put a JS runtime into the operator toolbox only to run one action. The step
|
|
reproduces what checkout@v7 `fetch-depth: 0` actually ran here (read from runs 490 and 495):
|
|
every branch to `refs/remotes/origin/*`, tags, plus the event SHA; detached for a PR,
|
|
`-B <branch>` for a push; asserts `HEAD == github.sha`. The job token goes to that one fetch via
|
|
`GIT_CONFIG_*` env and is **not** persisted in `.git/config` (nothing later needs network git).
|
|
- **"Install pinned CI toolchain" step removed**, replaced by **"Toolchain is the one
|
|
ci/versions.env pins"**: every `*_VERSION` in `ci/versions.env` must equal the image's
|
|
`/etc/dev-toolchain-versions`, else `::error::DRIFT` and the job fails. Without this, dropping
|
|
the installer would have made `ci/versions.env` an unenforced claim for everything except
|
|
promtool (which `alert-rules-unit-tested` already asserts).
|
|
- Header comments rewritten for the new job image (pull auth, cold-pull size, no-Node, why
|
|
`install-tools.sh` stays).
|
|
|
|
`renovate.json5`: the `renovate-digest` custom manager now also scans `.gitea/workflows/gate.yml`;
|
|
the `github-actions` manager is disabled for this repo's own images (same reason the `kubernetes`
|
|
manager already was: it would see a digest-only image and look up `latest`).
|
|
|
|
Comment/message wording only: `ci/gate.sh` (header + the strict-skip error text), `ci/lib.sh`,
|
|
`ci/README.md`, `.gitea/workflows/README.md` — they said the CI toolchain comes from
|
|
`install-tools.sh`.
|
|
|
|
`.gitea/workflows/scripts/install-tools.sh` is **kept**: `merge-gate.yaml` (dormant blueprint)
|
|
calls it, and `conformance/tests/l0-static/alert-rules-unit-tested/check.sh` asserts its promtool
|
|
wiring (not touched — another agent owns `conformance/`).
|
|
|
|
**Consequence to know:** every toolbox digest bump is now a Renovate PR touching `.gitea/**`,
|
|
i.e. a sensitive path needing an operator `/approve-override`. That is arguably correct (a new
|
|
toolchain changes what green means — and `ci/versions.env` was already sensitive), but it is a
|
|
new recurring operator touch.
|
|
|
|
## What I verified
|
|
|
|
- Registry pull auth: the Home-Network org is `public`; anonymous `/v2/token` → manifest `200`,
|
|
`docker-content-digest: sha256:83a3501e…ef81` for both `main` and `sha-dd941016478c`. So no
|
|
`container: credentials:` is needed. (The Actions token could not have been used anyway — no
|
|
package scope, per images.yaml's header.)
|
|
- Image: 466,912,274 bytes compressed across 6 layers (manifest sum), 1,541,728,125 bytes
|
|
unpacked (`podman image inspect`). Local cold pull 10.4s on the workstation.
|
|
- Image tool versions equal `ci/versions.env` for all 8 pins (kyverno 1.19.0, kyverno-json 0.0.3,
|
|
chainsaw 0.2.12, yamllint 1.35.1, kubectl 1.36.4, yq 4.44.6, promtool 3.14.0, check-jsonschema
|
|
0.29.4) — both via `/etc/dev-toolchain-versions` and by running each binary's `--version`.
|
|
`sleep`, `tail`, `git` 2.56.0, `bash`, `python3`, `jq`, `curl` present (act's entrypoint is
|
|
`/bin/sleep 10800`).
|
|
- The new checkout script, run in the pinned image against the public repo (auth header disabled
|
|
for the test only): PR ref `refs/pull/143/head` → detached at `60e3a24`, 1018 commits; push ref
|
|
`refs/heads/main` → `On branch main` at `e8d2666`; `origin/main` present in both. The
|
|
toolchain-drift step passes on the real `ci/versions.env` and exits 1 naming both tools when
|
|
yamllint/promtool pins are altered.
|
|
- Renovate marker regex (copied from renovate.json5) matches gate.yml exactly once, capturing
|
|
depName/tag/digest.
|
|
- LOCAL GATE: see below (filled after the run).
|
|
|
|
## What I did NOT verify
|
|
|
|
- Renovate actually opening a digest-bump PR for gate.yml — only the regex was tested, not a
|
|
Renovate run.
|
|
- The authenticated fetch with the real job token — only CI can do that; the PR's own gate run is
|
|
that test.
|
|
- Push-to-main behaviour in CI (only the PR event runs before merge).
|