From e0256968e507920d4781be28bb197a6e3dcbc14d Mon Sep 17 00:00:00 2001 From: Pedro Henrique Penna Date: Sat, 19 Sep 2026 08:33:51 -0700 Subject: [PATCH 1/3] ci: OpenVMM Rust targets on Linux runners Install x86_64-unknown-linux-musl for both KVM and MSHV runners so OpenVMM tests can build the Linux pipette without modifying the protected Rustup state during a workflow job. Extend runner environment validation to require both x86_64-unknown-none and x86_64-unknown-linux-musl. This rejects stale runners before they accept an OpenVMM test job and fail during setup. --- scripts/setup/setup-linux-runner.sh | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/scripts/setup/setup-linux-runner.sh b/scripts/setup/setup-linux-runner.sh index 96a6403..44b719c 100644 --- a/scripts/setup/setup-linux-runner.sh +++ b/scripts/setup/setup-linux-runner.sh @@ -461,13 +461,11 @@ install_rust_tools() { RUSTUP_HOME="$trusted_rustup_home" \ RUSTUP_TOOLCHAIN="$RUST_TOOLCHAIN" \ "$rustup" target add x86_64-unknown-none - if [ "$backend" = mshv ]; then - run_as_root env \ - CARGO_HOME="$trusted_cargo_home" \ - RUSTUP_HOME="$trusted_rustup_home" \ - RUSTUP_TOOLCHAIN="$RUST_TOOLCHAIN" \ - "$rustup" target add x86_64-unknown-linux-musl - fi + run_as_root env \ + CARGO_HOME="$trusted_cargo_home" \ + RUSTUP_HOME="$trusted_rustup_home" \ + RUSTUP_TOOLCHAIN="$RUST_TOOLCHAIN" \ + "$rustup" target add x86_64-unknown-linux-musl rust_version=$(run_as_root env \ CARGO_HOME="$trusted_cargo_home" \ @@ -624,6 +622,13 @@ check_environment() { rustc --version | awk '{print $2}') version_at_least "$rust_version" "$RUST_MINIMUM_VERSION" || die "Rust ${RUST_MINIMUM_VERSION} or newer is required" + installed_rust_targets=$(run_as_runner env \ + RUSTUP_TOOLCHAIN="$RUST_TOOLCHAIN" \ + rustup target list --installed) + for rust_target in x86_64-unknown-none x86_64-unknown-linux-musl; do + printf '%s\n' "$installed_rust_targets" | grep -Fxq "$rust_target" || + die "Rust target ${rust_target} is not installed" + done run_as_runner cargo nextest --version | grep -Fq "cargo-nextest ${CARGO_NEXTEST_VERSION}" || die "cargo-nextest ${CARGO_NEXTEST_VERSION} is not installed" From 30593b2370b8c3dc2e94783b38b91a8f7fab98ab Mon Sep 17 00:00:00 2001 From: Pedro Henrique Penna Date: Sat, 19 Sep 2026 08:35:30 -0700 Subject: [PATCH 2/3] ci: propagate Windows NVX validation failures Check $LASTEXITCODE after every Python invocation in the Windows validate-nvx action and immediately return the failing status. PowerShell does not automatically treat a nonzero native-process exit as a terminating error. A later successful command could therefore mask failed compilation, unit tests, host checks, or CLI smoke tests and incorrectly report a successful validation job. --- .github/actions/validate-nvx/action.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/actions/validate-nvx/action.yml b/.github/actions/validate-nvx/action.yml index 2388a3a..a92a104 100644 --- a/.github/actions/validate-nvx/action.yml +++ b/.github/actions/validate-nvx/action.yml @@ -22,9 +22,16 @@ runs: shell: powershell run: | python -m compileall -q scripts + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } python -m unittest scripts/test_performance.py scripts/test_nvx_tools.py scripts/test_microvm_tests.py scripts/test_development_release.py -v + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } python .github\skills\nvx-host-connect\scripts\test_hosts.py -v + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } python scripts\nvx.py --help + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } python scripts\nvx.py test-openvmm --help + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } python scripts\nvx.py test-microvm --help + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } python scripts\nvx.py benchmark --help + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } From f56c7b4703e976d84118ddcc7ecccfc8e22bbb74 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Penna Date: Sat, 19 Sep 2026 08:35:48 -0700 Subject: [PATCH 3/3] ci: canonical OpenVMM boot test across backends Add the exact multiarch::openvmm_linux_x64_boot selector to the existing OpenVMM test set while retaining the fork-specific PVH lifecycle and ttrpc snapshot coverage. Build a same-revision static Linux pipette in the musl binary job and transfer it to the Windows test lane. On WHP, prepare the Windows test bundle with the host-buildable artifacts, inject the Linux pipette, and run the complete selector through vmm-tests-run-target with only the WHP dependency enabled. Require the OpenVMM binary matrix to succeed before starting the test matrix, preventing secondary missing-artifact failures. Upload per-backend Petri/OpenVMM logs and nextest JUnit results whenever an OpenVMM test job fails. Document the Linux and Windows prerequisites, artifact ownership, and CI handoff. Add regression coverage for the exact selector, release and CI flags, Rust targets, pipette staging, producer gating, artifact uploads, and failure diagnostics. --- .github/workflows/ci.yml | 48 +++++++++++- doc/build.md | 14 ++-- doc/ci.md | 16 +++- doc/usage.md | 30 +++++++- scripts/nvx_tools/ci.py | 43 +++++++++-- scripts/test_nvx_tools.py | 150 +++++++++++++++++++++++++++++++++++--- 6 files changed, 274 insertions(+), 27 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6433440..bde9417 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -233,12 +233,36 @@ jobs: build-mode: ${{ matrix.build-mode }} binary-path: ${{ matrix.binary }} + - name: Build Linux pipette + if: matrix.build-mode == 'musl' + shell: bash + run: | + set -euo pipefail + rustup target add "${{ matrix.target }}" + cargo build \ + --manifest-path openvmm/Cargo.toml \ + --release \ + --target "${{ matrix.target }}" \ + --package pipette \ + --bin pipette + - name: Report sccache if: always() uses: ./.github/actions/sccache with: operation: stop + - name: Upload Linux pipette + if: matrix.build-mode == 'musl' + uses: actions/upload-artifact@v7 + with: + name: openvmm-linux-pipette + path: openvmm/target/${{ matrix.target }}/release/pipette + if-no-files-found: error + compression-level: 0 + overwrite: true + retention-days: 1 + - name: Upload OpenVMM executable uses: actions/upload-artifact@v7 with: @@ -261,7 +285,7 @@ jobs: openvmm-tests: name: OpenVMM tests / ${{ matrix.name }} - needs: [openvmm-changes] + needs: [openvmm-changes, openvmm-binaries] if: >- ${{ always() && @@ -274,6 +298,7 @@ jobs: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository ) && + needs.openvmm-binaries.result == 'success' && ( needs.openvmm-changes.result != 'success' || needs.openvmm-changes.outputs.run-tests == 'true' @@ -317,6 +342,13 @@ jobs: - name: Set up retrying curl uses: ./.github/actions/setup-curl + - name: Download Linux pipette + if: runner.os == 'Windows' + uses: actions/download-artifact@v8 + with: + name: openvmm-linux-pipette + path: ${{ runner.temp }}/openvmm-linux-pipette + - name: Prepare Windows cache tools if: runner.os == 'Windows' shell: powershell @@ -410,6 +442,7 @@ jobs: shell: powershell env: GH_TOKEN: ${{ github.token }} + OPENVMM_LINUX_PIPETTE: ${{ runner.temp }}\openvmm-linux-pipette\pipette run: python scripts\nvx.py test-openvmm --backend "${{ matrix.backend }}" - name: Report sccache @@ -478,6 +511,19 @@ jobs: ${{ format('{0}/cargo-home/git', runner.temp) }} key: openvmm-cargo-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('openvmm/Cargo.lock') }} + - name: Upload OpenVMM failure diagnostics + if: failure() + uses: actions/upload-artifact@v7 + with: + name: openvmm-tests-${{ matrix.backend }} + path: | + openvmm/target/vmm_tests/test_results + openvmm/target/vmm_tests/target/nextest/ci/junit.xml + ${{ runner.temp }}/${{ matrix.backend }}/test_results + ${{ runner.temp }}/${{ matrix.backend }}/target/nextest/ci/junit.xml + if-no-files-found: warn + overwrite: true + nvx-microvm-tests: name: NVX microVM tests / ${{ matrix.name }} needs: [artifacts, openvmm-binaries, openvmm-changes] diff --git a/doc/build.md b/doc/build.md index 3133842..feaf82c 100644 --- a/doc/build.md +++ b/doc/build.md @@ -16,9 +16,10 @@ The OpenVMM restore step excludes the compatibility IGVM artifact, which NVX does not build or package, so builds do not depend on unrelated upstream workflow artifacts. -OpenVMM's microVM tests build their own minimal Xen PVH guest from source in -the OpenVMM checkout. They do not consume `build/vmlinux` or -`build/initramfs.cpio.gz`. NVX uses those two artifacts only for its Linux and +OpenVMM's tests build their own minimal Xen PVH guest and Linux pipette agent +from source in the OpenVMM checkout. They also restore OpenVMM's packaged +Linux-direct test kernel and initrd. They do not consume `build/vmlinux` or +`build/initramfs.cpio.gz`; NVX uses those two artifacts only for its Linux and device correctness tests, benchmarks, and packaged runtime. On a Linux host, build the guest directly: @@ -51,8 +52,11 @@ python3 scripts/nvx.py test-openvmm --backend kvm python3 scripts/nvx.py test-microvm --backend kvm ``` -The first command needs only the OpenVMM checkout. The second needs the -standard build outputs above and writes complete per-scenario logs under +On Linux, the first command needs only the initialized OpenVMM checkout and the +Rust targets provisioned by the host setup. A local Windows/WHP run additionally +needs `OPENVMM_LINUX_PIPETTE` to identify a same-revision Linux pipette; see +[Usage](usage.md#test-openvmm). The second command needs the standard build +outputs above and writes complete per-scenario logs under `build/test-results/microvm` by default. The initramfs includes the sandbox PID-1 bootstrap, its container namespace diff --git a/doc/ci.md b/doc/ci.md index c765c10..f3b203b 100644 --- a/doc/ci.md +++ b/doc/ci.md @@ -6,8 +6,20 @@ three runners labeled by operating system, backend, and `virtual-machine`. Jobs target the shared backend labels so any available matching runner can execute them. This allows the backend lanes to execute concurrently without binding a workload to a specific host. `openvmm-tests` builds its Xen PVH probe -entirely from the OpenVMM checkout and exercises OpenVMM lifecycle, TTRPC, and -snapshot contracts without restoring NVX guest artifacts. +entirely from the OpenVMM checkout and exercises OpenVMM lifecycle, TTRPC, +snapshot, and the canonical `multiarch::openvmm_linux_x64_boot` Linux-direct +boot contract without restoring NVX guest artifacts. The selector uses an exact +nextest name matcher so similarly named boot variants are not included. +Linux runners build the static musl pipette locally. The Linux musl OpenVMM +binary job also uploads a same-revision pipette for WHP; Windows prepares the +remaining test bundle, adds that pipette, and runs the complete selector with +`vmm-tests-run-target`. Linux runner provisioning installs both +`x86_64-unknown-none` and `x86_64-unknown-linux-musl` because the protected +Rustup state is read-only to workflow jobs. +Failed OpenVMM jobs upload available Petri/OpenVMM diagnostics and nextest +JUnit results as `openvmm-tests-`. Uploads warn if setup failed before +producing diagnostics. + `nvx-microvm-tests` consumes the NVX Linux kernel and Alpine initramfs and exercises Linux, SMP, virtio, sandbox, and snapshot behavior through the public OpenVMM CLI. Failure logs from the NVX layer are uploaded per backend. diff --git a/doc/usage.md b/doc/usage.md index 199178f..5f9686e 100644 --- a/doc/usage.md +++ b/doc/usage.md @@ -28,7 +28,7 @@ python3 scripts/nvx.py performance gate --help | `build-initramfs` | Build the Alpine initramfs natively. | | `build-openvmm` | Build the OpenVMM release binary. | | `setup-cross-os-cache` | Install GNU tar and zstd for GitHub Actions cross-OS caches. | -| `test-openvmm` | Run self-contained OpenVMM microVM control-plane tests. | +| `test-openvmm` | Run checkout-owned OpenVMM control-plane and Linux-direct boot tests. | | `test-microvm` | Run NVX Linux and device correctness tests through OpenVMM. | | `build` | Build the guest artifacts and OpenVMM. | | `download` | Download and install the latest matching GitHub release. | @@ -127,7 +127,33 @@ python3 scripts/nvx.py test-openvmm --backend {kvm,mshv,whp} ``` Builds and runs OpenVMM's checkout-owned microVM tests. The test artifacts are -produced by OpenVMM itself; NVX's kernel and initramfs are not required. +produced from the pinned OpenVMM source; NVX's kernel and initramfs are not +required. The selector includes only the canonical Linux-direct boot test +`multiarch::openvmm_linux_x64_boot`, not similarly named boot variants. + +Linux hosts build the required static Linux pipette guest agent locally and +therefore need the `x86_64-unknown-linux-musl` Rust target. On Windows, set +`OPENVMM_LINUX_PIPETTE` to a same-revision Linux pipette before running the WHP +test: + +```powershell +$env:OPENVMM_LINUX_PIPETTE = "C:\path\to\pipette" +python scripts\nvx.py test-openvmm --backend whp +``` + +Build that artifact from the OpenVMM checkout on Linux: + +```bash +cd openvmm +cargo xflowey restore-packages --no-compat-igvm +rustup target add x86_64-unknown-linux-musl +cargo build --release --target x86_64-unknown-linux-musl \ + --package pipette --bin pipette +``` + +The resulting file is +`openvmm/target/x86_64-unknown-linux-musl/release/pipette`. GitHub Actions +builds, uploads, and downloads this artifact automatically for the WHP lane. ### `test-microvm` diff --git a/scripts/nvx_tools/ci.py b/scripts/nvx_tools/ci.py index b222914..c7469bd 100644 --- a/scripts/nvx_tools/ci.py +++ b/scripts/nvx_tools/ci.py @@ -24,10 +24,15 @@ ZSTD_SHA256 = "acb4e8111511749dc7a3ebedca9b04190e37a17afeb73f55d4425dbf0b90fad9" OPENVMM_TEST_BACKENDS = ("kvm", "mshv", "whp") OPENVMM_GUEST_RUST_TARGET = "x86_64-unknown-none" -OPENVMM_MICROVM_TEST_FILTER = ( +OPENVMM_LINUX_RUST_TARGET = "x86_64-unknown-linux-musl" +OPENVMM_MICROVM_BASE_TEST_FILTER = ( "test(openvmm_microvm_test_pvh_x64_phase_1_lifecycle) + " "test(test_ttrpc_microvm_pvh_snapshot)" ) +OPENVMM_LINUX_X64_BOOT_TEST_FILTER = "test(=multiarch::openvmm_linux_x64_boot)" +OPENVMM_MICROVM_TEST_FILTER = ( + f"{OPENVMM_MICROVM_BASE_TEST_FILTER} + {OPENVMM_LINUX_X64_BOOT_TEST_FILTER}" +) def validate_openvmm_test_backend(backend: str) -> None: @@ -59,11 +64,15 @@ def run_openvmm_tests(backend: str) -> None: cargo = require_tool("cargo") rustup = require_tool("rustup") - run_checked([rustup, "target", "add", OPENVMM_GUEST_RUST_TARGET]) + targets = [OPENVMM_GUEST_RUST_TARGET] + if os.name != "nt": + targets.append(OPENVMM_LINUX_RUST_TARGET) + run_checked([rustup, "target", "add", *targets]) run_checked( [cargo, "xflowey", "restore-packages", "--no-compat-igvm"], cwd=OPENVMM_DIR, ) + test_dir = Path(os.environ.get("RUNNER_TEMP", "C:/ovm-tests")) / backend command = [ cargo, "xflowey", @@ -75,12 +84,36 @@ def run_openvmm_tests(backend: str) -> None: OPENVMM_MICROVM_TEST_FILTER, ] if os.name == "nt": + pipette = os.environ.get("OPENVMM_LINUX_PIPETTE") + if not pipette: + raise ScriptError("OPENVMM_LINUX_PIPETTE is required for WHP OpenVMM tests") + pipette_path = require_file(Path(pipette), "OpenVMM Linux pipette") + build_command = [ + *command, + "--build-only", + "--dir", + os.fspath(test_dir), + ] + build_command[build_command.index("--filter") + 1] = ( + OPENVMM_MICROVM_BASE_TEST_FILTER + ) + run_checked(build_command, cwd=OPENVMM_DIR) + test_dir.mkdir(parents=True, exist_ok=True) + shutil.copy2(pipette_path, test_dir / "pipette") + command = [ + cargo, + "xflowey", + "vmm-tests-run-target", + "--needs-whp", + "--ci-profile", + "--skip-vhd-prompt", + "--filter", + OPENVMM_MICROVM_TEST_FILTER, + ] command.extend( ( "--dir", - os.fspath( - Path(os.environ.get("RUNNER_TEMP", "C:/ovm-tests")) / backend - ), + os.fspath(test_dir), ) ) run_checked(command, cwd=OPENVMM_DIR) diff --git a/scripts/test_nvx_tools.py b/scripts/test_nvx_tools.py index f7973ec..434596b 100644 --- a/scripts/test_nvx_tools.py +++ b/scripts/test_nvx_tools.py @@ -640,6 +640,8 @@ def test_openvmm_tests_are_independent_of_nvx_guest_artifacts(self): openvmm = root / "openvmm" openvmm.mkdir() (openvmm / "Cargo.toml").touch() + pipette = root / "pipette" + pipette.write_bytes(b"pipette") backend = "whp" if os.name == "nt" else "kvm" with ( @@ -655,36 +657,72 @@ def test_openvmm_tests_are_independent_of_nvx_guest_artifacts(self): "OPENVMM_MICROVM_PVH_INITRD": "nvx-initrd", "PETRI_CAPABILITIES": "vpci", "RUNNER_TEMP": os.fspath(root), + "OPENVMM_LINUX_PIPETTE": os.fspath(pipette), }, ), ): ci.run_openvmm_tests(backend) - self.assertEqual(run_checked.call_count, 3) - install_target, restore, tests = run_checked.call_args_list + self.assertEqual(run_checked.call_count, 4 if os.name == "nt" else 3) + install_target, restore, *test_commands = run_checked.call_args_list self.assertEqual( install_target.args[0], - ["rustup", "target", "add", "x86_64-unknown-none"], + [ + "rustup", + "target", + "add", + "x86_64-unknown-none", + *([] if os.name == "nt" else ["x86_64-unknown-linux-musl"]), + ], ) self.assertEqual( restore.args[0], ["cargo", "xflowey", "restore-packages", "--no-compat-igvm"], ) - command = tests.args[0] - self.assertEqual(command[:3], ["cargo", "xflowey", "vmm-tests-run"]) + for test_command in test_commands: + self.assertIn("--ci-profile", test_command.args[0]) + self.assertIn("--skip-vhd-prompt", test_command.args[0]) + command = test_commands[-1].args[0] + self.assertEqual( + command[:3], + [ + "cargo", + "xflowey", + "vmm-tests-run-target" if os.name == "nt" else "vmm-tests-run", + ], + ) + if os.name == "nt": + self.assertIn("--needs-whp", command) + self.assertNotIn("--needs-hyperv", command) + else: + self.assertIn("--release", command) filter_index = command.index("--filter") self.assertEqual(command[filter_index + 1], ci.OPENVMM_MICROVM_TEST_FILTER) - self.assertIn( - "test_ttrpc_microvm_pvh_snapshot", + self.assertEqual( ci.OPENVMM_MICROVM_TEST_FILTER, + "test(openvmm_microvm_test_pvh_x64_phase_1_lifecycle) + " + "test(test_ttrpc_microvm_pvh_snapshot) + " + "test(=multiarch::openvmm_linux_x64_boot)", ) - self.assertEqual(tests.kwargs["cwd"], openvmm) - self.assertNotIn("env", tests.kwargs) + self.assertEqual( + ci.OPENVMM_LINUX_X64_BOOT_TEST_FILTER, + "test(=multiarch::openvmm_linux_x64_boot)", + ) + self.assertEqual(test_commands[-1].kwargs["cwd"], openvmm) + self.assertNotIn("env", test_commands[-1].kwargs) if os.name == "nt": + build = test_commands[0].args[0] + self.assertIn("--build-only", build) + self.assertIn("--release", build) + self.assertEqual( + build[build.index("--filter") + 1], + ci.OPENVMM_MICROVM_BASE_TEST_FILTER, + ) self.assertEqual( command[command.index("--dir") + 1], os.fspath(root / backend), ) + self.assertEqual((root / backend / "pipette").read_bytes(), b"pipette") def test_openvmm_tests_reject_unknown_backend(self): with self.assertRaisesRegex(common.ScriptError, "unsupported.*backend"): @@ -692,6 +730,79 @@ def test_openvmm_tests_reject_unknown_backend(self): class CiConfigurationTests(unittest.TestCase): + def test_validate_nvx_windows_propagates_python_failures(self): + action = ( + common.REPO_ROOT / ".github" / "actions" / "validate-nvx" / "action.yml" + ).read_text(encoding="utf-8") + windows_steps = action.split( + " - name: Validate NVX CLI on Windows", + maxsplit=1, + )[1] + lines = windows_steps.splitlines() + python_commands = [ + index + for index, line in enumerate(lines) + if line.startswith(" python ") + ] + + self.assertGreater(len(python_commands), 0) + for index in python_commands: + self.assertEqual( + lines[index + 1], + " if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }", + ) + + def test_openvmm_tests_require_binary_handoff(self): + workflow = (common.REPO_ROOT / ".github" / "workflows" / "ci.yml").read_text( + encoding="utf-8" + ) + openvmm_tests = workflow.split(" openvmm-tests:", maxsplit=1)[1].split( + "\n nvx-microvm-tests:", maxsplit=1 + )[0] + + self.assertIn( + "needs.openvmm-binaries.result == 'success'", + openvmm_tests, + ) + + def test_openvmm_tests_upload_failure_diagnostics(self): + workflow = (common.REPO_ROOT / ".github" / "workflows" / "ci.yml").read_text( + encoding="utf-8" + ) + openvmm_tests = workflow.split(" openvmm-tests:", maxsplit=1)[1].split( + "\n nvx-microvm-tests:", maxsplit=1 + )[0] + upload_name = " - name: Upload OpenVMM failure diagnostics" + self.assertIn(upload_name, openvmm_tests) + upload_step = openvmm_tests.split(upload_name, maxsplit=1)[1].split( + "\n - name:", maxsplit=1 + )[0] + + self.assertIn(" if: failure()", upload_step) + self.assertIn(" uses: actions/upload-artifact@v7", upload_step) + self.assertIn( + " name: openvmm-tests-${{ matrix.backend }}", upload_step + ) + self.assertIn(" if-no-files-found: warn", upload_step) + self.assertIn(" overwrite: true", upload_step) + for platform in ("KVM", "MSHV", "Windows"): + self.assertLess( + openvmm_tests.index(f" - name: Run OpenVMM tests on {platform}"), + openvmm_tests.index(upload_name), + ) + upload_paths = upload_step.split(" path: |\n", maxsplit=1)[1].split( + "\n if-no-files-found:", maxsplit=1 + )[0] + self.assertEqual( + [line.strip() for line in upload_paths.splitlines()], + [ + "openvmm/target/vmm_tests/test_results", + "openvmm/target/vmm_tests/target/nextest/ci/junit.xml", + "${{ runner.temp }}/${{ matrix.backend }}/test_results", + "${{ runner.temp }}/${{ matrix.backend }}/target/nextest/ci/junit.xml", + ], + ) + def test_flowey_downloads_use_retrying_curl(self): action = ( common.REPO_ROOT / ".github" / "actions" / "setup-curl" / "action.yml" @@ -837,11 +948,19 @@ def test_ci_shares_openvmm_inputs_and_binary_artifacts(self): self.assertIn("artifact: openvmm-linux-musl", workflow) self.assertIn("artifact: openvmm-windows-msvc", workflow) self.assertIn("build/openvmm.provenance.json", workflow) - self.assertIn("name: ${{ matrix.artifact }}-executable", workflow) - self.assertIn("name: ${{ matrix.artifact }}-provenance", workflow) + for step_name, artifact_name in ( + ("Upload Linux pipette", "openvmm-linux-pipette"), + ("Upload OpenVMM executable", "${{ matrix.artifact }}-executable"), + ("Upload OpenVMM provenance", "${{ matrix.artifact }}-provenance"), + ): + upload_step = workflow.split( + f" - name: {step_name}", + maxsplit=1, + )[1].split("\n - name:", maxsplit=1)[0] + self.assertIn(f"name: {artifact_name}", upload_step) + self.assertIn("overwrite: true", upload_step) self.assertIn("path: openvmm/target/release", workflow) self.assertIn("path: build", workflow) - self.assertEqual(workflow.count("overwrite: true"), 2) self.assertNotIn("path: .", workflow) self.assertIn("uses: actions/download-artifact@v8", workflow) self.assertIn("openvmm-binary-v5-", build_action) @@ -876,6 +995,10 @@ def test_runner_setup_pins_and_validates_sccache(self): linux_setup = ( common.REPO_ROOT / "scripts" / "setup" / "setup-linux-runner.sh" ).read_text(encoding="utf-8") + check_environment = linux_setup.split( + "check_environment() {", + maxsplit=1, + )[1].split("\n}", maxsplit=1)[0] self.assertIn('$SccacheVersion = "0.18.0"', windows_setup) self.assertIn( @@ -897,6 +1020,9 @@ def test_runner_setup_pins_and_validates_sccache(self): self.assertIn("sccache --stop-server", action) self.assertIn("SCCACHE_IDLE_TIMEOUT", action) self.assertIn("sccache --version", validate_runner) + self.assertIn("rustup target list --installed", check_environment) + for rust_target in ("x86_64-unknown-none", "x86_64-unknown-linux-musl"): + self.assertIn(rust_target, check_environment) self.assertNotRegex( workflow, r"(?m)^\s+path: openvmm/target\s*$",