The dnf transaction is essentially the whole cost of a build — emulated rpm scriptlets for 502 packages on minimal, 1929 on workstation. Everything after it is minutes. Getting this laptop to boot will take several attempts at the kernel command line and the dracut driver list, and paying for a reinstall each time is not tenable. Stage the post-dnf tree under <work>/base, keyed on a hash of the package lists, release and variant, and copy it per build with --reflink=auto (a CoW clone on btrfs). Config and overlay edits now reuse it; package list edits invalidate it on their own, so --fresh is only needed to force the issue. Keep downloaded rpms in a cachedir outside the install root, so even --fresh re-runs the scriptlets without re-downloading. keepcache=0 was exactly the wrong setting for a build meant to be run repeatedly. Add --work so CI can put both outside the job workspace, which is wiped between runs, and default the build container to the gongfoo aarch64 build base so the assembly tooling is not installed under emulation every time. That image is a speedup, not a dependency: fall back to stock Fedora when it is unreachable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XWRjNJMistCy6ngXH5aJLS
77 lines
3.1 KiB
Markdown
77 lines
3.1 KiB
Markdown
# Gitea runner setup
|
||
|
||
The build runs on runners labelled `metal`, executing directly on the host
|
||
rather than in a runner-provided container, because it needs to drive podman
|
||
itself.
|
||
|
||
## One-time host preparation
|
||
|
||
On each runner host:
|
||
|
||
```sh
|
||
sudo dnf install -y podman qemu-user-static-aarch64
|
||
sudo systemctl restart systemd-binfmt
|
||
```
|
||
|
||
Verify:
|
||
|
||
```sh
|
||
cat /proc/sys/fs/binfmt_misc/qemu-aarch64
|
||
```
|
||
|
||
The `flags:` line must contain `F`. That flag makes the kernel open the
|
||
interpreter at registration time and keep the reference, so `qemu-aarch64-static`
|
||
resolves even inside a container that does not have it. Without it, every
|
||
aarch64 binary inside the build container fails with `exec format error`.
|
||
`qemu-user-static-aarch64` registers it correctly; plain `qemu-user` does not.
|
||
|
||
The workflow attempts this install itself, but that only works if the runner
|
||
account has passwordless sudo. Run `probe-runner.yaml` to find out.
|
||
|
||
## Checking what a runner can do
|
||
|
||
The `probe runner` workflow is manual-dispatch only and reports architecture,
|
||
user, sudo, podman, emulation status and free disk. Run it once per runner
|
||
before debugging a build failure.
|
||
|
||
## Disk space
|
||
|
||
The build keeps persistent state in `/var/tmp/c630-build` on each runner:
|
||
|
||
| Path | Contents | Rough size |
|
||
|---|---|---|
|
||
| `/var/tmp/c630-build/work/<variant>/base` | Staged post-dnf root filesystem | 2 GiB minimal, 8 GiB workstation |
|
||
| `/var/tmp/c630-build/dnf` | Downloaded rpms and repo metadata | 2 GiB minimal, 6 GiB workstation |
|
||
|
||
Plus, transiently in the job workspace, the working rootfs copy, three
|
||
filesystem images and the final disk image. Budget roughly 20 GiB for
|
||
`minimal` and 45 GiB for `workstation` per runner.
|
||
|
||
The workflow prunes cached rpms untouched for 30 days. The staged base is not
|
||
pruned — it is invalidated by content hash, not age, so a stale one is
|
||
harmless. Delete `/var/tmp/c630-build` to reclaim the space.
|
||
|
||
## Runtime
|
||
|
||
Every aarch64 binary runs under qemu-user emulation, and rpm scriptlets are the
|
||
worst case. A cold build is roughly 45–90 minutes for `minimal` and several
|
||
hours for `workstation`; `timeout-minutes` is set to 600 accordingly.
|
||
|
||
A warm build — one where the package set has not changed since that runner last
|
||
built — skips the dnf transaction entirely and finishes in minutes. Because the
|
||
state is per-runner and jobs are scheduled across all nine, expect the first
|
||
build on each runner to be cold. Pinning this workflow to a single runner with
|
||
a dedicated label would make warm builds the norm at the cost of parallelism.
|
||
|
||
If this becomes tiresome, the fix is a native aarch64 runner. Register one with
|
||
an `aarch64` label and change `runs-on: metal` to `runs-on: aarch64` in
|
||
`.gitea/workflows/build-image.yaml`; `build/build-image.sh` already skips the
|
||
emulation check when the host is already the target architecture.
|
||
|
||
## Changing which runners are used
|
||
|
||
All nine current runners carry `metal` and `podman`. `runs-on: metal` therefore
|
||
matches any of them. To pin the build to a subset, give those runners a distinct
|
||
label and use it — Gitea requires a runner to carry every label listed in
|
||
`runs-on`.
|