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.
76 lines
3.2 KiB
Markdown
76 lines
3.2 KiB
Markdown
# zegit-forgejo-runner
|
|
|
|
Custom toolchain image for the cluster's self-hosted **Gitea Actions (Forgejo)
|
|
runner**, which runs in *host mode* (Docker disabled) — workflow steps execute
|
|
inside this image, so it must carry node/git/make/etc. that the stock
|
|
`code.forgejo.org/forgejo/runner` image lacks.
|
|
|
|
This repo lives only on Gitea (`zegit-zoo/zegit-forgejo-runner`). The cluster-side
|
|
deployment and rationale live in `flux-config` (`runners.md`,
|
|
`applications/forgejo-runner/`).
|
|
|
|
## Build & push
|
|
|
|
The runner needs this image to run *any* job, so the **first** build must be done
|
|
by hand (CI can't bootstrap itself). The cluster is mixed amd64/arm64, so build
|
|
multi-arch:
|
|
|
|
```sh
|
|
docker buildx create --use --name zegit-ci 2>/dev/null || true
|
|
docker login gitea.langenoja.se # username + a Gitea personal access token
|
|
docker buildx build --platform linux/amd64,linux/arm64 \
|
|
-t gitea.langenoja.se/zegit-zoo/zegit-forgejo-runner:12.11.1-ci --push .
|
|
```
|
|
|
|
The Gitea package is **public**, so the `forgejo-runner-system` namespace pulls it
|
|
without an imagePullSecret.
|
|
|
|
## Wire it into the cluster
|
|
|
|
After the push succeeds, in `flux-config`
|
|
`applications/forgejo-runner/base/deployment-forgejo-runner.yaml` set the `image:`
|
|
on **both** the `register` initContainer and the `forgejo-runner` daemon to
|
|
`gitea.langenoja.se/zegit-zoo/zegit-forgejo-runner:12.11.1-ci`, then commit (Flux
|
|
reconciles). Do this only *after* the image exists, or the pod ImagePullBackOffs.
|
|
|
|
## Tag scheme
|
|
|
|
`<base-runner-version>-ci` (e.g. `12.11.1-ci`). Must satisfy the cluster's
|
|
Gatekeeper `enforce-semantic-image-tags` constraint — never `:latest`.
|
|
|
|
## Publishing a new image
|
|
|
|
./push-image.sh 12.11.1-ci4
|
|
|
|
The registry hostname in the image reference must stay `gitea.langenoja.se`,
|
|
because that is what the cluster pulls. The script therefore keeps the NAME and
|
|
changes only the ROUTE: it pins that hostname to the Istio gateway on the LAN
|
|
(`10.40.0.10`) for the duration of the push, then removes the pin.
|
|
|
|
That matters because `gitea.langenoja.se` resolves to Cloudflare, which caps
|
|
request bodies below a registry layer and returns **413 Payload Too Large**. The
|
|
gateway serves a `*.langenoja.se` wildcard cert, so TLS still validates normally
|
|
— no `insecure-registries`, no `--tls-verify=false`.
|
|
|
|
If a run is killed before it can clean up, the pin is left behind. It carries a
|
|
marker comment, so the next run recognises it as its own and removes it — or you
|
|
can clear it directly:
|
|
|
|
./push-image.sh --unpin
|
|
|
|
A pin you added yourself (no marker) is never touched, and the script refuses to
|
|
run if an unmarked pin points somewhere other than the gateway.
|
|
|
|
Requirements: a Gitea PAT with **`write:package`** (`read:package` is pull-only),
|
|
LAN reachability of the gateway, and qemu/binfmt registered for the arm64 build.
|
|
|
|
Multi-arch is not optional. `forgejo-runner` runs on arm64 nodes and
|
|
`zegit-runner` on amd64, neither deployment sets a `nodeSelector`, and both use
|
|
this single tag.
|
|
|
|
Two traps the script exists to prevent: pushing to the bare MagicDNS name
|
|
(`gitea-system-gitea-tailscale-http`) makes Docker treat it as a **Docker Hub
|
|
namespace**, since a registry host is only recognised when it contains a dot or
|
|
colon; and `docker push $_` pushes `.`, because `$_` is the last ARGUMENT of the
|
|
build command, not its tag.
|