diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 081b544..746c863 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -71,30 +71,45 @@ jobs: - name: Run integration tests run: python3 -m unittest discover -t . -s tests/integration -v - # Combined unit+integration coverage report (informational). See - # docs/decisions/0004-coverage-policy.md. + # Combined unit+integration coverage + the diff-coverage gate (the hard + # gate: new/changed lines >= 90%). See docs/decisions/0004-coverage-policy.md. # - # 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). + # 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. coverage: - runs-on: ubuntu-latest + runs-on: [self-hosted, kvm] steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" + - 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: Install dev requirements - run: python3 -m pip install -r requirements-dev.txt - - - name: Combined coverage report (unit + integration) + - name: Combined coverage (unit + integration, incl. firecracker) 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 c117e1a..26c5a2e 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,8 @@ 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 ```