5940b75bb7
test / integration-docker (pull_request) Successful in 12s
tracker-policy-pr / check-pr (pull_request) Successful in 8s
test / unit (pull_request) Successful in 33s
test / integration-firecracker (pull_request) Successful in 3m13s
test / coverage (pull_request) Failing after 1m45s
test / publish-infra (pull_request) Has been skipped
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
111 lines
4.2 KiB
Markdown
111 lines
4.2 KiB
Markdown
# 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:
|
|
|
|
1. **Duplicate artifact transfers.** `build-infra` (ubuntu-latest) built and
|
|
uploaded the ~194 MB rootfs; `integration-firecracker` downloaded it; the
|
|
`coverage` job downloaded it a second time. Combined download overhead: ~83
|
|
seconds per run, plus the ~70-second upload.
|
|
|
|
2. **Duplicate test execution.** `integration-firecracker` ran the Firecracker
|
|
integration suite; `coverage` ran 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 on
|
|
`publish-infra`).
|
|
|
|
## Non-goals
|
|
|
|
- Changing test semantics or the coverage policy (ADR 0004).
|
|
- Removing the KVM runner guard on `integration-firecracker` and `coverage`.
|
|
- Changing how `publish_infra.py` builds 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:
|
|
|
|
1. Builds the infra candidate locally with
|
|
`BOT_BOTTLE_FC_DROPBEAR=/var/cache/bot-bottle-fc/dropbear`.
|
|
2. Boots the candidate and runs integration tests with coverage, writing
|
|
`.coverage.firecracker`.
|
|
3. Uploads the small `coverage-firecracker` artifact unconditionally.
|
|
4. On main-branch pushes only, uploads the rootfs as `infra-candidate` and the
|
|
dropbear as `firecracker-inputs` so `publish-infra` can verify and publish
|
|
the byte-identical artifact.
|
|
|
|
### `coverage`
|
|
|
|
Moves from a KVM runner to `ubuntu-latest`. No tests are re-executed:
|
|
|
|
1. Downloads `coverage-unit`, `coverage-docker`, and `coverage-firecracker`.
|
|
2. Runs `scripts/coverage.sh aggregate critical`, which calls
|
|
`coverage combine` then `coverage report`.
|
|
3. 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
|
|
for `build-infra`. No longer needed.
|
|
- `build-infra`: the infra candidate is now built on the KVM runner in
|
|
`integration-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.
|