Skip to content

Commit ad3c92f

Browse files
committed
ci(e2e): reuse prebuilt VM driver artifact
Signed-off-by: Evan Lezar <elezar@nvidia.com>
1 parent 706e6a5 commit ad3c92f

6 files changed

Lines changed: 173 additions & 39 deletions

File tree

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Setup E2E VM Driver
2+
description: Download a prebuilt OpenShell VM driver for E2E tests
3+
4+
inputs:
5+
artifact-name:
6+
description: Artifact name to download, such as driver-vm-linux-amd64
7+
required: true
8+
9+
runs:
10+
using: composite
11+
steps:
12+
- name: Download prebuilt VM driver
13+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
14+
with:
15+
name: ${{ inputs.artifact-name }}
16+
path: .e2e/prebuilt-vm-driver
17+
18+
- name: Configure prebuilt VM driver
19+
shell: bash
20+
run: |
21+
set -euo pipefail
22+
extract_dir="$GITHUB_WORKSPACE/.e2e/prebuilt-vm-driver/bin"
23+
mkdir -p "$extract_dir"
24+
archive="$(find "$GITHUB_WORKSPACE/.e2e/prebuilt-vm-driver" -maxdepth 1 -type f -name 'openshell-driver-vm-*.tar.gz' -print -quit)"
25+
if [[ -z "$archive" ]]; then
26+
echo "downloaded artifact is missing openshell-driver-vm-*.tar.gz" >&2
27+
find "$GITHUB_WORKSPACE/.e2e/prebuilt-vm-driver" -maxdepth 2 -type f -print >&2 || true
28+
exit 1
29+
fi
30+
tar -xzf "$archive" -C "$extract_dir"
31+
driver="$extract_dir/openshell-driver-vm"
32+
if [[ ! -f "$driver" ]]; then
33+
echo "downloaded artifact is missing $driver" >&2
34+
exit 1
35+
fi
36+
chmod +x "$driver"
37+
"$driver" --version
38+
echo "OPENSHELL_VM_DRIVER_BIN=$driver" >> "$GITHUB_ENV"

.github/workflows/branch-e2e.yml

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,22 @@ jobs:
104104
component: cli
105105
platform: linux/amd64,linux/arm64
106106

107+
build-driver-vm-linux:
108+
needs: [pr_metadata]
109+
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
110+
permissions:
111+
contents: read
112+
packages: read
113+
uses: ./.github/workflows/driver-vm-linux.yml
114+
with:
115+
image-tag: ${{ github.sha }}
116+
checkout-ref: ${{ github.sha }}
117+
runtime-platforms: linux-x86_64
118+
driver-targets: >-
119+
[{"arch":"amd64","runner":"linux-amd64-cpu8","target":"x86_64-unknown-linux-gnu","zig_target":"x86_64-unknown-linux-gnu.2.28","platform":"linux-x86_64","guest_arch":"x86_64"}]
120+
107121
e2e:
108-
needs: [pr_metadata, build-gateway, build-supervisor, build-cli]
122+
needs: [pr_metadata, build-gateway, build-supervisor, build-cli, build-driver-vm-linux]
109123
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
110124
permissions:
111125
actions: read
@@ -117,6 +131,7 @@ jobs:
117131
runner: linux-arm64-cpu8
118132
cli-artifact-prefix: rust-binary-cli
119133
gateway-artifact-prefix: rust-binary-gateway
134+
vm-driver-artifact-name: driver-vm-linux-amd64
120135

121136
gpu-e2e:
122137
needs: [pr_metadata, build-gateway, build-supervisor, build-cli]
@@ -179,7 +194,7 @@ jobs:
179194

