feat: add NVX build-test smoke workflow - #9014
Conversation
Add smoke-nvx-build-test, modeled on smoke-cloud-hypervisor-build-test, to validate that the NVX microVM runtime can run real Node.js and Go build/test workloads on KVM hardware. The workload runs inside an NVX one-shot microVM from a pre-agent step (NVX is not a native gh-aw sandbox runtime), using an Alpine 3.22.1 guest layer with Node.js 22 and Go 1.24 preinstalled on the host. It builds from a scratch copy of the workspace so the host checkout is never modified, and returns results through the workspace copy-back. Trust the new workflow as a pinned NVX artifact signer, mirroring the existing smoke-nvx-copilot entry. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
🚀 Security Guard has started processing this pull request |
|
🧊🏗️ Smoke NVX Build Test reports failed. NVX compatibility issue detected.
|
|
| Metric | Base | PR | Delta |
|---|---|---|---|
| Lines | 92.65% | 92.65% | ➡️ +0.00% |
| Statements | 91.13% | 91.13% | ➡️ +0.00% |
| Functions | 89.11% | 89.11% | ➡️ +0.00% |
| Branches | 84.26% | 84.25% | 📉 -0.01% |
📁 Per-file Coverage Changes (3 files)
| File | Lines (Before → After) | Statements (Before → After) |
|---|---|---|
src/nvx/one-shot-adapter.ts |
83.5% → 82.9% (-0.60%) | 80.3% → 79.8% (-0.56%) |
src/nvx/artifact-manifest.ts |
87.9% → 88.0% (+0.18%) | 87.9% → 88.0% (+0.18%) |
src/log-directory-setup.ts |
96.2% → 100.0% (+3.78%) | 96.3% → 100.0% (+3.71%) |
Coverage comparison generated by scripts/ci/compare-coverage.ts
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The hardware run exhausts runner disk before boot, and guest npm egress excludes the lockfile’s package registry.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 2
Open (2)
What changed in this PR
Adds an NVX smoke workflow for real Node.js and Go build/test workloads inside a microVM.
Changes:
- Adds and compiles the NVX build-test workflow.
- Trusts its pinned workflow as an NVX artifact signer.
- Extends preflight signer tests.
| File | Description |
|---|---|
.github/workflows/smoke-nvx-build-test.md |
Defines the smoke workload and validation. |
.github/workflows/smoke-nvx-build-test.lock.yml |
Compiled workflow. |
src/nvx/artifact-manifest.ts |
Adds the trusted signer identity. |
src/nvx/preflight.test.ts |
Tests both smoke signers. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| -cf - . | tar -C "$build" -xf - | ||
| cd "$build" | ||
|
|
||
| timeout 15m npm ci > "$out/npm-ci.log" 2>&1 |
| --nvx-memory-mib 4096 \ | ||
| --nvx-memory-max-bytes 3758096384 \ | ||
| --nvx-pids-max 1024 \ | ||
| --nvx-scratch-bytes 6442450944 \ |
NVX stages its EROFS layer images and the guest scratch image under /run/awf-nvx/runs, and /run is a RAM-backed tmpfs Ubuntu sizes at ~10% of RAM. The first hardware run failed in mke2fs with 'No space left on device' before the guest booted. Grow the tmpfs ceiling (pages are only allocated when written) and shrink the scratch overlay to 3 GiB. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
✅ Copilot review passed with no inline comments. @lpcox Add the |
|
🚀 Security Guard has started processing this pull request |
|
🧊🏗️ Smoke NVX Build Test reports failed. NVX compatibility issue detected.
|
This comment has been minimized.
This comment has been minimized.
|
@copilot address review feedback |
Both findings are now addressed:
|
|
🚀 Security Guard has started processing this pull request |
|
🧊🏗️ Smoke NVX Build Test reports failed. NVX compatibility issue detected.
|
This comment has been minimized.
This comment has been minimized.
|
| Metric | Base | PR | Delta |
|---|---|---|---|
| Lines | 92.65% | 92.65% | ➡️ +0.00% |
| Statements | 91.13% | 91.13% | ➡️ +0.00% |
| Functions | 89.11% | 89.11% | ➡️ +0.00% |
| Branches | 84.26% | 84.25% | 📉 -0.01% |
📁 Per-file Coverage Changes (3 files)
| File | Lines (Before → After) | Statements (Before → After) |
|---|---|---|
src/nvx/one-shot-adapter.ts |
83.5% → 82.9% (-0.60%) | 80.3% → 79.8% (-0.56%) |
src/nvx/artifact-manifest.ts |
87.9% → 88.0% (+0.18%) | 87.9% → 88.0% (+0.18%) |
src/log-directory-setup.ts |
96.2% → 100.0% (+3.78%) | 96.3% → 100.0% (+3.71%) |
Coverage comparison generated by scripts/ci/compare-coverage.ts
NVX applies the credential deny list to every layer, including the workspace, so the repo's .npmrc (legacy-peer-deps=true) never reaches the guest and npm ci fails with ERESOLVE on the @babel/core 7/8 peer conflict. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
🚀 Security Guard has started processing this pull request |
|
🧊🏗️ Smoke NVX Build Test reports failed. NVX compatibility issue detected.
|
|
| Metric | Base | PR | Delta |
|---|---|---|---|
| Lines | 92.65% | 92.65% | ➡️ +0.00% |
| Statements | 91.13% | 91.13% | ➡️ +0.00% |
| Functions | 89.11% | 89.17% | 📈 +0.06% |
| Branches | 84.26% | 84.22% | 📉 -0.04% |
📁 Per-file Coverage Changes (5 files)
| File | Lines (Before → After) | Statements (Before → After) |
|---|---|---|
src/nvx/one-shot-adapter.ts |
83.5% → 82.9% (-0.60%) | 80.3% → 79.8% (-0.56%) |
src/nvx/artifact-manifest.ts |
87.9% → 88.0% (+0.18%) | 87.9% → 88.0% (+0.18%) |
src/nvx/confinement.ts |
88.2% → 88.9% (+0.73%) | 83.3% → 84.6% (+1.24%) |
src/cloud-hypervisor/confinement-verifier.ts |
82.9% → 84.7% (+1.83%) | 81.6% → 83.0% (+1.39%) |
src/log-directory-setup.ts |
96.2% → 100.0% (+3.78%) | 96.3% → 100.0% (+3.71%) |
Coverage comparison generated by scripts/ci/compare-coverage.ts
This comment has been minimized.
This comment has been minimized.
Jest ran 395/400 in the guest. The remaining failures came from the guest layer: /etc/passwd had no root entry (getRealUserHome tests) and /var/tmp was not world-writable (enclave path tests). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
🚀 Security Guard has started processing this pull request |
|
🧊🏗️ Smoke NVX Build Test completed. NVX build test passed. ✅
|
|
| Metric | Base | PR | Delta |
|---|---|---|---|
| Lines | 92.65% | 92.65% | ➡️ +0.00% |
| Statements | 91.13% | 91.13% | ➡️ +0.00% |
| Functions | 89.11% | 89.17% | 📈 +0.06% |
| Branches | 84.26% | 84.22% | 📉 -0.04% |
📁 Per-file Coverage Changes (5 files)
| File | Lines (Before → After) | Statements (Before → After) |
|---|---|---|
src/nvx/one-shot-adapter.ts |
83.5% → 82.9% (-0.60%) | 80.3% → 79.8% (-0.56%) |
src/nvx/artifact-manifest.ts |
87.9% → 88.0% (+0.18%) | 87.9% → 88.0% (+0.18%) |
src/nvx/confinement.ts |
88.2% → 88.9% (+0.73%) | 83.3% → 84.6% (+1.24%) |
src/cloud-hypervisor/confinement-verifier.ts |
82.9% → 84.7% (+1.83%) | 81.6% → 83.0% (+1.39%) |
src/log-directory-setup.ts |
96.2% → 100.0% (+3.78%) | 96.3% → 100.0% (+3.71%) |
Coverage comparison generated by scripts/ci/compare-coverage.ts
🧊🏗️ NVX Build Test Results
Overall: PASS
|
|
✅ Validated on real KVM hardware: run 36195809798 passed every check (microvm_run, guest_completed, workspace_copy_back, node_build, node_test, go_build, go_test, network_isolation). Fixes needed to get here:
|

