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.
82 lines
4.0 KiB
Docker
82 lines
4.0 KiB
Docker
# Custom toolchain image for the self-hosted Gitea Actions (Forgejo) runner.
|
|
#
|
|
# The cluster runs act_runner in *host mode* (docker disabled), so workflow steps
|
|
# execute INSIDE this image. The stock forgejo/runner image is minimal and lacks
|
|
# node (needed by JS actions: actions/checkout, setup-go, ...), git, make, etc.
|
|
# The runner pod is non-root with all caps dropped, so it cannot apt/apk-add at
|
|
# runtime -- the tools must be baked in here.
|
|
#
|
|
# PUBLISHING IS AUTOMATED. Run the "build image" workflow on GitHub with a tag
|
|
# (e.g. 12.11.1-ci4); it builds both architectures and pushes to
|
|
# ghcr.io/zegit-zoo/zegit-forgejo-runner. ./push-image.sh remains for building
|
|
# by hand. README.md explains why the registry moved off Gitea (Cloudflare's
|
|
# request-body cap returns 413 on a registry layer) and why the build cannot run
|
|
# on our own runners (host mode, all capabilities dropped).
|
|
#
|
|
# Then in flux-config set the image on BOTH deployment-zegit-runner and
|
|
# deployment-forgejo-runner, digest-pinned.
|
|
#
|
|
# The image is NOT public. Pulls need a registry credential in
|
|
# forgejo-runner-system -- github-registry-credentials for ghcr.io. An earlier
|
|
# version of this comment claimed the package was public and needed no secret;
|
|
# that was wrong for the Gitea registry too, which is private.
|
|
FROM code.forgejo.org/forgejo/runner:12.11.1
|
|
|
|
USER root
|
|
|
|
# nodejs -> JS actions runtime (actions/checkout, actions/setup-go, ...)
|
|
# git -> checkout
|
|
# git-daemon -> provides git-http-backend, which Alpine splits OUT of the `git`
|
|
# package (git alone ships git-remote-http/https but NOT the CGI
|
|
# backend). Without it, internal/forgeprobe's smart-HTTP tests died
|
|
# with "fork/exec git-http-backend: no such file or directory" and
|
|
# reported three FAILURES for a missing tool — which turned Gitea CI
|
|
# red on every zegit master commit while GitHub stayed green, for
|
|
# hours, unnoticed.
|
|
# bash -> `shell: bash` steps and many action entrypoints
|
|
# coreutils-> GNU userland (alpine busybox tools trip up some scripts)
|
|
# curl,tar -> setup-go / gitleaks download + extract their own arch-specific binaries
|
|
# make -> zegit Makefile targets
|
|
# ca-certificates -> trusted roots for HTTPS to gitea/github/goproxy
|
|
# openssl -> the release tag gate verifies the Control Plane's SIGNED quorum
|
|
# verdict (Ed25519 DSSE) with `openssl pkeyutl -verify -rawin`.
|
|
# Absent, that command failed "command not found" and the gate's
|
|
# `|| { echo ...; exit 1; }` reported it as "Quorum verdict
|
|
# signature FAILED verification" -- a TOOLING gap narrated as a
|
|
# SECURITY verdict, pointing the investigation at a perfectly
|
|
# valid signed approval instead of at this image. It blocked
|
|
# v0.0.134 and v0.0.135 on 2026-08-31.
|
|
# jq -> the same story one step earlier: the gate parsed that envelope
|
|
# with `jq -e`, and a missing jq was reported as "not a signed
|
|
# DSSE envelope". jq is the reflex tool for JSON in shell steps,
|
|
# so its absence keeps resurfacing as a misleading failure in
|
|
# whichever step reaches for it next.
|
|
# gcc,musl-dev -> C toolchain for cgo; `go test -race` (the Makefile test
|
|
# target) requires cgo, which needs a C compiler + libc headers
|
|
# (musl on Alpine)
|
|
# python3,py3-yaml -> the repo's own gates are Python: credentials-coverage.sh,
|
|
# validate-admission.sh and feature-lint.sh all shell out to
|
|
# python3, and credentials-coverage parses manifests with yaml
|
|
#
|
|
# Go is intentionally NOT baked in: actions/setup-go fetches the pinned version
|
|
# per-run (node + tar are present), so the image needs no rebuild on Go bumps.
|
|
RUN apk add --no-cache \
|
|
nodejs \
|
|
git \
|
|
git-daemon \
|
|
bash \
|
|
coreutils \
|
|
curl \
|
|
tar \
|
|
make \
|
|
ca-certificates \
|
|
gcc \
|
|
musl-dev \
|
|
python3 \
|
|
py3-yaml \
|
|
openssl \
|
|
jq
|
|
|
|
# Match the runner pod's runAsUser (1000); keep the image non-root by default.
|
|
USER 1000
|