Each test job now runs once under coverage and uploads a small .coverage.* artifact. The coverage job combines them on ubuntu-latest — no test reruns, no KVM dependency. The infra candidate is built directly on the KVM runner, eliminating the build-infra job and the ~70 s upload + ~83 s combined download. For PRs, no rootfs artifact is transferred at all. Main-branch pushes upload the tested rootfs and matching dropbear so publish-infra publishes the byte-identical artifact. relative_files = True in .coveragerc lets coverage files from different runners combine without path remapping. Closes #446
4.2 KiB
PRD prd-new: CI artifact-based coverage and local Firecracker candidate flow
- Status: Active
- Author: Claude
- Created: 2026-07-21
- Issue: #446
Summary
Restructure the CI test pipeline to run each test suite exactly once, upload
small .coverage.* artifacts, and combine them in a lightweight aggregation
job. Move the infra build onto the KVM runner so the ~194 MB rootfs never
crosses the network for PRs. On main-branch pushes, publish the byte-identical
rootfs that was tested.
Motivation
The prior pipeline had two redundant costs:
-
Duplicate artifact transfers.
build-infra(ubuntu-latest) built and uploaded the ~194 MB rootfs;integration-firecrackerdownloaded it; thecoveragejob downloaded it a second time. Combined download overhead: ~83 seconds per run, plus the ~70-second upload. -
Duplicate test execution.
integration-firecrackerran the Firecracker integration suite;coverageran the entire unit + integration suite again on the same KVM runner to collect coverage data. Every line of Firecracker code was tested twice per CI run.
Goals
- Each test suite (unit, integration-docker, integration-firecracker) executes exactly once per workflow run.
- PRs incur no large artifact transfers — the rootfs stays on the KVM runner.
- Main-branch pushes publish a byte-for-byte identical rootfs to the one that passed the integration tests.
- Concurrent workflow runs cannot cross-publish candidates (naturally enforced by Gitea Actions' per-run artifact scoping).
- Failed or cancelled runs block publication (enforced by the
needs:chain onpublish-infra).
Non-goals
- Changing test semantics or the coverage policy (ADR 0004).
- Removing the KVM runner guard on
integration-firecrackerandcoverage. - Changing how
publish_infra.pybuilds or uploads the rootfs.
Design
Job graph
unit ──────────────────────────────────┐
integration-docker ────────────────────┤──► coverage ──► publish-infra (main only)
integration-firecracker (KVM) ─────────┘
unit
Unchanged except: coverage run writes --data-file=.coverage.unit; the file
is uploaded as the coverage-unit artifact.
integration-docker
Adds a coverage install step. coverage run writes --data-file=.coverage.docker;
the file is uploaded as coverage-docker.
integration-firecracker (KVM runner)
Replaces the old stage-firecracker-inputs → build-infra → download chain:
- Builds the infra candidate locally with
BOT_BOTTLE_FC_DROPBEAR=/var/cache/bot-bottle-fc/dropbear. - Boots the candidate and runs integration tests with coverage, writing
.coverage.firecracker. - Uploads the small
coverage-firecrackerartifact unconditionally. - On main-branch pushes only, uploads the rootfs as
infra-candidateand the dropbear asfirecracker-inputssopublish-infracan verify and publish the byte-identical artifact.
coverage
Moves from a KVM runner to ubuntu-latest. No tests are re-executed:
- Downloads
coverage-unit,coverage-docker, andcoverage-firecracker. - Runs
scripts/coverage.sh aggregate critical, which callscoverage combinethencoverage report. - Runs the diff-coverage gate (
scripts/diff_coverage.py).
Coverage files use relative_files = True (.coveragerc) so they combine
cleanly across runners with different absolute workspace paths.
publish-infra
Depends on all four predecessor jobs (unchanged gate). Downloads infra-candidate
and firecracker-inputs that were uploaded by integration-firecracker on
main — the same byte sequence that passed the integration tests.
Eliminated jobs
stage-firecracker-inputs: existed only to copy the dropbear to ubuntu-latest forbuild-infra. No longer needed.build-infra: the infra candidate is now built on the KVM runner inintegration-firecracker.
Script changes
scripts/coverage.sh gains an aggregate mode (coverage.sh aggregate [critical])
that combines pre-existing .coverage.* files instead of re-running tests.
The existing run mode (coverage.sh [critical]) is preserved for local dev.