feat(ci): run the merge gate inside the CI-built toolbox image, pinned by digest #147

Merged
jakman85 merged 4 commits from feat/gate-toolbox-container into main 2026-11-08 06:51:00 +00:00
Owner

What

The gate job now runs inside the CI-built toolbox image, pinned by digest, instead of catthehacker/ubuntu:act-22.04 + a per-run download of yamllint / check-jsonschema / kyverno / promtool / chainsaw (~11s of every run, measured on runs 494/495). Local dev.sh and CI now gate on the identical image.

container:
  # renovate-digest: image=gitea.home.keithjackson.dev/home-network/toolbox tag=main
  image: gitea.home.keithjackson.dev/home-network/toolbox@sha256:83a3501efe34fa0b039e3f0d360738686a0f59109866c49779768f5d5ed0ef81

Unchanged: job name gate (required context gate / gate (pull_request)), the gate step, its env, and || [ "$?" -eq 3 ].

Decisions

  • Plain-git checkout instead of adding Node to the toolbox. actions/checkout is a Node action and the toolbox has no Node. Adding Node would need two sequential PRs (Containerfile → publish → pin). The git step reproduces what checkout@v7 fetch-depth: 0 ran here (all branches → origin/*, tags, event SHA; detached for PR, -B <branch> for push) and asserts HEAD == github.sha. The job token goes to that one fetch via GIT_CONFIG_* env, and is not persisted.
  • No pull credentials. Org Home-Network is public and the registry issues anonymous pull tokens (measured: anonymous /v2/token → manifest 200).
  • Pins stay enforced. The install step is replaced by a check that every *_VERSION in ci/versions.env equals the image's /etc/dev-toolchain-versions (::error::DRIFT otherwise; negative-tested).
  • Renovate follows the digest via the existing # renovate-digest: convention (renovate.json5 now scans gate.yml; github-actions manager disabled for own images, same as kubernetes). registry-retention.sh reads the same marker.
  • install-tools.sh kept: merge-gate.yaml and alert-rules-unit-tested still reference it.

Image: 466.9 MB compressed / 1.54 GB unpacked. The DinD store is an emptyDir, so the first run after a runner restart or digest bump pays one pull.

Consequence: future toolbox digest bumps touch .gitea/** → sensitive path → need an operator override.

Gate

Local, in the pinned image itself: rc 3, gitea-settings the only stage not run. Not green.
CI: this PR's own run is the test (Gitea runs pull_request workflows from the PR head). approval-policy is expected to fail (.gitea/**, ci/** are sensitive). This needs an operator review/override — I have not posted one and will not merge.

Handoff: decisions/2026-11-08-gate-toolbox-container.md.

🤖 Generated with Claude Code

## What The `gate` job now runs **inside the CI-built toolbox image**, pinned by digest, instead of `catthehacker/ubuntu:act-22.04` + a per-run download of yamllint / check-jsonschema / kyverno / promtool / chainsaw (~11s of every run, measured on runs 494/495). Local `dev.sh` and CI now gate on the **identical** image. ```yaml container: # renovate-digest: image=gitea.home.keithjackson.dev/home-network/toolbox tag=main image: gitea.home.keithjackson.dev/home-network/toolbox@sha256:83a3501efe34fa0b039e3f0d360738686a0f59109866c49779768f5d5ed0ef81 ``` Unchanged: job name `gate` (required context `gate / gate (pull_request)`), the gate step, its env, and `|| [ "$?" -eq 3 ]`. ## Decisions - **Plain-git checkout instead of adding Node to the toolbox.** `actions/checkout` is a Node action and the toolbox has no Node. Adding Node would need two sequential PRs (Containerfile → publish → pin). The git step reproduces what checkout@v7 `fetch-depth: 0` ran here (all branches → `origin/*`, tags, event SHA; detached for PR, `-B <branch>` for push) and asserts `HEAD == github.sha`. The job token goes to that one fetch via `GIT_CONFIG_*` env, and is not persisted. - **No pull credentials.** Org `Home-Network` is public and the registry issues anonymous pull tokens (measured: anonymous `/v2/token` → manifest 200). - **Pins stay enforced.** The install step is replaced by a check that every `*_VERSION` in `ci/versions.env` equals the image's `/etc/dev-toolchain-versions` (`::error::DRIFT` otherwise; negative-tested). - **Renovate** follows the digest via the existing `# renovate-digest:` convention (`renovate.json5` now scans `gate.yml`; `github-actions` manager disabled for own images, same as `kubernetes`). `registry-retention.sh` reads the same marker. - `install-tools.sh` **kept**: `merge-gate.yaml` and `alert-rules-unit-tested` still reference it. Image: 466.9 MB compressed / 1.54 GB unpacked. The DinD store is an emptyDir, so the first run after a runner restart or digest bump pays one pull. **Consequence:** future toolbox digest bumps touch `.gitea/**` → sensitive path → need an operator override. ## Gate Local, in the pinned image itself: **rc 3**, `gitea-settings` the only stage not run. Not green. CI: this PR's own run is the test (Gitea runs `pull_request` workflows from the PR head). `approval-policy` is expected to fail (`.gitea/**`, `ci/**` are sensitive). **This needs an operator review/override — I have not posted one and will not merge.** Handoff: `decisions/2026-11-08-gate-toolbox-container.md`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
gitea-admin added 2 commits 2026-11-08 06:15:53 +00:00
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]>
docs(decisions): record local gate result for gate-toolbox-container
gate / gate (pull_request) Failing after 2m31s
gate / conformance (pull_request) Skipped
514ed5d5a4
Co-Authored-By: Claude Opus 5.5 <[email protected]>
gitea-admin added 1 commit 2026-11-08 06:20:39 +00:00
fix(ci): umask 0022 in the git checkout step — act's 0000 made Ansible ignore ansible.cfg
gate / gate (pull_request) Failing after 2m20s
gate / conformance (pull_request) Skipped
27d603f726
Run 500 (this PR's first CI run) failed L0 prod-baremetal-not-dev-surface: act runs steps with
umask 0000, the checkout was world-writable, and Ansible (now present, via the toolbox) refuses
a world-writable directory as an ansible.cfg source. Reproduced and fixed locally in the pinned
image.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
gitea-admin added 1 commit 2026-11-08 06:26:28 +00:00
docs(decisions): record CI runs 500/503 for gate-toolbox-container
gate / gate (pull_request) Successful in 2m20s
gate / conformance (pull_request) Skipped
64c5617ddd
Co-Authored-By: Claude Opus 5.5 <[email protected]>
jakman85 approved these changes 2026-11-08 06:46:05 +00:00
jakman85 merged commit 6e2e353bcc into main 2026-11-08 06:51:00 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Home-Network/home-network-k3s-impl#147