180195
core-e2e-result:
181196
name: Core E2E result
182-
needs: [pr_metadata, build-gateway, build-supervisor, build-cli, e2e, kubernetes-e2e]
197+
needs: [pr_metadata, build-gateway, build-supervisor, build-cli, build-driver-vm-linux, e2e, kubernetes-e2e]
183198
if: always() && needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
184199
runs-on: ubuntu-latest
185200
steps:
@@ -188,6 +203,7 @@ jobs:
188203
BUILD_GATEWAY_RESULT: ${{ needs.build-gateway.result }}
189204
BUILD_SUPERVISOR_RESULT: ${{ needs.build-supervisor.result }}
190205
BUILD_CLI_RESULT: ${{ needs.build-cli.result }}
206+
BUILD_DRIVER_VM_RESULT: ${{ needs.build-driver-vm-linux.result }}
191207
E2E_RESULT: ${{ needs.e2e.result }}
192208
KUBERNETES_E2E_RESULT: ${{ needs.kubernetes-e2e.result }}
193209
run: |
@@ -197,6 +213,7 @@ jobs:
197213
"build-gateway:$BUILD_GATEWAY_RESULT" \
198214
"build-supervisor:$BUILD_SUPERVISOR_RESULT" \
199215
"build-cli:$BUILD_CLI_RESULT" \
216+
"build-driver-vm-linux:$BUILD_DRIVER_VM_RESULT" \
200217
"e2e:$E2E_RESULT" \
201218
"kubernetes-e2e:$KUBERNETES_E2E_RESULT"; do
202219
name="${item%%:*}"

.github/workflows/driver-vm-linux.yml

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,26 @@ on:
44
workflow_call:
55
inputs:
66
cargo-version:
7-
required: true
7+
required: false
88
type: string
9+
default: ""
910
image-tag:
1011
required: true
1112
type: string
1213
checkout-ref:
1314
required: true
1415
type: string
16+
runtime-platforms:
17+
description: "Comma-separated VM runtime platforms to download: linux-aarch64,linux-x86_64"
18+
required: false
19+
type: string
20+
default: "linux-aarch64,linux-x86_64"
21+
driver-targets:
22+
description: "JSON matrix entries for Linux VM driver targets"
23+
required: false
24+
type: string
25+
default: >-
26+
[{"arch":"arm64","runner":"linux-arm64-cpu8","target":"aarch64-unknown-linux-gnu","zig_target":"aarch64-unknown-linux-gnu.2.28","platform":"linux-aarch64","guest_arch":"aarch64"},{"arch":"amd64","runner":"linux-amd64-cpu8","target":"x86_64-unknown-linux-gnu","zig_target":"x86_64-unknown-linux-gnu.2.28","platform":"linux-x86_64","guest_arch":"x86_64"}]
1527
1628
permissions:
1729
contents: read
@@ -39,11 +51,29 @@ jobs:
3951
- name: Download Linux runtime tarballs
4052
env:
4153
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
54+
RUNTIME_PLATFORMS: ${{ inputs['runtime-platforms'] }}
4255
run: |
4356
set -euo pipefail
4457
mkdir -p runtime-artifacts
4558
46-
for platform in linux-aarch64 linux-x86_64; do
59+
runtime_platforms="${RUNTIME_PLATFORMS//[[:space:]]/}"
60+
IFS=',' read -r -a platforms <<< "$runtime_platforms"
61+
if [ "${#platforms[@]}" -eq 0 ]; then
62+
echo "::error::No VM runtime platforms requested"
63+
exit 1
64+
fi
65+
for platform in "${platforms[@]}"; do
66+
case "$platform" in
67+
linux-aarch64 | linux-x86_64) ;;
68+
*)
69+
echo "::error::Unsupported VM runtime platform '$platform' in runtime-platforms"
70+
exit 1
71+
;;
72+
esac
73+
done
74+
printf '%s\n' "${platforms[@]}" > runtime-artifacts/platforms.txt
75+
76+
for platform in "${platforms[@]}"; do
4777
asset="vm-runtime-${platform}.tar.zst"
4878
echo "Downloading ${asset}..."
4979
if ! gh release download vm-runtime \
@@ -61,9 +91,9 @@ jobs:
6191
- name: Verify downloads
6292
run: |
6393
set -euo pipefail
64-
for platform in linux-aarch64 linux-x86_64; do
94+
while IFS= read -r platform; do
6595
test -f "runtime-artifacts/vm-runtime-${platform}.tar.zst"
66-
done
96+
done < runtime-artifacts/platforms.txt
6797
6898
- name: Upload runtime artifacts
6999
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
@@ -77,19 +107,7 @@ jobs:
77107
needs: [download-kernel-runtime]
78108
strategy:
79109
matrix:
80-
include:
81-
- arch: arm64
82-
runner: linux-arm64-cpu8
83-
target: aarch64-unknown-linux-gnu
84-
zig_target: aarch64-unknown-linux-gnu.2.28
85-
platform: linux-aarch64
86-
guest_arch: aarch64
87-
- arch: amd64
88-
runner: linux-amd64-cpu8
89-
target: x86_64-unknown-linux-gnu
90-
zig_target: x86_64-unknown-linux-gnu.2.28
91-
platform: linux-x86_64
92-
guest_arch: x86_64
110+
include: ${{ fromJSON(inputs['driver-targets']) }}
93111
runs-on: ${{ matrix.runner }}
94112
timeout-minutes: 30
95113
container:

