Skip to content

ci: build the Linux OpenVMM VMM tests once for the KVM and MSHV jobs - #708

Open
Pedro Henrique Penna (ppenna) wants to merge 2 commits into
devfrom
agents/build-vmm-tests-once
Open

Pedro Henrique Penna (ppenna) wants to merge 2 commits into
devfrom
agents/build-vmm-tests-once

Conversation

@ppenna

@ppenna Pedro Henrique Penna (ppenna) commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Part of #690.
Part of #430.

Summary

The KVM and MSHV legs of openvmm-vmm-tests each 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's vmm-tests-run-target.

The WHP tests can't move yet: at the pinned OpenVMM revision, vmm-tests-run-target requires 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 ID openvmm-vmm-tests-windows.

Why

  • In reference run 39585909588, the KVM and MSHV legs each spent about 4.6 and 4.9 minutes on flowey, the test-binary build, and OpenVMM before their first test, and both logs resolve the identical artifact selection (the same Resolved selections line: the x86_64-unknown-linux-gnu nextest 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.
  • In the 14 successful first-attempt full-matrix dev pushes 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.
  • One Linux build serves both backends: the target triple is the same, the backend is detected at run time, OpenVMM's Cargo configuration sets no host-CPU code generation, both pools run Azure Linux 3, and the GNU OpenVMM that a KVM runner builds already runs the MSHV microVM tests.

Changes

nvx.py build-openvmm-tests and test-openvmm --content (first commit)

  • build-openvmm-tests --backend B [--backend B ...] --output DIR runs cargo xflowey vmm-tests-run --build-only on the union of the backends' filters ((<kvm>) | (<mshv>)), which writes the nextest archive, OpenVMM, the guest agents, the test firmware and kernels, and a flowey_hvlite that runs vmm-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 writes nvx-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 DIR runs backend B's own filter from such a directory with its vmm-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 same OPENVMM_MICROVM_TEST_KERNEL/INITRD, RUSTUP_TOOLCHAIN, and XDG_CACHE_HOME as before. Without --content, test-openvmm builds and runs the tests as before. On Windows, the build links with /Brepro, as test-openvmm does since ci: link the Windows OpenVMM test builds with /Brepro for sccache #714.

CI (second commit)

Job Runner Waits for Does
build-openvmm-vmm-tests-linux (new) general KVM, whose sccache holds the Linux test build openvmm-changes only build-openvmm-tests --backend kvm --backend mshv; uploads the one-day openvmm-vmm-tests-linux artifact (include-hidden-files for .config/nextest.toml, overwrite: true); restores and, on dev pushes, saves the vmm-tests input and Cargo caches
openvmm-vmm-tests-linux (KVM, MSHV legs) general KVM / MSHV, as before artifacts and the build downloads the content and the guest artifacts, runs test-openvmm --content; no OpenVMM checkout, no caches, no sccache
openvmm-vmm-tests-windows general WHP, as before artifacts unchanged: builds and runs the WHP tests
  • Diagnostics keep the openvmm-vmm-tests-<os>-<backend>-<run-id>-<run-attempt> names; Linux now collects <runner-temp>/openvmm-vmm-tests/test_results.
  • Gating: the build runs whenever run-openvmm-vmm-tests is set, which the classifier of ci: classify microVM tests, benchmarks, and artifact builds separately #712 sets only together with run-guest-artifacts, or when the change detector fails, so it runs exactly when its consumers do, and run-openvmm-vmm-tests=false skips 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-ci expects it with them. Publish development release and Persist performance baseline, which accepted any skipped VMM-test job, now need openvmm-changes and 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.
  • Reruns: a rerun of a failed consumer reuses the producer's artifact from the earlier attempt instead of rebuilding; a rerun of the producer replaces its artifact.
  • Docs: doc/ci.md gains an OpenVMM VMM tests section on the producer, consumers, handoff, manifest, and WHP; doc/usage.md documents both commands.

WHP

At the pinned OpenVMM revision, 9f04d05c5 since #661, whose flowey/ is identical to that of d0bdbed8f, vmm-tests-run-target hard-codes hyperv: true for Windows targets (flowey/flowey_hvlite/src/pipelines/vmm_tests_run_target.rs), so install_vmm_tests_external_deps requires Get-VM to succeed and the AllowFirmwareLoadFromFile and EnableAdditionalComPorts values under HKLM\Software\Microsoft\Windows NT\CurrentVersion\Virtualization, or, with --install-missing-deps, runs DISM /Online, which needs elevation. The WHP runners run jobs as Network Service (validate-runner enforces S-1-5-20), which the runner setup doesn't add to Hyper-V Administrators, and the setup sets neither value. The combined vmm-tests-run computes hyperv from the selected tests, which is why the WHP job works today. Splitting WHP needs vmm-tests-run-target to take the dependencies that the build resolved, for example the --needs-hyperv and --needs-whp options of commit 79eb62981 on the copilot/vmm-tests-run-target-deps branch of nanvix/openvmm, for which I found no microsoft/openvmm pull request, and then a pin promotion. The content format and the consumer here already cover a Windows producer.

Tests

  • The union build filter; both run-script formats and each rejection (filter, cd, pipeline, runner outside the directory, unknown option, missing value, other directory, missing --target).
  • The build's commands, environment, and manifest; the refusal of another OS's backends, unknown backends, and used directories.
  • The consumer's command, working directory, environment, and restored executable bits; the refusal of another format, revision, backend, or filter, a malformed runner or options, a file outside the directory, and a missing or truncated file.
  • The CLI parsing and doc/usage.md sections.
  • Workflow structure: the producer's runner, needs, and condition; the change detector's outputs that the VMM-test jobs and the two gates read; the consumers' needs and conditions; the upload's name, path, hidden files, overwrite, and retention; the download and test order; diagnostics on both OSes; the WHP job's independence of the Linux build; the release and persistence gate; the required-check policy for every event and flag combination; the cache keys and saves; and the VMM suite's inputs derived from every job that builds or runs it.

Validation

  • Windows: the full scripts/test_nvx_tools.py module (500 tests, OK, after the rebase onto 4f9e7cbd1); ruff check, ruff format --check; pyright for the Linux and Windows platforms; check_markdown_links.py; git diff --check.
  • WSL Ubuntu (Python 3.14): CiTests, CliTests, CiConfigurationTests, and the guest-artifact download test (132 tests, OK), which cover the Linux paths of the build and the consumer.
  • actionlint v1.7.7 (release binary, checksum verified) with .github/actionlint.yaml: ci.yml reports only unexpected 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.
  • CI on the rebased head 2f85af825, run 39691097674: every job passed, including Required 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, including Required status check, passed. test-openvmm and test-openvmm-unit can't run locally.

Expected effect

From the PR runs (details on #690):

  • Linux runner-minutes: 7.0 + 4.6 + 10.5 = 22.1 and 6.7 + 4.8 + 10.3 = 21.8 instead of a median 25.6 (−3.5 and −3.8, −14% and −15%). The producer's extra flowey_hvlite build for the target, about 1.5 minutes, missed sccache on each of the three KVM runners that have run the producer so far, each for the first time, so the saving may grow as their caches warm.
  • Linux lanes: with cold guest artifacts (9 of the 14 runs above), the build overlaps them, so the Linux lanes finish about 3 minutes sooner: in run 39691097674, whose guest artifacts took 5.9 minutes, the MSHV and KVM lanes finished 11.8 and 18.3 minutes after the first job started, 3.5 and 2.8 minutes before the medians of the comparable runs above; with warm ones, the tests wait for the build and the handoff, so the KVM and MSHV lanes should finish about 1.5 and 1 minutes later, still well before the WHP lane.
  • Wall time: unchanged, because the WHP lane, the last to finish in most full runs, is unchanged until the WHP split is unblocked.

Post-merge verification

  • Compare at least five comparable full-matrix runs after merge with the 14 runs above: producer and consumer queue time and duration, the content's size, upload, and download times, the Linux lanes' finish times, and the Linux runner-minutes.

Interactions with other pull requests

This branch is rebased onto 4f9e7cbd1 and adapted to the pull requests that merged since its first version:

Trial merges of this branch with the open siblings' heads as of 2026-10-21 23:10 UTC show what the pull request that merges second must change:

Copilot AI balanced review requested due to automatic review settings October 21, 2026 15:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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

Comment thread scripts/nvx_tools/ci.py
Copilot AI balanced review requested due to automatic review settings October 21, 2026 15:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The cross-runner artifact handoff needs successful producer and KVM/MSHV consumer runs before human approval.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

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 and the guest
artifacts 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 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>
Copilot AI balanced review requested due to automatic review settings October 21, 2026 23:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The cross-runner executable handoff and release/performance gate changes warrant final human review with current-head CI results.

No open issues 🎉

Review effort: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants