From 5cd379d4582116400fe5397a358e4fbe00e0bb15 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Thu, 16 Jul 2026 00:21:41 +0200 Subject: [PATCH 1/3] ci(e2e): reuse prebuilt CLI artifacts Signed-off-by: Evan Lezar --- .github/actions/setup-e2e-cli/action.yml | 29 ++++++++++++++++++++ .github/workflows/branch-e2e.yml | 32 ++++++++++++++++------- .github/workflows/docker-build.yml | 2 +- .github/workflows/e2e-gpu-test.yaml | 12 +++++++++ .github/workflows/e2e-kubernetes-test.yml | 13 +++++++++ .github/workflows/e2e-test.yml | 18 +++++++++++++ CI.md | 2 +- e2e/rust/e2e-docker.sh | 2 -- e2e/rust/e2e-kubernetes.sh | 7 ++++- e2e/rust/e2e-podman.sh | 2 -- e2e/rust/src/harness/binary.rs | 19 ++++++++------ e2e/support/gateway-common.sh | 16 +++++++----- tasks/test.toml | 2 -- 13 files changed, 124 insertions(+), 32 deletions(-) create mode 100644 .github/actions/setup-e2e-cli/action.yml diff --git a/.github/actions/setup-e2e-cli/action.yml b/.github/actions/setup-e2e-cli/action.yml new file mode 100644 index 0000000000..fb894194c8 --- /dev/null +++ b/.github/actions/setup-e2e-cli/action.yml @@ -0,0 +1,29 @@ +name: Setup E2E CLI +description: Download an architecture-matched prebuilt OpenShell CLI for E2E tests + +inputs: + artifact-prefix: + description: Artifact name prefix; linux- is appended automatically + required: true + +runs: + using: composite + steps: + - name: Download prebuilt CLI + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ format('{0}-linux-{1}', inputs.artifact-prefix, runner.arch == 'X64' && 'amd64' || 'arm64') }} + path: .e2e/prebuilt-cli + + - name: Configure prebuilt CLI + shell: bash + run: | + set -euo pipefail + cli="$GITHUB_WORKSPACE/.e2e/prebuilt-cli/openshell" + if [[ ! -f "$cli" ]]; then + echo "downloaded artifact is missing $cli" >&2 + exit 1 + fi + chmod +x "$cli" + "$cli" --version + echo "OPENSHELL_BIN=$cli" >> "$GITHUB_ENV" diff --git a/.github/workflows/branch-e2e.yml b/.github/workflows/branch-e2e.yml index 28728e597f..1316fc524e 100644 --- a/.github/workflows/branch-e2e.yml +++ b/.github/workflows/branch-e2e.yml @@ -95,38 +95,42 @@ jobs: build-cli: needs: [pr_metadata] - if: needs.pr_metadata.outputs.should_run == 'true' + if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_any_e2e == 'true' permissions: contents: read packages: write uses: ./.github/workflows/docker-build.yml with: component: cli - platform: linux/amd64 + platform: linux/amd64,linux/arm64 e2e: - needs: [pr_metadata, build-gateway, build-supervisor] + needs: [pr_metadata, build-gateway, build-supervisor, build-cli] if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true' permissions: + actions: read contents: read packages: read uses: ./.github/workflows/e2e-test.yml with: image-tag: ${{ github.sha }} runner: linux-arm64-cpu8 + cli-artifact-prefix: rust-binary-cli gpu-e2e: - needs: [pr_metadata, build-supervisor] + needs: [pr_metadata, build-supervisor, build-cli] if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_gpu_e2e == 'true' permissions: + actions: read contents: read packages: read uses: ./.github/workflows/e2e-gpu-test.yaml with: image-tag: ${{ github.sha }} + cli-artifact-prefix: rust-binary-cli kubernetes-e2e: - needs: [pr_metadata, build-gateway, build-supervisor] + needs: [pr_metadata, build-gateway, build-supervisor, build-cli] if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true' strategy: fail-fast: false @@ -145,6 +149,7 @@ jobs: topology: sidecar extra_helm_values: deploy/helm/openshell/ci/values-sidecar.yaml permissions: + actions: read contents: read packages: read uses: ./.github/workflows/e2e-kubernetes-test.yml @@ -153,11 +158,13 @@ jobs: job-name: Kubernetes E2E (Rust smoke, ${{ matrix.topology }}, Agent Sandbox ${{ matrix.agent_sandbox_api }}) agent-sandbox-version: ${{ matrix.agent_sandbox_version }} extra-helm-values: ${{ matrix.extra_helm_values }} + cli-artifact-prefix: rust-binary-cli kubernetes-ha-e2e: - needs: [pr_metadata, build-gateway, build-supervisor] + needs: [pr_metadata, build-gateway, build-supervisor, build-cli] if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_kubernetes_ha_e2e == 'true' permissions: + actions: read contents: read packages: read uses: ./.github/workflows/e2e-kubernetes-test.yml @@ -166,10 +173,11 @@ jobs: job-name: Kubernetes HA E2E (Rust smoke) extra-helm-values: deploy/helm/openshell/ci/values-high-availability.yaml external-postgres-secret: openshell-ha-pg + cli-artifact-prefix: rust-binary-cli core-e2e-result: name: Core E2E result - needs: [pr_metadata, build-gateway, build-supervisor, e2e, kubernetes-e2e] + needs: [pr_metadata, build-gateway, build-supervisor, build-cli, e2e, kubernetes-e2e] if: always() && needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true' runs-on: ubuntu-latest steps: @@ -177,6 +185,7 @@ jobs: env: BUILD_GATEWAY_RESULT: ${{ needs.build-gateway.result }} BUILD_SUPERVISOR_RESULT: ${{ needs.build-supervisor.result }} + BUILD_CLI_RESULT: ${{ needs.build-cli.result }} E2E_RESULT: ${{ needs.e2e.result }} KUBERNETES_E2E_RESULT: ${{ needs.kubernetes-e2e.result }} run: | @@ -185,6 +194,7 @@ jobs: for item in \ "build-gateway:$BUILD_GATEWAY_RESULT" \ "build-supervisor:$BUILD_SUPERVISOR_RESULT" \ + "build-cli:$BUILD_CLI_RESULT" \ "e2e:$E2E_RESULT" \ "kubernetes-e2e:$KUBERNETES_E2E_RESULT"; do name="${item%%:*}" @@ -198,19 +208,21 @@ jobs: gpu-e2e-result: name: GPU E2E result - needs: [pr_metadata, build-supervisor, gpu-e2e] + needs: [pr_metadata, build-supervisor, build-cli, gpu-e2e] if: always() && needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_gpu_e2e == 'true' runs-on: ubuntu-latest steps: - name: Verify GPU E2E jobs env: BUILD_SUPERVISOR_RESULT: ${{ needs.build-supervisor.result }} + BUILD_CLI_RESULT: ${{ needs.build-cli.result }} GPU_E2E_RESULT: ${{ needs.gpu-e2e.result }} run: | set -euo pipefail failed=0 for item in \ "build-supervisor:$BUILD_SUPERVISOR_RESULT" \ + "build-cli:$BUILD_CLI_RESULT" \ "gpu-e2e:$GPU_E2E_RESULT"; do name="${item%%:*}" result="${item#*:}" @@ -223,7 +235,7 @@ jobs: kubernetes-ha-e2e-result: name: Kubernetes HA E2E result - needs: [pr_metadata, build-gateway, build-supervisor, kubernetes-ha-e2e] + needs: [pr_metadata, build-gateway, build-supervisor, build-cli, kubernetes-ha-e2e] if: always() && needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_kubernetes_ha_e2e == 'true' runs-on: ubuntu-latest steps: @@ -231,6 +243,7 @@ jobs: env: BUILD_GATEWAY_RESULT: ${{ needs.build-gateway.result }} BUILD_SUPERVISOR_RESULT: ${{ needs.build-supervisor.result }} + BUILD_CLI_RESULT: ${{ needs.build-cli.result }} KUBERNETES_HA_E2E_RESULT: ${{ needs.kubernetes-ha-e2e.result }} run: | set -euo pipefail @@ -238,6 +251,7 @@ jobs: for item in \ "build-gateway:$BUILD_GATEWAY_RESULT" \ "build-supervisor:$BUILD_SUPERVISOR_RESULT" \ + "build-cli:$BUILD_CLI_RESULT" \ "kubernetes-ha-e2e:$KUBERNETES_HA_E2E_RESULT"; do name="${item%%:*}" result="${item#*:}" diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 0ed09d9d4b..da8c07c0a4 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -154,7 +154,7 @@ jobs: echo "arches=${arches[*]}" echo "binary_component=$binary_component" echo "binary_name=$binary_name" - echo "artifact_prefix=rust-binary-${component}-${binary_component}" + echo "artifact_prefix=rust-binary-${component}" echo "image_tag_base=$image_tag_base" echo "features=$features" echo "has_image=$has_image" diff --git a/.github/workflows/e2e-gpu-test.yaml b/.github/workflows/e2e-gpu-test.yaml index bcbd96bb49..0be2f89a39 100644 --- a/.github/workflows/e2e-gpu-test.yaml +++ b/.github/workflows/e2e-gpu-test.yaml @@ -7,8 +7,14 @@ on: description: "Image tag to test (typically the commit SHA)" required: true type: string + cli-artifact-prefix: + description: "Optional prebuilt CLI artifact prefix (artifact suffix is linux-)" + required: false + type: string + default: "" permissions: + actions: read contents: read packages: read @@ -58,6 +64,12 @@ jobs: steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - name: Use prebuilt OpenShell CLI + if: inputs.cli-artifact-prefix != '' + uses: ./.github/actions/setup-e2e-cli + with: + artifact-prefix: ${{ inputs.cli-artifact-prefix }} + - name: Log in to GHCR run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin diff --git a/.github/workflows/e2e-kubernetes-test.yml b/.github/workflows/e2e-kubernetes-test.yml index f8c042b2a6..23399fc2a1 100644 --- a/.github/workflows/e2e-kubernetes-test.yml +++ b/.github/workflows/e2e-kubernetes-test.yml @@ -42,8 +42,14 @@ on: required: false type: string default: "v2026.4.25" + cli-artifact-prefix: + description: "Optional prebuilt CLI artifact prefix (artifact suffix is linux-)" + required: false + type: string + default: "" permissions: + actions: read contents: read packages: read @@ -56,6 +62,7 @@ jobs: runs-on: ${{ inputs.runner }} timeout-minutes: 60 permissions: + actions: read contents: read packages: read env: @@ -68,6 +75,12 @@ jobs: with: ref: ${{ inputs['checkout-ref'] || github.sha }} + - name: Use prebuilt OpenShell CLI + if: inputs.cli-artifact-prefix != '' + uses: ./.github/actions/setup-e2e-cli + with: + artifact-prefix: ${{ inputs.cli-artifact-prefix }} + - name: Install mise run: | curl https://mise.run | MISE_VERSION=v2026.4.25 sh diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index cb7bc67d19..c58f738686 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -17,8 +17,14 @@ on: required: false type: string default: "" + cli-artifact-prefix: + description: "Optional prebuilt CLI artifact prefix (artifact suffix is linux-)" + required: false + type: string + default: "" permissions: + actions: read contents: read packages: read @@ -63,6 +69,12 @@ jobs: ref: ${{ inputs['checkout-ref'] || github.sha }} persist-credentials: false + - name: Use prebuilt OpenShell CLI + if: inputs.cli-artifact-prefix != '' + uses: ./.github/actions/setup-e2e-cli + with: + artifact-prefix: ${{ inputs.cli-artifact-prefix }} + - name: Check out MCP conformance tests if: matrix.suite == 'mcp' uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -126,6 +138,12 @@ jobs: ref: ${{ inputs['checkout-ref'] || github.sha }} persist-credentials: false + - name: Use prebuilt OpenShell CLI + if: inputs.cli-artifact-prefix != '' + uses: ./.github/actions/setup-e2e-cli + with: + artifact-prefix: ${{ inputs.cli-artifact-prefix }} + - name: Install mise run: | curl https://mise.run | MISE_VERSION=v2026.4.25 sh diff --git a/CI.md b/CI.md index 19fad7bd35..c98b049aba 100644 --- a/CI.md +++ b/CI.md @@ -20,7 +20,7 @@ Three opt-in labels enable the long-running E2E suites: - `test:e2e-kubernetes` runs Kubernetes E2E with the HA Helm overlay (`replicaCount: 2` and bundled PostgreSQL) in `Branch E2E Checks` -When multiple labels are present, `Branch E2E Checks` builds the shared gateway and supervisor images once and fans out all enabled suites in parallel. +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. Rust and MCP E2E jobs reuse the matching prebuilt CLI instead of compiling another debug CLI in each job. 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. The GitHub ruleset should require the `OpenShell / ...` statuses published by `Required CI Gates`, not the push-triggered workflow jobs directly. diff --git a/e2e/rust/e2e-docker.sh b/e2e/rust/e2e-docker.sh index 99cd6daf75..6c28868083 100755 --- a/e2e/rust/e2e-docker.sh +++ b/e2e/rust/e2e-docker.sh @@ -13,8 +13,6 @@ E2E_TEST="${OPENSHELL_E2E_DOCKER_TEST:-smoke}" E2E_FEATURES="${OPENSHELL_E2E_DOCKER_FEATURES:-e2e,e2e-docker}" DEFAULT_WORKLOAD_MANIFEST="${ROOT}/e2e/gpu/images/.build/workloads.yaml" -cargo build -p openshell-cli - if [ "${E2E_TEST}" = "gpu" ] && [ -z "${OPENSHELL_E2E_WORKLOAD_MANIFEST:-}" ] && [ ! -f "${DEFAULT_WORKLOAD_MANIFEST}" ]; then echo "note: running GPU e2e without a workload manifest; workload validation will log an explicit skip. Build one with 'mise run e2e:workloads:build' or set OPENSHELL_E2E_WORKLOAD_MANIFEST." fi diff --git a/e2e/rust/e2e-kubernetes.sh b/e2e/rust/e2e-kubernetes.sh index ec0faefed5..20343f7231 100755 --- a/e2e/rust/e2e-kubernetes.sh +++ b/e2e/rust/e2e-kubernetes.sh @@ -21,7 +21,12 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" E2E_FEATURES="${OPENSHELL_E2E_KUBERNETES_FEATURES:-e2e,e2e-host-gateway,e2e-kubernetes}" -cargo build -p openshell-cli +# Docker and Podman build their local gateway and CLI together in the shared +# gateway wrapper. Kubernetes consumes published gateway images, so only its +# local CLI needs to be built when CI has not supplied a prebuilt one. +if [ -z "${OPENSHELL_BIN:-}" ]; then + cargo build -p openshell-cli +fi test_filter=() if [ -n "${OPENSHELL_E2E_KUBE_TEST:-}" ]; then diff --git a/e2e/rust/e2e-podman.sh b/e2e/rust/e2e-podman.sh index 39b6b523a8..16796c0562 100755 --- a/e2e/rust/e2e-podman.sh +++ b/e2e/rust/e2e-podman.sh @@ -13,8 +13,6 @@ E2E_TEST="${OPENSHELL_E2E_PODMAN_TEST:-}" E2E_FEATURES="${OPENSHELL_E2E_PODMAN_FEATURES:-e2e-podman}" DEFAULT_WORKLOAD_MANIFEST="${ROOT}/e2e/gpu/images/.build/workloads.yaml" -cargo build -p openshell-cli - if [ "${E2E_TEST}" = "gpu" ] && [ -z "${OPENSHELL_E2E_WORKLOAD_MANIFEST:-}" ] && [ ! -f "${DEFAULT_WORKLOAD_MANIFEST}" ]; then echo "note: running Podman GPU e2e without a workload manifest; workload validation will log an explicit skip. Build one with 'CONTAINER_ENGINE=podman mise run e2e:workloads:build' or set OPENSHELL_E2E_WORKLOAD_MANIFEST." fi diff --git a/e2e/rust/src/harness/binary.rs b/e2e/rust/src/harness/binary.rs index 13b8fa4515..4cf87c4c87 100644 --- a/e2e/rust/src/harness/binary.rs +++ b/e2e/rust/src/harness/binary.rs @@ -3,9 +3,9 @@ //! CLI binary resolution for e2e tests. //! -//! Resolves the `openshell` binary at `/target/debug/openshell`. -//! The binary must already be built — the `e2e:rust` mise task handles -//! this by running `cargo build -p openshell-cli` before the tests. +//! Resolves the `openshell` binary from `OPENSHELL_BIN`, or from +//! `/target/debug/openshell` for local runs. The local binary must +//! already be built — the E2E mise tasks do this before running the tests. use std::path::{Path, PathBuf}; @@ -22,17 +22,20 @@ fn workspace_root() -> PathBuf { /// Return the path to the `openshell` CLI binary. /// -/// Expects the binary at `/target/debug/openshell`. +/// Uses `OPENSHELL_BIN` when set, otherwise expects the binary at +/// `/target/debug/openshell`. /// /// # Panics /// -/// Panics if the binary is not found. Run `cargo build -p openshell-cli` -/// (or `mise run e2e:rust`) first. +/// Panics if the configured or locally-built binary is not found. pub fn openshell_bin() -> PathBuf { - let bin = workspace_root().join("target/debug/openshell"); + let bin = std::env::var_os("OPENSHELL_BIN").map_or_else( + || workspace_root().join("target/debug/openshell"), + PathBuf::from, + ); assert!( bin.is_file(), - "openshell binary not found at {} — run `cargo build -p openshell-cli` first", + "openshell binary not found at {} — set OPENSHELL_BIN or run `cargo build -p openshell-cli` first", bin.display() ); bin diff --git a/e2e/support/gateway-common.sh b/e2e/support/gateway-common.sh index a52b4c0770..adc0af8d3a 100644 --- a/e2e/support/gateway-common.sh +++ b/e2e/support/gateway-common.sh @@ -182,22 +182,26 @@ e2e_build_gateway_binaries() { target_dir="$(e2e_cargo_target_dir "${root}")" printf -v "${target_var}" '%s' "${target_dir}" printf -v "${gateway_var}" '%s' "${target_dir}/debug/openshell-gateway" - printf -v "${cli_var}" '%s' "${target_dir}/debug/openshell" + printf -v "${cli_var}" '%s' "${OPENSHELL_BIN:-${target_dir}/debug/openshell}" echo "Building openshell-gateway..." cargo build "${jobs[@]}" \ -p openshell-server --bin openshell-gateway - echo "Building openshell-cli..." - cargo build "${jobs[@]}" \ - -p openshell-cli + if [ -z "${OPENSHELL_BIN:-}" ]; then + echo "Building openshell-cli..." + cargo build "${jobs[@]}" \ + -p openshell-cli + else + echo "Using prebuilt openshell CLI at ${OPENSHELL_BIN}" + fi if [ ! -x "${target_dir}/debug/openshell-gateway" ]; then echo "ERROR: expected openshell-gateway binary at ${target_dir}/debug/openshell-gateway" >&2 exit 1 fi - if [ ! -x "${target_dir}/debug/openshell" ]; then - echo "ERROR: expected openshell CLI binary at ${target_dir}/debug/openshell" >&2 + if [ ! -x "${!cli_var}" ]; then + echo "ERROR: expected openshell CLI binary at ${!cli_var}" >&2 exit 1 fi } diff --git a/tasks/test.toml b/tasks/test.toml index a75b05cce2..fa6779f6c4 100644 --- a/tasks/test.toml +++ b/tasks/test.toml @@ -60,14 +60,12 @@ hide = true ["e2e:rust"] description = "Run Rust CLI e2e tests against a Docker-backed gateway" run = [ - "cargo build -p openshell-cli", "e2e/with-docker-gateway.sh cargo test --manifest-path e2e/rust/Cargo.toml --features e2e-docker", ] ["e2e:websocket-conformance"] description = "Run focused WebSocket conformance e2e tests against a Docker-backed gateway" run = [ - "cargo build -p openshell-cli", "e2e/with-docker-gateway.sh cargo test --manifest-path e2e/rust/Cargo.toml --features e2e-docker --test websocket_conformance", ] From 706e6a500ca9d784a114ca564c0649e8d5dc4164 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Thu, 16 Jul 2026 00:23:08 +0200 Subject: [PATCH 2/3] ci(e2e): reuse prebuilt gateway artifacts Signed-off-by: Evan Lezar --- .github/actions/setup-e2e-gateway/action.yml | 29 ++++++++++++++++++++ .github/workflows/branch-e2e.yml | 10 +++++-- .github/workflows/e2e-gpu-test.yaml | 11 ++++++++ .github/workflows/e2e-test.yml | 17 ++++++++++++ CI.md | 2 +- e2e/support/gateway-common.sh | 16 +++++++---- 6 files changed, 75 insertions(+), 10 deletions(-) create mode 100644 .github/actions/setup-e2e-gateway/action.yml diff --git a/.github/actions/setup-e2e-gateway/action.yml b/.github/actions/setup-e2e-gateway/action.yml new file mode 100644 index 0000000000..b0ee52392b --- /dev/null +++ b/.github/actions/setup-e2e-gateway/action.yml @@ -0,0 +1,29 @@ +name: Setup E2E Gateway +description: Download an architecture-matched prebuilt OpenShell gateway for E2E tests + +inputs: + artifact-prefix: + description: Artifact name prefix; linux- is appended automatically + required: true + +runs: + using: composite + steps: + - name: Download prebuilt gateway + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ format('{0}-linux-{1}', inputs.artifact-prefix, runner.arch == 'X64' && 'amd64' || 'arm64') }} + path: .e2e/prebuilt-gateway + + - name: Configure prebuilt gateway + shell: bash + run: | + set -euo pipefail + gateway="$GITHUB_WORKSPACE/.e2e/prebuilt-gateway/openshell-gateway" + if [[ ! -f "$gateway" ]]; then + echo "downloaded artifact is missing $gateway" >&2 + exit 1 + fi + chmod +x "$gateway" + "$gateway" --version + echo "OPENSHELL_GATEWAY_BIN=$gateway" >> "$GITHUB_ENV" diff --git a/.github/workflows/branch-e2e.yml b/.github/workflows/branch-e2e.yml index 1316fc524e..1d56d3338a 100644 --- a/.github/workflows/branch-e2e.yml +++ b/.github/workflows/branch-e2e.yml @@ -73,7 +73,7 @@ jobs: build-gateway: needs: [pr_metadata] - if: needs.pr_metadata.outputs.should_run == 'true' && (needs.pr_metadata.outputs.run_core_e2e == 'true' || needs.pr_metadata.outputs.run_kubernetes_ha_e2e == 'true') + if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_any_e2e == 'true' permissions: contents: read packages: write @@ -116,9 +116,10 @@ jobs: image-tag: ${{ github.sha }} runner: linux-arm64-cpu8 cli-artifact-prefix: rust-binary-cli + gateway-artifact-prefix: rust-binary-gateway gpu-e2e: - needs: [pr_metadata, build-supervisor, build-cli] + needs: [pr_metadata, build-gateway, build-supervisor, build-cli] if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_gpu_e2e == 'true' permissions: actions: read @@ -128,6 +129,7 @@ jobs: with: image-tag: ${{ github.sha }} cli-artifact-prefix: rust-binary-cli + gateway-artifact-prefix: rust-binary-gateway kubernetes-e2e: needs: [pr_metadata, build-gateway, build-supervisor, build-cli] @@ -208,12 +210,13 @@ jobs: gpu-e2e-result: name: GPU E2E result - needs: [pr_metadata, build-supervisor, build-cli, gpu-e2e] + needs: [pr_metadata, build-gateway, build-supervisor, build-cli, gpu-e2e] if: always() && needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_gpu_e2e == 'true' runs-on: ubuntu-latest steps: - name: Verify GPU E2E jobs env: + BUILD_GATEWAY_RESULT: ${{ needs.build-gateway.result }} BUILD_SUPERVISOR_RESULT: ${{ needs.build-supervisor.result }} BUILD_CLI_RESULT: ${{ needs.build-cli.result }} GPU_E2E_RESULT: ${{ needs.gpu-e2e.result }} @@ -221,6 +224,7 @@ jobs: set -euo pipefail failed=0 for item in \ + "build-gateway:$BUILD_GATEWAY_RESULT" \ "build-supervisor:$BUILD_SUPERVISOR_RESULT" \ "build-cli:$BUILD_CLI_RESULT" \ "gpu-e2e:$GPU_E2E_RESULT"; do diff --git a/.github/workflows/e2e-gpu-test.yaml b/.github/workflows/e2e-gpu-test.yaml index 0be2f89a39..3695764dfb 100644 --- a/.github/workflows/e2e-gpu-test.yaml +++ b/.github/workflows/e2e-gpu-test.yaml @@ -12,6 +12,11 @@ on: required: false type: string default: "" + gateway-artifact-prefix: + description: "Optional prebuilt gateway artifact prefix (artifact suffix is linux-)" + required: false + type: string + default: "" permissions: actions: read @@ -70,6 +75,12 @@ jobs: with: artifact-prefix: ${{ inputs.cli-artifact-prefix }} + - name: Use prebuilt OpenShell gateway + if: inputs.gateway-artifact-prefix != '' + uses: ./.github/actions/setup-e2e-gateway + with: + artifact-prefix: ${{ inputs.gateway-artifact-prefix }} + - name: Log in to GHCR run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index c58f738686..cd7f7aa1ea 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -22,6 +22,11 @@ on: required: false type: string default: "" + gateway-artifact-prefix: + description: "Optional prebuilt gateway artifact prefix (artifact suffix is linux-)" + required: false + type: string + default: "" permissions: actions: read @@ -75,6 +80,12 @@ jobs: with: artifact-prefix: ${{ inputs.cli-artifact-prefix }} + - name: Use prebuilt OpenShell gateway + if: inputs.gateway-artifact-prefix != '' + uses: ./.github/actions/setup-e2e-gateway + with: + artifact-prefix: ${{ inputs.gateway-artifact-prefix }} + - name: Check out MCP conformance tests if: matrix.suite == 'mcp' uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -144,6 +155,12 @@ jobs: with: artifact-prefix: ${{ inputs.cli-artifact-prefix }} + - name: Use prebuilt OpenShell gateway + if: inputs.gateway-artifact-prefix != '' + uses: ./.github/actions/setup-e2e-gateway + with: + artifact-prefix: ${{ inputs.gateway-artifact-prefix }} + - name: Install mise run: | curl https://mise.run | MISE_VERSION=v2026.4.25 sh diff --git a/CI.md b/CI.md index c98b049aba..8949e101b6 100644 --- a/CI.md +++ b/CI.md @@ -20,7 +20,7 @@ Three opt-in labels enable the long-running E2E suites: - `test:e2e-kubernetes` runs Kubernetes E2E with the HA Helm overlay (`replicaCount: 2` and bundled PostgreSQL) in `Branch E2E Checks` -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. Rust and MCP E2E jobs reuse the matching prebuilt CLI instead of compiling another debug CLI in each job. +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. 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. The GitHub ruleset should require the `OpenShell / ...` statuses published by `Required CI Gates`, not the push-triggered workflow jobs directly. diff --git a/e2e/support/gateway-common.sh b/e2e/support/gateway-common.sh index adc0af8d3a..6b9e6a0956 100644 --- a/e2e/support/gateway-common.sh +++ b/e2e/support/gateway-common.sh @@ -181,12 +181,16 @@ e2e_build_gateway_binaries() { target_dir="$(e2e_cargo_target_dir "${root}")" printf -v "${target_var}" '%s' "${target_dir}" - printf -v "${gateway_var}" '%s' "${target_dir}/debug/openshell-gateway" + printf -v "${gateway_var}" '%s' "${OPENSHELL_GATEWAY_BIN:-${target_dir}/debug/openshell-gateway}" printf -v "${cli_var}" '%s' "${OPENSHELL_BIN:-${target_dir}/debug/openshell}" - echo "Building openshell-gateway..." - cargo build "${jobs[@]}" \ - -p openshell-server --bin openshell-gateway + if [ -z "${OPENSHELL_GATEWAY_BIN:-}" ]; then + echo "Building openshell-gateway..." + cargo build "${jobs[@]}" \ + -p openshell-server --bin openshell-gateway + else + echo "Using prebuilt openshell gateway at ${OPENSHELL_GATEWAY_BIN}" + fi if [ -z "${OPENSHELL_BIN:-}" ]; then echo "Building openshell-cli..." @@ -196,8 +200,8 @@ e2e_build_gateway_binaries() { echo "Using prebuilt openshell CLI at ${OPENSHELL_BIN}" fi - if [ ! -x "${target_dir}/debug/openshell-gateway" ]; then - echo "ERROR: expected openshell-gateway binary at ${target_dir}/debug/openshell-gateway" >&2 + if [ ! -x "${!gateway_var}" ]; then + echo "ERROR: expected openshell-gateway binary at ${!gateway_var}" >&2 exit 1 fi if [ ! -x "${!cli_var}" ]; then From ad3c92fc1e4f7df84b4f629ff06b5007fbc17a77 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Fri, 17 Jul 2026 15:48:27 +0200 Subject: [PATCH 3/3] ci(e2e): reuse prebuilt VM driver artifact Signed-off-by: Evan Lezar --- .../actions/setup-e2e-vm-driver/action.yml | 38 ++++++++++ .github/workflows/branch-e2e.yml | 21 ++++- .github/workflows/driver-vm-linux.yml | 52 ++++++++----- .github/workflows/e2e-test.yml | 23 ++++++ CI.md | 2 +- e2e/rust/e2e-vm.sh | 76 ++++++++++++++----- 6 files changed, 173 insertions(+), 39 deletions(-) create mode 100644 .github/actions/setup-e2e-vm-driver/action.yml diff --git a/.github/actions/setup-e2e-vm-driver/action.yml b/.github/actions/setup-e2e-vm-driver/action.yml new file mode 100644 index 0000000000..20c43cf98f --- /dev/null +++ b/.github/actions/setup-e2e-vm-driver/action.yml @@ -0,0 +1,38 @@ +name: Setup E2E VM Driver +description: Download a prebuilt OpenShell VM driver for E2E tests + +inputs: + artifact-name: + description: Artifact name to download, such as driver-vm-linux-amd64 + required: true + +runs: + using: composite + steps: + - name: Download prebuilt VM driver + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ inputs.artifact-name }} + path: .e2e/prebuilt-vm-driver + + - name: Configure prebuilt VM driver + shell: bash + run: | + set -euo pipefail + extract_dir="$GITHUB_WORKSPACE/.e2e/prebuilt-vm-driver/bin" + mkdir -p "$extract_dir" + archive="$(find "$GITHUB_WORKSPACE/.e2e/prebuilt-vm-driver" -maxdepth 1 -type f -name 'openshell-driver-vm-*.tar.gz' -print -quit)" + if [[ -z "$archive" ]]; then + echo "downloaded artifact is missing openshell-driver-vm-*.tar.gz" >&2 + find "$GITHUB_WORKSPACE/.e2e/prebuilt-vm-driver" -maxdepth 2 -type f -print >&2 || true + exit 1 + fi + tar -xzf "$archive" -C "$extract_dir" + driver="$extract_dir/openshell-driver-vm" + if [[ ! -f "$driver" ]]; then + echo "downloaded artifact is missing $driver" >&2 + exit 1 + fi + chmod +x "$driver" + "$driver" --version + echo "OPENSHELL_VM_DRIVER_BIN=$driver" >> "$GITHUB_ENV" diff --git a/.github/workflows/branch-e2e.yml b/.github/workflows/branch-e2e.yml index 1d56d3338a..17aa068581 100644 --- a/.github/workflows/branch-e2e.yml +++ b/.github/workflows/branch-e2e.yml @@ -104,8 +104,22 @@ jobs: component: cli platform: linux/amd64,linux/arm64 + build-driver-vm-linux: + needs: [pr_metadata] + if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true' + permissions: + contents: read + packages: read + uses: ./.github/workflows/driver-vm-linux.yml + with: + image-tag: ${{ github.sha }} + checkout-ref: ${{ github.sha }} + runtime-platforms: linux-x86_64 + driver-targets: >- + [{"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"}] + e2e: - needs: [pr_metadata, build-gateway, build-supervisor, build-cli] + needs: [pr_metadata, build-gateway, build-supervisor, build-cli, build-driver-vm-linux] if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true' permissions: actions: read @@ -117,6 +131,7 @@ jobs: runner: linux-arm64-cpu8 cli-artifact-prefix: rust-binary-cli gateway-artifact-prefix: rust-binary-gateway + vm-driver-artifact-name: driver-vm-linux-amd64 gpu-e2e: needs: [pr_metadata, build-gateway, build-supervisor, build-cli] @@ -179,7 +194,7 @@ jobs: core-e2e-result: name: Core E2E result - needs: [pr_metadata, build-gateway, build-supervisor, build-cli, e2e, kubernetes-e2e] + needs: [pr_metadata, build-gateway, build-supervisor, build-cli, build-driver-vm-linux, e2e, kubernetes-e2e] if: always() && needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true' runs-on: ubuntu-latest steps: @@ -188,6 +203,7 @@ jobs: BUILD_GATEWAY_RESULT: ${{ needs.build-gateway.result }} BUILD_SUPERVISOR_RESULT: ${{ needs.build-supervisor.result }} BUILD_CLI_RESULT: ${{ needs.build-cli.result }} + BUILD_DRIVER_VM_RESULT: ${{ needs.build-driver-vm-linux.result }} E2E_RESULT: ${{ needs.e2e.result }} KUBERNETES_E2E_RESULT: ${{ needs.kubernetes-e2e.result }} run: | @@ -197,6 +213,7 @@ jobs: "build-gateway:$BUILD_GATEWAY_RESULT" \ "build-supervisor:$BUILD_SUPERVISOR_RESULT" \ "build-cli:$BUILD_CLI_RESULT" \ + "build-driver-vm-linux:$BUILD_DRIVER_VM_RESULT" \ "e2e:$E2E_RESULT" \ "kubernetes-e2e:$KUBERNETES_E2E_RESULT"; do name="${item%%:*}" diff --git a/.github/workflows/driver-vm-linux.yml b/.github/workflows/driver-vm-linux.yml index 7b41dc70ca..7a15271d05 100644 --- a/.github/workflows/driver-vm-linux.yml +++ b/.github/workflows/driver-vm-linux.yml @@ -4,14 +4,26 @@ on: workflow_call: inputs: cargo-version: - required: true + required: false type: string + default: "" image-tag: required: true type: string checkout-ref: required: true type: string + runtime-platforms: + description: "Comma-separated VM runtime platforms to download: linux-aarch64,linux-x86_64" + required: false + type: string + default: "linux-aarch64,linux-x86_64" + driver-targets: + description: "JSON matrix entries for Linux VM driver targets" + required: false + type: string + default: >- + [{"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"}] permissions: contents: read @@ -39,11 +51,29 @@ jobs: - name: Download Linux runtime tarballs env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RUNTIME_PLATFORMS: ${{ inputs['runtime-platforms'] }} run: | set -euo pipefail mkdir -p runtime-artifacts - for platform in linux-aarch64 linux-x86_64; do + runtime_platforms="${RUNTIME_PLATFORMS//[[:space:]]/}" + IFS=',' read -r -a platforms <<< "$runtime_platforms" + if [ "${#platforms[@]}" -eq 0 ]; then + echo "::error::No VM runtime platforms requested" + exit 1 + fi + for platform in "${platforms[@]}"; do + case "$platform" in + linux-aarch64 | linux-x86_64) ;; + *) + echo "::error::Unsupported VM runtime platform '$platform' in runtime-platforms" + exit 1 + ;; + esac + done + printf '%s\n' "${platforms[@]}" > runtime-artifacts/platforms.txt + + for platform in "${platforms[@]}"; do asset="vm-runtime-${platform}.tar.zst" echo "Downloading ${asset}..." if ! gh release download vm-runtime \ @@ -61,9 +91,9 @@ jobs: - name: Verify downloads run: | set -euo pipefail - for platform in linux-aarch64 linux-x86_64; do + while IFS= read -r platform; do test -f "runtime-artifacts/vm-runtime-${platform}.tar.zst" - done + done < runtime-artifacts/platforms.txt - name: Upload runtime artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 @@ -77,19 +107,7 @@ jobs: needs: [download-kernel-runtime] strategy: matrix: - include: - - 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 + include: ${{ fromJSON(inputs['driver-targets']) }} runs-on: ${{ matrix.runner }} timeout-minutes: 30 container: diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index cd7f7aa1ea..561b6c84d6 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -27,6 +27,11 @@ on: required: false type: string default: "" + vm-driver-artifact-name: + description: "Optional prebuilt VM driver artifact name" + required: false + type: string + default: "" permissions: actions: read @@ -257,6 +262,24 @@ jobs: ref: ${{ inputs['checkout-ref'] || github.sha }} persist-credentials: false + - name: Use prebuilt OpenShell CLI + if: inputs.cli-artifact-prefix != '' + uses: ./.github/actions/setup-e2e-cli + with: + artifact-prefix: ${{ inputs.cli-artifact-prefix }} + + - name: Use prebuilt OpenShell gateway + if: inputs.gateway-artifact-prefix != '' + uses: ./.github/actions/setup-e2e-gateway + with: + artifact-prefix: ${{ inputs.gateway-artifact-prefix }} + + - name: Use prebuilt OpenShell VM driver + if: inputs.vm-driver-artifact-name != '' + uses: ./.github/actions/setup-e2e-vm-driver + with: + artifact-name: ${{ inputs.vm-driver-artifact-name }} + - name: Enable KVM access run: | set -euo pipefail diff --git a/CI.md b/CI.md index 8949e101b6..2eb3da7571 100644 --- a/CI.md +++ b/CI.md @@ -20,7 +20,7 @@ Three opt-in labels enable the long-running E2E suites: - `test:e2e-kubernetes` runs Kubernetes E2E with the HA Helm overlay (`replicaCount: 2` and bundled PostgreSQL) in `Branch E2E Checks` -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. +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. 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. The GitHub ruleset should require the `OpenShell / ...` statuses published by `Required CI Gates`, not the push-triggered workflow jobs directly. diff --git a/e2e/rust/e2e-vm.sh b/e2e/rust/e2e-vm.sh index 8005d09bd0..584c7b91cd 100755 --- a/e2e/rust/e2e-vm.sh +++ b/e2e/rust/e2e-vm.sh @@ -23,9 +23,12 @@ # mise run e2e:vm # # What the script does: -# 1. Ensures the VM runtime (libkrun + gvproxy) and bundled supervisor are staged. +# 1. When no prebuilt VM driver is supplied, ensures the VM runtime +# (libkrun + gvproxy) and bundled supervisor are staged. # 2. Builds `openshell-gateway`, `openshell-driver-vm`, and the -# `openshell` CLI with the embedded runtime. +# `openshell` CLI with the embedded runtime as needed. When CI supplies +# OPENSHELL_GATEWAY_BIN, OPENSHELL_VM_DRIVER_BIN, or OPENSHELL_BIN, the +# matching prebuilt binary is reused instead of rebuilt. # 3. On macOS, codesigns the VM driver (libkrun needs the # `com.apple.security.hypervisor` entitlement). # 4. Writes a per-run gateway config with `[openshell.drivers.vm]` @@ -35,7 +38,7 @@ # 5. Tears the gateway down and (on failure) preserves the gateway # log and every VM serial console log for post-mortem. # -# Prerequisites (handled automatically by this script if missing): +# Prerequisites (handled automatically by this script for local VM-driver builds): # - `mise run vm:setup` — downloads / builds the libkrun runtime. # - `mise run vm:supervisor` — builds the bundled sandbox supervisor. @@ -45,8 +48,9 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" source "${ROOT}/e2e/support/gateway-common.sh" COMPRESSED_DIR="${ROOT}/target/vm-runtime-compressed" -GATEWAY_BIN="${ROOT}/target/debug/openshell-gateway" -DRIVER_BIN="${ROOT}/target/debug/openshell-driver-vm" +GATEWAY_BIN="${OPENSHELL_GATEWAY_BIN:-${ROOT}/target/debug/openshell-gateway}" +DRIVER_BIN="${OPENSHELL_VM_DRIVER_BIN:-${ROOT}/target/debug/openshell-driver-vm}" +CLI_BIN="${OPENSHELL_BIN:-${ROOT}/target/debug/openshell}" E2E_TEST_OVERRIDE="${OPENSHELL_E2E_VM_TEST:-}" E2E_FEATURES="${OPENSHELL_E2E_VM_FEATURES:-e2e-vm}" @@ -73,25 +77,59 @@ if [ -n "${RUSTC_WRAPPER:-}" ] && [ "${OPENSHELL_E2E_VM_ALLOW_RUSTC_WRAPPER:-0}" unset RUSTC_WRAPPER fi -mkdir -p "${COMPRESSED_DIR}" +if [ -z "${OPENSHELL_VM_DRIVER_BIN:-}" ]; then + mkdir -p "${COMPRESSED_DIR}" -if ! find "${COMPRESSED_DIR}" -maxdepth 1 -name 'libkrun*.zst' | grep -q .; then - echo "==> Preparing embedded VM runtime (mise run vm:setup)" - mise run vm:setup + if ! find "${COMPRESSED_DIR}" -maxdepth 1 -name 'libkrun*.zst' | grep -q .; then + echo "==> Preparing embedded VM runtime (mise run vm:setup)" + mise run vm:setup + fi + + if [ ! -f "${COMPRESSED_DIR}/openshell-sandbox.zst" ]; then + echo "==> Building bundled VM supervisor (mise run vm:supervisor)" + mise run vm:supervisor + fi + + export OPENSHELL_VM_RUNTIME_COMPRESSED_DIR="${OPENSHELL_VM_RUNTIME_COMPRESSED_DIR:-${COMPRESSED_DIR}}" +else + echo "==> Skipping embedded VM runtime prep; prebuilt VM driver was supplied" fi -if [ ! -f "${COMPRESSED_DIR}/openshell-sandbox.zst" ]; then - echo "==> Building bundled VM supervisor (mise run vm:supervisor)" - mise run vm:supervisor +build_packages=() +if [ -z "${OPENSHELL_GATEWAY_BIN:-}" ]; then + build_packages+=(-p openshell-server) +else + echo "==> Using prebuilt openshell-gateway at ${GATEWAY_BIN}" +fi +if [ -z "${OPENSHELL_VM_DRIVER_BIN:-}" ]; then + build_packages+=(-p openshell-driver-vm) +else + echo "==> Using prebuilt openshell-driver-vm at ${DRIVER_BIN}" +fi +if [ -z "${OPENSHELL_BIN:-}" ]; then + build_packages+=(-p openshell-cli) +else + echo "==> Using prebuilt openshell CLI at ${CLI_BIN}" fi -export OPENSHELL_VM_RUNTIME_COMPRESSED_DIR="${OPENSHELL_VM_RUNTIME_COMPRESSED_DIR:-${COMPRESSED_DIR}}" +if [ "${#build_packages[@]}" -gt 0 ]; then + echo "==> Building ${build_packages[*]}" + cargo build "${build_packages[@]}" +fi -echo "==> Building openshell-gateway, openshell-driver-vm, openshell (CLI)" -cargo build \ - -p openshell-server \ - -p openshell-driver-vm \ - -p openshell-cli +for pair in \ + "openshell-gateway:${GATEWAY_BIN}" \ + "openshell-driver-vm:${DRIVER_BIN}" \ + "openshell CLI:${CLI_BIN}"; do + label="${pair%%:*}" + path="${pair#*:}" + if [ ! -x "${path}" ]; then + echo "ERROR: expected ${label} binary at ${path}" >&2 + exit 1 + fi +done +export OPENSHELL_BIN="${CLI_BIN}" +DRIVER_DIR="$(dirname "${DRIVER_BIN}")" if [ "$(uname -s)" = "Darwin" ]; then echo "==> Codesigning openshell-driver-vm (Hypervisor entitlement)" @@ -224,7 +262,7 @@ ttl_secs = 0 [openshell.drivers.vm] grpc_endpoint = "https://host.openshell.internal:${HOST_PORT}" -driver_dir = "${ROOT}/target/debug" +driver_dir = "${DRIVER_DIR}" state_dir = "${RUN_STATE_DIR}" guest_tls_ca = "${PKI_DIR}/ca.crt" guest_tls_cert = "${PKI_DIR}/client/tls.crt"