.github/workflows/e2e-test.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ on:
2727
required: false
2828
type: string
2929
default: ""
30+
vm-driver-artifact-name:
31+
description: "Optional prebuilt VM driver artifact name"
32+
required: false
33+
type: string
34+
default: ""
3035

3136
permissions:
3237
actions: read
@@ -257,6 +262,24 @@ jobs:
257262
ref: ${{ inputs['checkout-ref'] || github.sha }}
258263
persist-credentials: false
259264

265+
- name: Use prebuilt OpenShell CLI
266+
if: inputs.cli-artifact-prefix != ''
267+
uses: ./.github/actions/setup-e2e-cli
268+
with:
269+
artifact-prefix: ${{ inputs.cli-artifact-prefix }}
270+
271+
- name: Use prebuilt OpenShell gateway
272+
if: inputs.gateway-artifact-prefix != ''
273+
uses: ./.github/actions/setup-e2e-gateway
274+
with:
275+
artifact-prefix: ${{ inputs.gateway-artifact-prefix }}
276+
277+
- name: Use prebuilt OpenShell VM driver
278+
if: inputs.vm-driver-artifact-name != ''
279+
uses: ./.github/actions/setup-e2e-vm-driver
280+
with:
281+
artifact-name: ${{ inputs.vm-driver-artifact-name }}
282+
260283
- name: Enable KVM access
261284
run: |
262285
set -euo pipefail

CI.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Three opt-in labels enable the long-running E2E suites:
2020
- `test:e2e-kubernetes` runs Kubernetes E2E with the HA Helm overlay
2121
(`replicaCount: 2` and bundled PostgreSQL) in `Branch E2E Checks`
2222

23-
When multiple labels are present, `Branch E2E Checks` builds the shared gateway and supervisor images once, builds one CLI artifact per runner architecture, and fans out all enabled suites in parallel. Docker, Podman, GPU, Rust, Python, and MCP E2E jobs reuse the matching prebuilt gateway and CLI binaries instead of compiling additional debug binaries in each job; Kubernetes E2E consumes the gateway image directly and reuses the prebuilt CLI.
23+
When multiple labels are present, `Branch E2E Checks` builds the shared gateway and supervisor images once, builds one CLI artifact per runner architecture, builds the Linux VM driver artifact once, and fans out all enabled suites in parallel. Docker, Podman, GPU, Rust, Python, MCP, and VM E2E jobs reuse the matching prebuilt gateway and CLI binaries instead of compiling additional debug binaries in each job; Kubernetes E2E consumes the gateway image directly and reuses the prebuilt CLI. VM E2E also reuses the prebuilt VM driver artifact and falls back to local VM-driver/runtime preparation for local runs or workflow invocations that omit the artifact.
2424
The `OpenShell / E2E` and `OpenShell / GPU E2E` required statuses are evaluated from separate suite result jobs inside that workflow. `test:e2e-kubernetes` is optional while HA behavior is under active iteration: failures are visible in the workflow run but do not publish a required CI gate status.
2525

2626
The GitHub ruleset should require the `OpenShell / ...` statuses published by `Required CI Gates`, not the push-triggered workflow jobs directly.

e2e/rust/e2e-vm.sh

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@
2323
# mise run e2e:vm
2424
#
2525
# What the script does:
26-
# 1. Ensures the VM runtime (libkrun + gvproxy) and bundled supervisor are staged.
26+
# 1. When no prebuilt VM driver is supplied, ensures the VM runtime
27+
# (libkrun + gvproxy) and bundled supervisor are staged.
2728
# 2. Builds `openshell-gateway`, `openshell-driver-vm`, and the
28-
# `openshell` CLI with the embedded runtime.
29+
# `openshell` CLI with the embedded runtime as needed. When CI supplies
30+
# OPENSHELL_GATEWAY_BIN, OPENSHELL_VM_DRIVER_BIN, or OPENSHELL_BIN, the
31+
# matching prebuilt binary is reused instead of rebuilt.
2932
# 3. On macOS, codesigns the VM driver (libkrun needs the
3033
# `com.apple.security.hypervisor` entitlement).
3134
# 4. Writes a per-run gateway config with `[openshell.drivers.vm]`
@@ -35,7 +38,7 @@
3538
# 5. Tears the gateway down and (on failure) preserves the gateway
3639
# log and every VM serial console log for post-mortem.
3740
#
38-
# Prerequisites (handled automatically by this script if missing):
41+
# Prerequisites (handled automatically by this script for local VM-driver builds):
3942
# - `mise run vm:setup` — downloads / builds the libkrun runtime.
4043
# - `mise run vm:supervisor` — builds the bundled sandbox supervisor.
4144

@@ -45,8 +48,9 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
4548
source "${ROOT}/e2e/support/gateway-common.sh"
4649

4750
COMPRESSED_DIR="${ROOT}/target/vm-runtime-compressed"
48-
GATEWAY_BIN="${ROOT}/target/debug/openshell-gateway"
49-
DRIVER_BIN="${ROOT}/target/debug/openshell-driver-vm"
51+
GATEWAY_BIN="${OPENSHELL_GATEWAY_BIN:-${ROOT}/target/debug/openshell-gateway}"
52+
DRIVER_BIN="${OPENSHELL_VM_DRIVER_BIN:-${ROOT}/target/debug/openshell-driver-vm}"
53+
CLI_BIN="${OPENSHELL_BIN:-${ROOT}/target/debug/openshell}"
5054
E2E_TEST_OVERRIDE="${OPENSHELL_E2E_VM_TEST:-}"
5155
E2E_FEATURES="${OPENSHELL_E2E_VM_FEATURES:-e2e-vm}"
5256

