From b2f498cac5934f437763673332c69aa0c3376f3a Mon Sep 17 00:00:00 2001 From: didericis Date: Sun, 12 Jul 2026 16:44:18 -0400 Subject: [PATCH] ci(coverage): defer the KVM-runner diff-coverage gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the self-hosted KVM runner setup out of this branch so main's CI doesn't require a runner that isn't registered yet. The `coverage` job runs on ubuntu-latest again and reports combined coverage informationally — the hard diff-coverage gate (which needs the firecracker integration suite, hence /dev/kvm + the provisioned pool) is deferred and moves to a separate PR. The unit-coverage tests added alongside the gate stay. See PRD 0069 / #348; the runner + gate re-land on the ci-runner branch. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01WBMWTEtQdJ4W5UrWuLHCck --- .gitea/workflows/test.yml | 49 ++++++++++++++------------------------- README.md | 2 -- 2 files changed, 17 insertions(+), 34 deletions(-) diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 746c863..081b544 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -71,45 +71,30 @@ jobs: - name: Run integration tests run: python3 -m unittest discover -t . -s tests/integration -v - # Combined unit+integration coverage + the diff-coverage gate (the hard - # gate: new/changed lines >= 90%). See docs/decisions/0004-coverage-policy.md. + # Combined unit+integration coverage report (informational). See + # docs/decisions/0004-coverage-policy.md. # - # This runs on a self-hosted KVM runner (label `kvm`), NOT ubuntu-latest, - # because the Firecracker backend's subprocess/VM orchestration - # (launch/boot/SSH/isolation-probe) is covered by the integration suite, - # and that suite needs `/dev/kvm` + the provisioned TAP/nft pool — which a - # container-based runner doesn't have. On such a runner the firecracker - # integration test skips and its ~230 orchestration lines read as - # uncovered, so the gate can't pass there. - # - # Runner prerequisites (provision once on the host; see the README - # "Firecracker on Linux" section): the `firecracker` binary on PATH, - # `/dev/kvm` accessible to the runner user, Docker, the cached guest - # kernel + static dropbear (BOT_BOTTLE_FC_KERNEL / BOT_BOTTLE_FC_DROPBEAR), - # and the network pool installed as the persistent systemd unit - # (`./cli.py backend setup --backend=firecracker`). The preflight step - # below fails fast with instructions if anything is missing. + # The hard diff-coverage gate (changed lines >= 90%) is DEFERRED: the + # Firecracker backend's VM/SSH orchestration is covered by the integration + # suite, which needs /dev/kvm + the provisioned TAP/nft pool — a + # container-based runner skips it and those lines read uncovered, so the + # gate can't pass here. Re-enabling it on a self-hosted KVM runner is + # tracked separately (see PRD 0069 / #348 and the ci-runner branch). coverage: - runs-on: [self-hosted, kvm] + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Preflight — Firecracker host is ready - run: | - command -v firecracker >/dev/null || { - echo "firecracker not on PATH — provision the runner (README: Firecracker on Linux)"; exit 1; } - test -e /dev/kvm || { echo "/dev/kvm missing — KVM not available on this runner"; exit 1; } - # `backend status` exits non-zero unless the TAP pool is up + no - # range overlap; it prints the exact `backend setup` fix. - python3 cli.py backend status --backend=firecracker + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" - - name: Combined coverage (unit + integration, incl. firecracker) + - name: Install dev requirements + run: python3 -m pip install -r requirements-dev.txt + + - name: Combined coverage report (unit + integration) run: PYTHON=python3 bash scripts/coverage.sh critical - - - name: Diff-coverage gate (changed lines >= 90%) - run: | - git fetch --no-tags origin main:refs/remotes/origin/main - python3 scripts/diff_coverage.py --base origin/main --min 90 diff --git a/README.md b/README.md index 26c5a2e..c117e1a 100644 --- a/README.md +++ b/README.md @@ -90,8 +90,6 @@ BOT_BOTTLE_BACKEND=firecracker ./cli.py start > **NixOS:** enable `virtualisation.docker`, ensure the KVM module is loaded (`boot.kernelModules = [ "kvm-intel" ];` or `kvm-amd`), and add your user to the `kvm` and `docker` groups. For the network pool, consume the flake module — `imports = [ inputs.bot-bottle.nixosModules.firecracker-netpool ]; services.bot-bottle-firecracker = { enable = true; owner = "you"; };` — then `nixos-rebuild switch` (imperative nft/TAP rules don't survive a rebuild; channel users can `imports = [ /nix/firecracker-netpool.nix ]`). `firecracker` isn't in nixpkgs by default as a user binary — install the release binary (pin the version) and put it on `PATH`. -> **CI:** the coverage gate (`.gitea/workflows/test.yml` → `coverage` job) runs on a self-hosted runner labelled `kvm`, because the Firecracker backend's VM/SSH orchestration is exercised only by the integration suite, which needs `/dev/kvm` + the provisioned pool (a container runner would skip it and read as uncovered). Provision that runner exactly like a normal Firecracker host — `firecracker` on `PATH`, `/dev/kvm`, Docker, the cached guest kernel + static dropbear, and the pool installed as the persistent systemd unit — then register it with the `kvm` label. The unit/lint jobs still run on `ubuntu-latest`. - ```sh ./cli.py start # builds the image on first run, drops you into claude ```