Repository navigation
ci: build the Linux OpenVMM VMM tests once for the KVM and MSHV jobs - #708
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Windows manifest paths can escape the content directory; cross-runner execution also still needs end-to-end CI validation.
1 open issue
What changed in this PR
Builds Linux OpenVMM VMM tests once for KVM and MSHV, reducing duplicate CI work while retaining the combined WHP job.
Changes:
- Adds build-only and prebuilt-content CLI commands.
- Splits Linux CI into a producer and backend consumers, updating required and publishing gates.
- Adds handoff validation tests and documentation.
| File | Description |
|---|---|
| scripts/test_nvx_tools.py | Tests CLI, manifests, handoffs, and workflow gates. |
| scripts/nvx.py | Exposes build-only and content-consumer options. |
| scripts/nvx_tools/ci.py | Implements content production, validation, and execution. |
| doc/usage.md | Documents new command options. |
| doc/ci.md | Explains job dependencies and artifact handoffs. |
| .github/workflows/ci.yml | Shares Linux test builds and updates gating. |
Review effort: Balanced
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/build-vmm-tests-once
branch
from
October 21, 2026 15:27
3673a50 to
9432843
Compare
This was referenced Oct 21, 2026
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/build-vmm-tests-once
branch
from
October 21, 2026 23:05
9432843 to
2f85af8
Compare
test-openvmm built OpenVMM's VMM tests and ran them in one command, so every CI job that ran a backend's tests also built them: the KVM and MSHV jobs built the same Linux tests, and each job waited for the guest artifacts before it started its build, although only the tests boot the NVX kernel and initramfs. build-openvmm-tests builds the tests of one or more backends of the host's operating system into a test content directory without running them. It runs OpenVMM's vmm-tests-run in build-only mode on the union of the backends' filters, which writes the nextest archive, OpenVMM, the guest agents, the test firmware and kernels, and the flowey_hvlite that runs vmm-tests-run-target. It then reads that runner and its options from the run script that the build writes, and records them in nvx-openvmm-tests.json with the OpenVMM revision and whether its checkout was clean, the Rust release, each backend's filter, and every file's size and executable bit, so the content names what it holds and the inputs that built it. The run script is parsed strictly: it must run vmm-tests-run-target on the built filter in its own directory, with a runner inside it and only the options that the pinned flowey writes, so that a change of its format fails the build instead of running other tests. test-openvmm --content runs one backend's filter from such a directory with vmm-tests-run-target, which needs neither the OpenVMM checkout nor a build, only the guest artifacts: the microVM TTRPC test reads the NVX kernel and initramfs from its environment when it runs. It refuses content built from another OpenVMM revision than the gitlink or for another filter than the backend's, and content that lacks a file or holds a truncated one, and it restores the executable bits that an artifact download drops. Without --content, test-openvmm builds and runs the tests as before. Like it, the build links with /Brepro on Windows. doc/usage.md describes both commands, including that the pinned vmm-tests-run-target checks for Hyper-V on Windows whatever tests it runs. Tests cover the union filter, both run script formats and every rejection of a script, the build's commands, environment, and manifest, the refusal of another OS's backends and of used directories, the consumer's command, environment, and restored executable bits, and the refusal of foreign, malformed, and incomplete content. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Each Linux leg of openvmm-vmm-tests built the same x86_64-unknown-linux-gnu tests before it ran its backend's filter, and every leg waited for the guest artifacts before it started its build. In reference run 39585909588, the KVM and MSHV legs each spent about five minutes on that build, and their logs resolve the same artifacts and lay out the same test content. build-openvmm-vmm-tests-linux now builds the KVM and MSHV tests once, with build-openvmm-tests, on a general KVM runner, whose sccache holds that build, and hands the content to the two openvmm-vmm-tests-linux legs as the one-day openvmm-vmm-tests-linux artifact. The build boots no guest, so it starts with the OpenVMM binary producers instead of waiting for the guest artifacts, and it takes over the vmm-tests input and Cargo caches. Each leg downloads the content, the guest kernel, and the Alpine initramfs and runs its backend's own filter with test-openvmm --content, without checking out or building OpenVMM, and it uploads its diagnostics under the same openvmm-vmm-tests-<os>-<backend>-<run-id>-<run-attempt> name, now from the content's test_results. The upload keeps the hidden .config/nextest.toml and replaces an earlier attempt's content, so a rerun of the build replaces it and a rerun of a failed leg reuses it. The WHP tests stay in openvmm-vmm-tests-windows, which still builds and runs them in one job: on Windows, the pinned vmm-tests-run-target requires Hyper-V whatever tests it runs, through Get-VM and two registry values or administrator rights, and the WHP runners run jobs as Network Service. A Windows build job can follow once vmm-tests-run-target checks only what the tests of its build need. The build runs exactly when its test jobs do: whenever the change selects the VMM tests, since the classifier then also selects the guest artifacts that they boot, or when the change detector fails. It therefore reads run-openvmm-vmm-tests alone. check-required-ci expects it with them, and the development release and performance persistence, which accepted any skipped VMM-test job, now require the three jobs to succeed, or all three to be skipped because openvmm-changes selected no VMM tests, so that a failed or unexpectedly skipped build or test blocks them. Fork, draft, and path-filter gating, cancellation, and the cache saves stay as they were. Tests cover the build and test jobs, their runners, needs, and conditions, the change detector's outputs that they and the two gates read, the guest artifact components that each downloads, reads, and waits for, the artifact's name, path, hidden files, overwrite, and retention, the order of the downloads and the tests, the diagnostics of both operating systems, the required-check policy, the release and persistence gate, the cache keys, and the derivation of the VMM tests' inputs from every job that builds or runs them. doc/ci.md describes the build, its consumers, and the handoff. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/build-vmm-tests-once
branch
from
October 22, 2026 11:30
2f85af8 to
07efc18
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Part of #690.
Part of #430.
Summary
The KVM and MSHV legs of
openvmm-vmm-testseach built the same Linux OpenVMM VMM tests before running their backend's filter, and every leg waited for the guest artifacts before it started building. This PR builds the Linux tests once, in a build-only producer that starts beside the guest-artifact build, and hands the test content to two backend consumers that only run their existing filters with OpenVMM'svmm-tests-run-target.The WHP tests can't move yet: at the pinned OpenVMM revision,
vmm-tests-run-targetrequires Hyper-V on Windows whatever tests it runs, which the WHP runners can't provide (see WHP below). The WHP job therefore still builds and runs its tests in one job, unchanged, under the new job IDopenvmm-vmm-tests-windows.Why
Resolved selectionsline: thex86_64-unknown-linux-gnunextest archive, OpenVMM,openvmm_vhost, the musl pipette,guest_test_uefi, the test kernel, initrd, bzImage, and UEFI firmware) and lay out the same test content.devpushes from 2026-10-20 18:13 to 2026-10-21 13:32 UTC (runs and data on #690), the Linux legs took a median 15.6 (KVM) and 9.9 (MSHV) minutes, 25.6 Linux runner-minutes per run, and all three legs started only after the guest artifacts, which ran cold in 9 of the 14 runs for a median 5.3 minutes.Changes
nvx.py build-openvmm-testsandtest-openvmm --content(first commit)build-openvmm-tests --backend B [--backend B ...] --output DIRrunscargo xflowey vmm-tests-run --build-onlyon the union of the backends' filters ((<kvm>) | (<mshv>)), which writes the nextest archive, OpenVMM, the guest agents, the test firmware and kernels, and aflowey_hvlitethat runsvmm-tests-run-target. It parses the run script that the build writes strictly (runner inside the directory,--dir ., the built filter, and only the options that the pinned flowey writes), and writesnvx-openvmm-tests.json: the format, the OpenVMM revision and whether its checkout was clean, the Rust release, each backend's filter, the build filter, the runner and its options, and every file's size and executable bit.test-openvmm --backend B --content DIRruns backend B's own filter from such a directory with itsvmm-tests-run-target, without the OpenVMM checkout or a build. It refuses content of another OpenVMM revision than the gitlink, of another filter, or with a missing or truncated file, and it restores the executable bits that artifact downloads drop. It sets the sameOPENVMM_MICROVM_TEST_KERNEL/INITRD,RUSTUP_TOOLCHAIN, andXDG_CACHE_HOMEas before. Without--content,test-openvmmbuilds and runs the tests as before. On Windows, the build links with/Brepro, astest-openvmmdoes since ci: link the Windows OpenVMM test builds with /Brepro for sccache #714.CI (second commit)
build-openvmm-vmm-tests-linux(new)openvmm-changesonlybuild-openvmm-tests --backend kvm --backend mshv; uploads the one-dayopenvmm-vmm-tests-linuxartifact (include-hidden-filesfor.config/nextest.toml,overwrite: true); restores and, ondevpushes, saves thevmm-testsinput and Cargo cachesopenvmm-vmm-tests-linux(KVM, MSHV legs)artifactsand the buildguest-kernelandguest-alpinecomponents, runstest-openvmm --content; no OpenVMM checkout, no caches, no sccacheopenvmm-vmm-tests-windowsartifactsopenvmm-vmm-tests-<os>-<backend>-<run-id>-<run-attempt>names; Linux now collects<runner-temp>/openvmm-vmm-tests/test_results.run-openvmm-vmm-testsis set, which the classifier of ci: classify microVM tests, benchmarks, and artifact builds separately #712 sets only together withrun-guest-artifacts, or when the change detector fails, so it runs exactly when its consumers do, andrun-openvmm-vmm-tests=falseskips producer and consumers together. It reads that output alone, as ci: classify microVM tests, benchmarks, and artifact builds separately #712's one-output-per-job test requires.check-required-ciexpects it with them.Publish development releaseandPersist performance baseline, which accepted any skipped VMM-test job, now needopenvmm-changesand require the three VMM jobs to succeed, or all three to be skipped because the change selected no VMM tests, so a failed or unexpectedly skipped producer or consumer blocks them. Fork, draft, and path-filter gating, cancellation, and the eight cache saves' keys and guards are unchanged.doc/ci.mdgains an OpenVMM VMM tests section on the producer, consumers, handoff, manifest, and WHP;doc/usage.mddocuments both commands.WHP
At the pinned OpenVMM revision,
9f04d05c5since #661, whoseflowey/is identical to that ofd0bdbed8f,vmm-tests-run-targethard-codeshyperv: truefor Windows targets (flowey/flowey_hvlite/src/pipelines/vmm_tests_run_target.rs), soinstall_vmm_tests_external_depsrequiresGet-VMto succeed and theAllowFirmwareLoadFromFileandEnableAdditionalComPortsvalues underHKLM\Software\Microsoft\Windows NT\CurrentVersion\Virtualization, or, with--install-missing-deps, runsDISM /Online, which needs elevation. The WHP runners run jobs as Network Service (validate-runnerenforcesS-1-5-20), which the runner setup doesn't add to Hyper-V Administrators, and the setup sets neither value. The combinedvmm-tests-runcomputeshypervfrom the selected tests, which is why the WHP job works today. Splitting WHP needsvmm-tests-run-targetto take the dependencies that the build resolved, for example the--needs-hypervand--needs-whpoptions of commit79eb62981on thecopilot/vmm-tests-run-target-depsbranch ofnanvix/openvmm, for which I found nomicrosoft/openvmmpull request, and then a pin promotion. The content format and the consumer here already cover a Windows producer.Tests
cd, pipeline, runner outside the directory, unknown option, missing value, other directory, missing--target).doc/usage.mdsections.test-openvmmreads only those two whether it builds the tests or runs a build's content.Validation
scripts/test_nvx_tools.pymodule on each commit after the rebase ontobd977e35b(504 tests on the first, 505 on the second, OK);ruff check,ruff format --check;pyrightfor the Linux and Windows platforms;check_markdown_links.py;git diff --check.CiTests,CliTests,CiConfigurationTests, the guest-artifact download test, and the guest component selection test (134 tests, OK), which cover the Linux paths of the build and the consumer..github/actionlint.yaml:ci.ymlreports onlyunexpected key "queue" for "concurrency", which ci: queue the dev push runs instead of cancelling the waiting one #734 added and GitHub accepts; actionlint 1.7.7 predates the key.07efc1873, rebased onto ci: build the guest-artifact components in parallel and download only what each job uses #711: run 39771039182 passed every job, includingRequired status check, apart from the expected skips. The KVM and MSHV jobs downloaded onlyguest-kernelandguest-alpine(1 to 2 seconds each) and the content (4 and 6 seconds), and ran 58 and 57 tests, all passing, with 49 and 50 skipped, as before, in 10.4 and 4.6 minutes. The producer's second run on its KVM runner hit sccache for all but 4 compilations, so it built the content in 5.5 minutes.2f85af825, run 39691097674: every job passed, includingRequired status check, apart from the expected skips (the MSHV and WHP debug-kernel jobs on a pull request, the release, and the persistence). The producer built the same 214.7 MiB of content in 6.7 minutes, and the MSHV and KVM consumers ran the same 57 and 58 tests, all passing, in 4.8 and 10.3 minutes. Before the rebase, run 39625831399: the producer built the content (214.7 MiB, a 112.3 MiB artifact, uploaded in 6 seconds) in 7.0 minutes, and the MSHV and KVM consumers downloaded it in 8 and 6 seconds and ran 57 and 58 tests, all passing, with 50 and 49 skipped, exactly as before, in 4.6 and 10.5 minutes; every other job, includingRequired status check, passed.test-openvmmandtest-openvmm-unitcan't run locally.Expected effect
From the PR runs (details on #690):
flowey_hvliteon each KVM runner misses its sccache (197 misses, about 1.3 minutes); the second onazure-kvm-4missed 4.Post-merge verification
Interactions with other pull requests
This branch is rebased onto
bd977e35band adapted to the pull requests that merged since its first version:Publish development releaseandPersist performance baselinereadrun-openvmm-vmm-testsalone,check-required-ciexpects the build whenever the VMM tests are selected (the test jobs also need the guest artifacts), and ci: classify microVM tests, benchmarks, and artifact builds separately #712's tests map the build job to that output and accept the two gates' reads of it.openvmm-vmm-tests; the VMM-test inputs derive from the actions of all three jobs andartifacts, and don't include the host side ofnvx-rs.build-openvmm-testslinks with/Breproon Windows through_prepare_openvmm_test_environment, which a test now checks.guest-kernelandguest-alpine, as the WHP job does, and wait only forartifacts, notdistro-artifacts; ci: build the guest-artifact components in parallel and download only what each job uses #711's component test lists them among the consumers, mapsbuild-openvmm-teststo no guest file, checks that the build downloads, reads, and waits for no component, and checks both paths oftest-openvmm. A negative check confirmed that the test fails when a Linux test job lacks the Alpine download or the build downloads the kernel.9f04d05c5.Trial merges of this branch with the open siblings' heads as of 2026-10-22 11:35 UTC:
289be9cb1) and ci: reuse a pull request's guest images on its dev push #713 (7e3c899ac), both rebasing: ci: reuse a pull request's guest images on its dev push #713 already conflicts withdevin all four of the files that it shares with this branch, and ci: run the guest and sandbox steps beside the default microVM suite #706 indoc/ci.md; this branch adds a conflict in theREQUIRED_CI_*constants ofci.pyfor ci: run the guest and sandbox steps beside the default microVM suite #706. Keep both sides; whichever merges second resolves them.a904264bc) merges cleanly.