Self-hosted CI
LeapView runs trusted continuous-integration workloads on a dedicated GitHub Actions runner
in the runner group leapview-ci. GitHub owns event delivery, queueing, logs, permissions,
and job cancellation. The repository owns the toolchain image, cache mounts, commands, and
test tiers that define a valid change.
The current runner is a single Linux x64 Hetzner CX53 in EU Central. Repository access is
restricted at the runner-group boundary, and labels additionally require leapview and
cx53. A single runner intentionally serializes jobs while each LeapView test contract uses
bounded internal parallelism.
Trust boundary
Trusted internal pull requests, merge groups, scheduled jobs, and main-branch artifact jobs
may execute on the persistent runner. External and Dependabot pull requests continue to run
task ci:pr on an ephemeral GitHub-hosted runner. Untrusted code therefore never receives
access to the host Docker socket, registry credentials, caches, or persistent workspace.
The trusted checkout uses clean: false. Git still checks out the requested commit, while
the persistent workspace retains ignored generated state and dependencies between jobs. This
is responsible for fast unchanged-run feedback. Repository generation checks remain the
correctness boundary: a changed input must regenerate its outputs, and generated:check
rejects stale checked-in artifacts.
Execution contract
Four Taskfile targets define the validation tiers:
task ci
task ci:pr
task ci:full
task ci:nightly
task ci is the local alias for the fast pull-request contract. task ci:pr generates and
builds shared inputs, runs the Go and frontend lanes, and checks generated artifacts.
task ci:full adds desktop tests, static and selected race analysis, route QA, and deployment
validation. task ci:nightly adds dependency and vulnerability scans. task ci:local
remains a compatibility alias for the full current-machine contract.
GitHub Actions invokes the same targets through scripts/run_ci_container.sh. Pull requests
run task ci:pr, the exact merge-queue candidate runs task ci:full, and the daily schedule
runs task ci:nightly. There is no second CI task language.
Toolchain and caches
Dockerfile.ci pins Go, Bun, Node, Terraform, Docker, Task, Buf, and Playwright. The runner
harness hashes this file and builds a content-addressed local image when that exact toolchain
is not already present. A failed toolchain change cannot overwrite the image used by another
commit.
The harness mounts stable Docker volumes for the Go build cache, the complete Go workspace, the Bun download cache, and the Terraform provider cache. It mounts the checked-out workspace at its unchanged host path so nested Docker commands can safely bind files from that checkout. It also mounts the Docker socket, configures the checkout as a safe Git directory, forwards the command without shell evaluation, and repairs checkout ownership on every exit path.
Workflow tiers
The pull-request workflow selects exactly one trust path. Internal branches use the self-hosted runner; forks and Dependabot use GitHub-hosted infrastructure. A GitHub-hosted gate requires whichever path applies.
The merge queue runs task ci:full against the exact merge_group commit and reports the
required CI gate context. Nightly CI runs the exhaustive tier. Post-merge artifact CI builds
and pushes the production image with Docker Buildx, qualifies the immutable digest through
the same runner harness, and records that digest in the job summary.
Operations
The GitHub runner service must be online and idle before a job can start. Monitor runner availability, queue time, disk usage, Docker volumes, and build cache growth. The host should retain enough free disk for parallel image generations; prune only unreferenced Docker data, never the named LeapView cache volumes during active work.
Because the runner is long-lived, do not route fork or Dependabot code to it. If trust routing is uncertain, use a GitHub-hosted runner. If the host is unavailable, manually dispatching the workflow will remain queued rather than silently falling back to a different execution environment.