GitHub-hosted CI

LeapView runs continuous integration on standard, ephemeral GitHub-hosted runners. GitHub owns runner provisioning, isolation, queueing, logs, permissions, cancellation, and cleanup. The repository owns the pinned toolchain setup, bounded remote caches, Taskfile commands, and validation tiers that define a valid change.

Every job starts from a clean Ubuntu image and loses its local filesystem when it completes. Correctness therefore cannot depend on an old checkout, Docker daemon, image, volume, or tool cache. This prevents accumulated runner state from making a later run fail or pass.

Trust boundary

Internal, external, and Dependabot pull requests use the same GitHub-hosted execution path. The pull-request workflow grants only contents: read; it does not expose deployment or registry credentials. Fork code never executes on a Flid-managed host.

GitHub Actions caches contain only reproducible dependency downloads, compiler outputs, browser binaries, Terraform providers, and BuildKit layers. They never contain credentials, runtime databases, Docker state, or qualification evidence. Cache restoration is a performance optimization: deleting every cache must still produce the same result.

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 prepares shared inputs, runs the bounded 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 distributes those same Taskfile units across clean runners. Pull requests run APIGen, the non-application Go packages, sharded application tests, and frontend validation concurrently. The three repository lanes execute task ci:prepare; the independent APIGen module does not need generated or embedded application assets and skips that preparation. The merge queue adds task ci:full:extras, and the daily schedule also runs task ci:nightly:extras. Local composition remains available through the tier targets; the workflow does not duplicate individual test commands or introduce a runner-specific container wrapper.

Toolchain and caches

.github/actions/setup-ci installs pinned Go, Node.js, Bun, Task, and Buf versions. Jobs opt into the pinned Terraform and Playwright installations only when their validation requires them. The action is shared by pull-request, merge, nightly, and production qualification jobs so a toolchain change has one reviewable source.

The setup action uses separate GitHub Actions cache entries for:

  • Go modules and compiler outputs, keyed by the Go dependency graph;
  • Bun downloads, keyed by the root and desktop lockfiles;
  • the pinned Playwright Chromium build;
  • Terraform providers, keyed by the deployment lockfiles.

Production and public-site image builds export BuildKit layers to independently scoped GitHub Actions caches. LeapView does not cache node_modules, /var/lib/docker, mutable worktrees, or application data. Exact keys are immutable, restore prefixes may seed a new dependency set, and the package manager or build tool always validates restored content. The main artifact workflow populates the default-branch Bun download cache so new pull requests do not inherit an empty cache entry from image qualification.

The repository currently works within GitHub's default 10 GB cache allowance. The intended operating limit is 50 GB or more so the independent Go, Bun, browser, Terraform, and BuildKit caches do not evict one another. Hitting the lower limit may reduce cache hits but cannot change validation behavior.

Workflow tiers

The pull-request workflow runs APIGen, Go package, Go application, and frontend validation on independent four-vCPU runners and reports the stable required CI gate check. This prevents browser and Go test contention and shortens wall-clock feedback without increasing per-job machine size.

For a native GitHub pull-request stack, only the top pull request runs those validation lanes. Lower layers report a successful CI gate with a summary that validation is deferred to the stack tip. The workflow listens for the stacked action, and its concurrency key uses the native stack ID, so rebasing a stack cancels obsolete feedback for the whole stack instead of filling the runner queue. Standalone pull requests and manual dispatches continue to run both lanes.

The main-branch ruleset must require GitHub's merge queue. A deferred lower-layer gate is feedback, not authorization to merge directly. The queue validates the exact candidate selected for main, whether that candidate is the complete stack or a contiguous prefix, by running the Go and frontend lanes plus the full extras. Merge-validation concurrency is scoped to the candidate ref: a rebuilt candidate cancels its obsolete run without making distinct queue candidates cancel one another.

Nightly CI also runs security scans in parallel. Post-merge artifact CI builds and pushes the production image using a BuildKit cache, then qualifies its immutable digest on a second clean runner.

Splitting production build and qualification prevents build layers from consuming the local disk needed by the qualification journey. The digest passed between jobs is the only product identity boundary; qualification never falls back to a mutable tag or source build.

Operations

There is no persistent CI VM to patch, prune, or recover. Monitor queue time, cache hit rate, cache eviction, job duration, and GitHub service health. A cache outage or eviction should make a job slower, not fail it. If a clean GitHub runner lacks enough per-job CPU, memory, or disk, split independent work into jobs before introducing persistent infrastructure.