Commit Graph
13 Commits
Author SHA1 Message Date
agent bc1d6890b6 fix: bake openssl and jq into the runner image, and assert them
The release tag gate parses the Control Plane quorum verdict and verifies its
Ed25519 DSSE signature by shelling out to jq and openssl. Neither is in this
image, and both call sites read cmd ... || { echo <security message>; exit 1; },
so command-not-found took the failure branch: the gate reported a valid, signed
approval first as "not a signed DSSE envelope" and then as "signature FAILED
verification". That blocked v0.0.134 and v0.0.135 on 2026-08-31 and pointed the
investigation at the Tag Request rather than at this image — the same shape as
the git-daemon gap already documented above the package list.

Also adds both to the toolchain assertion. That check already existed and still
missed them, because it only covers what someone thought to list; the comment
now says so, and says to extend it whenever a gate grows a shell dependency.

Verified by building this image and running the gate script inside it against
the live Control Plane: openssl 3.5.8, jq 1.8.1, and the v0.0.135 verdict
verifies and binds to its commit. The unpatched image fails the same script.
2026-08-31 12:56:44 +02:00
markus bf26dc4e15 Merge pull request 'ci: assert the published image still contains the toolchain the gates need' (#5) from claude/ci/assert-image-contents into master
build image / build (push) Skipped
Reviewed-on: #5
2026-08-20 14:22:27 +00:00
markus d0b0bbf239 Merge pull request 'fix: restore gcc, musl-dev, python3 and py3-yaml to the image' (#4) from claude/fix/restore-toolchain into master
build image / build (push) Skipped
Reviewed-on: #4
2026-08-20 14:03:55 +00:00
agent afc1701f84 ci: assert the published image still contains the toolchain the gates need
This image is a TOOLCHAIN, so "it built" is not the property worth checking —
"it still contains what the repo's gates shell out to" is.

It exists because the hand-built image had drifted from this Dockerfile: the
deployed ci3 carried gcc, musl-dev, python3 and py3-yaml, the Dockerfile did
not, and rebuilding from the Dockerfile silently dropped them. Gitea CI then
failed at "credentials-coverage: need python3" without reaching the tests at
all. Nothing in the pipeline objected, because every check asked whether the
image built, not what was in it.

Asserts python3 with a working yaml import, gcc, node, bash, make, curl, tar
and git-http-backend, on BOTH architectures — a package that resolves on amd64
but not arm64 would otherwise reach half the nodes unnoticed.

Verified the check can actually FAIL, which is the only property that makes it
worth having: against the bare forgejo/runner:12.11.1 it reports python3+yaml,
gcc, node and git-http-backend all MISSING; with the packages installed it
reports 3.12.14 and ok for the rest.
2026-08-20 16:03:11 +02:00
agent 5427ccbad2 fix: restore gcc, musl-dev, python3 and py3-yaml to the image
REGRESSION, mine. The committed Dockerfile had drifted from the image actually
in service: the running ci3 image contained these four packages, the Dockerfile
did not, and I rebuilt ci4 from the Dockerfile. The rollout therefore removed
tools zegit's CI depends on.

Proof the deployed ci3 had them: its Gitea runs got PAST `make credentials-check`
— which shells out to python3 — and failed later at cover-check. The ci4 run
fails earlier, at `credentials-coverage: need python3`.

What each is for, so the next rebuild does not drop them again:
  python3, py3-yaml  the repo's own gates are Python — credentials-coverage.sh,
                     validate-admission.sh and feature-lint.sh all shell out to
                     it, and credentials-coverage parses manifests with yaml
  gcc, musl-dev      cgo, which `go test -race` in the Makefile test target
                     requires: a C compiler plus libc headers (musl on Alpine)

Verified on the real base image rather than assumed — on
code.forgejo.org/forgejo/runner:12.11.1, `apk add git git-daemon gcc musl-dev
python3 py3-yaml` yields Python 3.12.14, a working `import yaml`, gcc 15.2.0,
and git-http-backend on the git exec path.

The deeper lesson is that a hand-built image can drift from its Dockerfile
silently, and nothing notices until someone rebuilds. Publishing via CI (#2)
is what stops that recurring, since every future image comes from the
committed file.
2026-08-20 15:53:23 +02:00
markus 137dde8122 Merge pull request 'docs: correct the registry notes in the Dockerfile header' (#3) from claude/docs/correct-registry-notes into master
build image / build (push) Skipped
Reviewed-on: #3
2026-08-20 13:48:05 +00:00
agent 6b6743113f docs: correct the registry notes in the Dockerfile header
Two things were wrong, and the second actively misled.

The publish instructions described pushing to the Gitea registry by hand.
Publishing now runs on GitHub Actions to ghcr.io, because Cloudflare's
request-body cap returns 413 on a registry layer and our own runners cannot
build images (host mode, all capabilities dropped). The example tag had also
been mangled to "12.11.1-ci".

"Package visibility is PUBLIC, so the runner namespace pulls without a secret"
was false. The Gitea package is private — flux-config's kustomization says so
in as many words — and the runner Deployments have always carried a pull
secret. Reading that line led me to conclude the ghcr package would need no
credential; it does, and forgejo-runner-system now holds
github-registry-credentials for exactly that.

Verified with `docker build --check`: no warnings.
2026-08-20 15:42:26 +02:00
markus f2b9661be2 Merge pull request 'ci: build and publish the image on GitHub to ghcr.io' (#2) from claude/ci/build-on-github into master
build image / build (push) Skipped
Reviewed-on: #2
2026-08-20 12:46:03 +00:00
agent f0922508a8 ci: build and publish the image on GitHub to ghcr.io
Publishing this image by hand kept hitting the same four walls — Cloudflare's
413, Docker parsing a bare MagicDNS name as a Docker Hub namespace, a PAT
missing write:package, and multi-arch. push-image.sh made that survivable;
this removes the manual step entirely.

It builds on GitHub rather than our own Gitea Actions because the runners
cannot build images: their labels are `ubuntu-latest:host` (host mode, no job
containers) and the container drops ALL capabilities with
allowPrivilegeEscalation:false, while rootless buildah needs newuidmap/user
namespaces for RUN steps. Building there would mean weakening the posture the
hardening established.

It publishes to ghcr.io rather than the Gitea registry because Cloudflare's
body cap is what caused the 413 in the first place, and because the cluster
already pulls zegit's own images from ghcr.io through an existing
github-registry-credentials pull secret — an established path rather than a
new one.

A master push validates that both arches still build but publishes nothing;
only an explicit dispatch names a tag and pushes, so a branch push cannot
silently mint a version. The final step reads the published index back and
fails if either architecture is absent, since a single-arch image under this
tag would CrashLoop on half the nodes.

Action SHAs match the pins zegit already uses, rather than introducing a
second set of versions to track.
2026-08-20 14:44:59 +02:00
markus 8c356b7e95 Merge pull request 'fix: add git-daemon so git-http-backend exists, plus a publish script' (#1) from claude/fix/git-http-backend into master
Reviewed-on: #1
2026-08-20 12:25:29 +00:00
agent b2d4e02c6f feat: add push-image.sh for publishing the runner image
Publishing this image by hand hit four obstacles in a row, each of which
recurs on every rebuild:

  413 Payload Too Large      gitea.langenoja.se is behind Cloudflare, which
                             caps request bodies below a registry layer
  Docker Hub auth refusal    the bare MagicDNS name was parsed as a Docker Hub
                             NAMESPACE — a registry host is only recognised
                             when it contains a dot or a colon
  insufficient_scope         the PAT needs write:package, not read:package
  wrong-arch pulls           forgejo-runner runs on arm64 and zegit-runner on
                             amd64, neither pins a nodeSelector, and both use
                             this one tag

The image reference must keep the hostname gitea.langenoja.se, because that is
what the cluster pulls. So the script keeps the NAME and changes only the
ROUTE: it pins that hostname to the Istio gateway on the LAN for the duration
of the push. The gateway serves a *.langenoja.se wildcard cert, so TLS
validates normally — no insecure-registries needed.

The pin carries a marker comment, which makes three cases distinguishable: a
leftover from a run that died before its trap fired (reuse, then remove), a
pin the operator added deliberately (never touch), and one pointing somewhere
other than the gateway (refuse to run). Without the marker a leftover pin was
indistinguishable from a deliberate one, so it survived every later run and
had to be reverted by hand. `--unpin` clears a leftover without building, and
the trap covers INT and TERM as well as EXIT.

It also verifies the published manifest really lists two architectures rather
than trusting the push's exit code.
2026-08-20 14:22:56 +02:00
agent afed70586a fix: add git-daemon so git-http-backend exists in the image
Alpine splits git-http-backend OUT of the `git` package: git alone ships
git-remote-http/https but not the CGI backend. The runner therefore had git
2.52.0 and no git-http-backend, so zegit's internal/forgeprobe smart-HTTP tests
failed at request time with "fork/exec git-http-backend: no such file or
directory" and reported three FAILURES for a missing tool.

That turned Gitea CI red on every zegit master commit while GitHub stayed
green, for hours, with nobody looking — GitHub is the release oracle, which is
exactly why a red mirror is invisible.

Verified against the real base image, not just alpine:3 — on
code.forgejo.org/forgejo/runner:12.11.1 (Alpine 3.23), `apk add git git-daemon`
puts git-http-backend at /usr/libexec/git-core/git-http-backend, and
`git --exec-path` resolves to that directory, which is the lookup the test
harness performs.
2026-08-20 13:37:22 +02:00
markusandClaude Opus 4.8 9886437b2f feat: custom toolchain image for host-mode Gitea Actions runner
Adds the Dockerfile (FROM code.forgejo.org/forgejo/runner:12.11.1, plus
node/git/bash/coreutils/curl/tar/make/ca-certificates) and a README with
the multi-arch build/push workflow. Built and pushed as
gitea.langenoja.se/zegit-zoo/zegit-forgejo-runner:12.11.1-ci; consumed by
flux-config applications/forgejo-runner.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-16 11:54:04 +02:00