Summary
Adds
smoke-nvx-build-test, an NVX counterpart to the existingsmoke-cloud-hypervisor-build-testandsmoke-gvisor-build-testworkflows. It validates that the NVX microVM runtime can run real Node.js and Go build/test workloads on KVM hardware — a much heavier workload than the single Copilot prompt thatsmoke-nvx-copilotexercises.Design
Workload parity with Cloud Hypervisor. The guest runs the same workload, with the same result schema and post-step validation:
http_codemust be200)npm ci && npm run buildsquid-config|docker-manager|logger)coloranduuidfixturesexample.commust be blocked)NVX-specific structure. NVX is not a native gh-aw
sandbox.agent.runtime, so this workflow followssmoke-nvx-copilotinstead of setting a runtime in the frontmatter:build_nvx_artifactsjob fetches and verifies the pinned NVX release, then self-attests the manifest.awf --container-runtime nvx.Guest layer. Alpine 3.22.1 (the same pinned digest as the Copilot smoke) with Node.js 22 and Go 1.24:
github.com,registry.npmjs.org. The Go fixtures have no module dependencies, andGOTOOLCHAIN=localblocks toolchain downloads.esbuild,unrs-resolver,@parcel/watcher); all ship musl builds.Host checkout safety. The guest builds from a scratch copy of the workspace (excluding
node_modules,.git, anddist). The host checkout and its glibcnode_modulesare never modified. Only a.nvx-build-test/results directory comes back through the workspace export.Sizing. NVX's defaults (512 MiB, 128 pids) are sized for one CLI invocation, and the guest is hard-coded to 1 vCPU. This workflow requests:
Jest runs
--runInBandto fit the single vCPU.Extra checks beyond the Cloud Hypervisor workflow.
microvm_run(awf exit code),guest_completed, andworkspace_copy_back. The last one proves both--envpassthrough and the copy-back path: the guest writes a per-run marker and the host checks it.Evidence. All logs (
awf.log, inner proxy logs, and per-stage workload logs) are uploaded as thenvx-build-test-evidenceartifact.Source change
src/nvx/artifact-manifest.tsaddsNVX_BUILD_TEST_SIGNER_WORKFLOW, so the new workflow can verify its own self-attested manifest. This mirrors the existingNVX_SMOKE_SIGNER_WORKFLOWentry: it trusts exactly one additional pinned lock file and leaves the default signer unchanged. The preflight test is parameterized to cover both smoke signers. The existing untrusted-signer rejection test still passes.Validation
gh aw compile smoke-nvx-build-test: 0 warnings. Postprocessing touched only the new lock file, and the session-state fix from fix: get the NVX Copilot smoke test to genuinely pass on real KVM hardware #9010 applies to it automatically.bash -n. Both heredocs and theRESULTS_EOFterminator land at column 0.jest src/nvx/preflight.test.ts: 24 of 24 tests pass.tsc --noEmitis clean. ESLint shows no new warnings.Not yet run on hardware. I couldn't run it locally. The end-to-end check is running the workflow on this PR. Because the workflow isn't on
mainyet,workflow_dispatchmay not be able to target it; adding thetest-nvx-buildlabel to this PR triggers it instead.Known risk
The NVX confinement verifier race (#9012) affects every NVX launch, so this workflow can fail at
openvmmReadybefore the guest boots, for reasons unrelated to this change. In that case themicrovm_runandguest_completedchecks fail, with the confinement error visible inlogs/awf.log.