@@ -73,25 +77,59 @@ if [ -n "${RUSTC_WRAPPER:-}" ] && [ "${OPENSHELL_E2E_VM_ALLOW_RUSTC_WRAPPER:-0}"
7377
unset RUSTC_WRAPPER
7478
fi
7579

76-
mkdir -p "${COMPRESSED_DIR}"
80+
if [ -z "${OPENSHELL_VM_DRIVER_BIN:-}" ]; then
81+
mkdir -p "${COMPRESSED_DIR}"
7782

78-
if ! find "${COMPRESSED_DIR}" -maxdepth 1 -name 'libkrun*.zst' | grep -q .; then
79-
echo "==> Preparing embedded VM runtime (mise run vm:setup)"
80-
mise run vm:setup
83+
if ! find "${COMPRESSED_DIR}" -maxdepth 1 -name 'libkrun*.zst' | grep -q .; then
84+
echo "==> Preparing embedded VM runtime (mise run vm:setup)"
85+
mise run vm:setup
86+
fi
87+
88+
if [ ! -f "${COMPRESSED_DIR}/openshell-sandbox.zst" ]; then
89+
echo "==> Building bundled VM supervisor (mise run vm:supervisor)"
90+
mise run vm:supervisor
91+
fi
92+
93+
export OPENSHELL_VM_RUNTIME_COMPRESSED_DIR="${OPENSHELL_VM_RUNTIME_COMPRESSED_DIR:-${COMPRESSED_DIR}}"
94+
else
95+
echo "==> Skipping embedded VM runtime prep; prebuilt VM driver was supplied"
8196
fi
8297

83-
if [ ! -f "${COMPRESSED_DIR}/openshell-sandbox.zst" ]; then
84-
echo "==> Building bundled VM supervisor (mise run vm:supervisor)"
85-
mise run vm:supervisor
98+
build_packages=()
99+
if [ -z "${OPENSHELL_GATEWAY_BIN:-}" ]; then
100+
build_packages+=(-p openshell-server)
101+
else
102+
echo "==> Using prebuilt openshell-gateway at ${GATEWAY_BIN}"
103+
fi
104+
if [ -z "${OPENSHELL_VM_DRIVER_BIN:-}" ]; then
105+
build_packages+=(-p openshell-driver-vm)
106+
else
107+
echo "==> Using prebuilt openshell-driver-vm at ${DRIVER_BIN}"
108+
fi
109+
if [ -z "${OPENSHELL_BIN:-}" ]; then
110+
build_packages+=(-p openshell-cli)
111+
else
112+
echo "==> Using prebuilt openshell CLI at ${CLI_BIN}"
86113
fi
87114

88-
export OPENSHELL_VM_RUNTIME_COMPRESSED_DIR="${OPENSHELL_VM_RUNTIME_COMPRESSED_DIR:-${COMPRESSED_DIR}}"
115+
if [ "${#build_packages[@]}" -gt 0 ]; then
116+
echo "==> Building ${build_packages[*]}"
117+
cargo build "${build_packages[@]}"
118+
fi
89119

90-
echo "==> Building openshell-gateway, openshell-driver-vm, openshell (CLI)"
91-
cargo build \
92-
-p openshell-server \
93-
-p openshell-driver-vm \
94-
-p openshell-cli
120+
for pair in \
121+
"openshell-gateway:${GATEWAY_BIN}" \
122+
"openshell-driver-vm:${DRIVER_BIN}" \
123+
"openshell CLI:${CLI_BIN}"; do
124+
label="${pair%%:*}"
125+
path="${pair#*:}"
126+
if [ ! -x "${path}" ]; then
127+
echo "ERROR: expected ${label} binary at ${path}" >&2
128+
exit 1
129+
fi
130+
done
131+
export OPENSHELL_BIN="${CLI_BIN}"
132+
DRIVER_DIR="$(dirname "${DRIVER_BIN}")"
95133

96134
if [ "$(uname -s)" = "Darwin" ]; then
97135
echo "==> Codesigning openshell-driver-vm (Hypervisor entitlement)"
@@ -224,7 +262,7 @@ ttl_secs = 0
224262
225263
[openshell.drivers.vm]
226264
grpc_endpoint = "https://host.openshell.internal:${HOST_PORT}"
227-
driver_dir = "${ROOT}/target/debug"
265+
driver_dir = "${DRIVER_DIR}"
228266
state_dir = "${RUN_STATE_DIR}"
229267
guest_tls_ca = "${PKI_DIR}/ca.crt"
230268
guest_tls_cert = "${PKI_DIR}/client/tls.crt"

0 commit comments

Comments
 (0)