- Python 62%
- Shell 38%
Spawns transient VMs per CI job with per-repo CI-user and ephemeral write:package token. Components: orchestrator (Python), base-image builder (Bash), Debian Trixie + UEFI runner VM. See ARCHITECTURE.md. Signed-off-by: Your Name <you@example.com> |
||
|---|---|---|
| .gitignore | ||
| ARCHITECTURE.md | ||
| build-runner-base.sh | ||
| README.md | ||
| runner_controller.py | ||
| smoke-test.sh | ||
forgejo-kvm-runner
Ephemeral Forgejo runner on KVM. Fresh VM per CI job. Per-repo CI user with an ephemeral write:package token for the Forgejo registry. Architecture: ARCHITECTURE.md.
Prerequisites
Host packages (Arch):
sudo pacman -S libvirt qemu-base virt-install cloud-image-utils libvirt-python python-requests edk2-ovmf
sudo systemctl enable --now libvirtd
sudo virsh net-autostart default && sudo virsh net-start default
Forgejo admin token with scopes: sudo, read:user, write:user, write:admin_users, read:repository, write:repository, read:package, write:package, plus the admin scope covering the Actions discovery endpoint (else: 403 on discovery poll).
Run-through
1. Build the base image
sudo ./build-runner-base.sh
~7 min: Debian download → virt-customize → smoke-boot → atomic publish to /var/lib/libvirt/images/runner-base.qcow2.
2. Target repo workflow
runs-on: external-host- No
docker loginstep — registry token is injected via cloud-init - Image tag:
git.jonathan-schmidt.de/<owner>-<repo>-ci/<image>:<sha>
3. Start the controller
read -s FORGEJO_ADMIN_TOKEN && export FORGEJO_ADMIN_TOKEN
export FORGEJO_URL=https://git.jonathan-schmidt.de
sudo -E python runner_controller.py
Expect: startup sweep... → entering discovery loop (label=external-host, interval=15s, max_vms=3).
4. Trigger a job
Push a commit. Controller log:
queue: dispatching job <id> (handle=...)
dispatch job <id> for <owner>/<repo> (vm=fr-<short>)
creating CI user <owner>-<repo>-ci for ... # first job per repo only
VM fr-<short> started for job <id>
VM fr-<short> finished (job <id>)
VM boot ~30 s, job duration depends on the build.
5. Stop
Ctrl+C. Controller waits for in-flight jobs, then exits. startup_sweep revokes leftover orch-* tokens on the next start.
Debugging
virsh list # is a VM running?
virsh console fr-<short> # live cloud-init / runner output (Ctrl+] to detach)
journalctl -u libvirtd -n 50
CI-user state: sudo cat /var/lib/forgejo-runner-controller/ci-users.json.