Skip to content

Commit 5cd379d

Browse files
committed
ci(e2e): reuse prebuilt CLI artifacts
Signed-off-by: Evan Lezar <elezar@nvidia.com>
1 parent d556748 commit 5cd379d

13 files changed

Lines changed: 124 additions & 32 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Setup E2E CLI
2+
description: Download an architecture-matched prebuilt OpenShell CLI for E2E tests
3+
4+
inputs:
5+
artifact-prefix:
6+
description: Artifact name prefix; linux-<arch> is appended automatically
7+
required: true
8+
9+
runs:
10+
using: composite
11+
steps:
12+
- name: Download prebuilt CLI
13+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
14+
with:
15+
name: ${{ format('{0}-linux-{1}', inputs.artifact-prefix, runner.arch == 'X64' && 'amd64' || 'arm64') }}
16+
path: .e2e/prebuilt-cli
17+
18+
- name: Configure prebuilt CLI
19+
shell: bash
20+
run: |
21+
set -euo pipefail
22+
cli="$GITHUB_WORKSPACE/.e2e/prebuilt-cli/openshell"
23+
if [[ ! -f "$cli" ]]; then
24+
echo "downloaded artifact is missing $cli" >&2
25+
exit 1
26+
fi
27+
chmod +x "$cli"
28+
"$cli" --version
29+
echo "OPENSHELL_BIN=$cli" >> "$GITHUB_ENV"

.github/workflows/branch-e2e.yml

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,38 +95,42 @@ jobs:
9595

9696
build-cli:
9797
needs: [pr_metadata]
98-
if: needs.pr_metadata.outputs.should_run == 'true'
98+
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_any_e2e == 'true'
9999
permissions:
100100
contents: read
101101
packages: write
102102
uses: ./.github/workflows/docker-build.yml
103103
with:
104104
component: cli
105-
platform: linux/amd64
105+
platform: linux/amd64,linux/arm64
106106

107107
e2e:
108-
needs: [pr_metadata, build-gateway, build-supervisor]
108+
needs: [pr_metadata, build-gateway, build-supervisor, build-cli]
109109
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
110110
permissions:
111+
actions: read
111112
contents: read
112113
packages: read
113114
uses: ./.github/workflows/e2e-test.yml
114115
with:
115116
image-tag: ${{ github.sha }}
116117
runner: linux-arm64-cpu8
118+
cli-artifact-prefix: rust-binary-cli
117119

118120
gpu-e2e:
119-
needs: [pr_metadata, build-supervisor]
121+
needs: [pr_metadata, build-supervisor, build-cli]
120122
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_gpu_e2e == 'true'
121123
permissions:
124+
actions: read
122125
contents: read
123126
packages: read
124127
uses: ./.github/workflows/e2e-gpu-test.yaml
125128
with:
126129
image-tag: ${{ github.sha }}
130+
cli-artifact-prefix: rust-binary-cli
127131

128132
kubernetes-e2e:
129-
needs: [pr_metadata, build-gateway, build-supervisor]
133+
needs: [pr_metadata, build-gateway, build-supervisor, build-cli]
130134
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
131135
strategy:
132136
fail-fast: false
@@ -145,6 +149,7 @@ jobs:
145149
topology: sidecar
146150
extra_helm_values: deploy/helm/openshell/ci/values-sidecar.yaml
147151
permissions:
152+
actions: read
148153
contents: read
149154
packages: read
150155
uses: ./.github/workflows/e2e-kubernetes-test.yml
@@ -153,11 +158,13 @@ jobs:
153158
job-name: Kubernetes E2E (Rust smoke, ${{ matrix.topology }}, Agent Sandbox ${{ matrix.agent_sandbox_api }})
154159
agent-sandbox-version: ${{ matrix.agent_sandbox_version }}
155160
extra-helm-values: ${{ matrix.extra_helm_values }}
161+
cli-artifact-prefix: rust-binary-cli
156162

157163
kubernetes-ha-e2e:
158-
needs: [pr_metadata, build-gateway, build-supervisor]
164+
needs: [pr_metadata, build-gateway, build-supervisor, build-cli]
159165
if: needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_kubernetes_ha_e2e == 'true'
160166
permissions:
167+
actions: read
161168
contents: read
162169
packages: read
163170
uses: ./.github/workflows/e2e-kubernetes-test.yml
@@ -166,17 +173,19 @@ jobs:
166173
job-name: Kubernetes HA E2E (Rust smoke)
167174
extra-helm-values: deploy/helm/openshell/ci/values-high-availability.yaml
168175
external-postgres-secret: openshell-ha-pg
176+
cli-artifact-prefix: rust-binary-cli
169177

170178
core-e2e-result:
171179
name: Core E2E result
172-
needs: [pr_metadata, build-gateway, build-supervisor, e2e, kubernetes-e2e]
180+
needs: [pr_metadata, build-gateway, build-supervisor, build-cli, e2e, kubernetes-e2e]
173181
if: always() && needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_core_e2e == 'true'
174182
runs-on: ubuntu-latest
175183
steps:
176184
- name: Verify core E2E jobs
177185
env:
178186
BUILD_GATEWAY_RESULT: ${{ needs.build-gateway.result }}
179187
BUILD_SUPERVISOR_RESULT: ${{ needs.build-supervisor.result }}
188+
BUILD_CLI_RESULT: ${{ needs.build-cli.result }}
180189
E2E_RESULT: ${{ needs.e2e.result }}
181190
KUBERNETES_E2E_RESULT: ${{ needs.kubernetes-e2e.result }}
182191
run: |
@@ -185,6 +194,7 @@ jobs:
185194
for item in \
186195
"build-gateway:$BUILD_GATEWAY_RESULT" \
187196
"build-supervisor:$BUILD_SUPERVISOR_RESULT" \
197+
"build-cli:$BUILD_CLI_RESULT" \
188198
"e2e:$E2E_RESULT" \
189199
"kubernetes-e2e:$KUBERNETES_E2E_RESULT"; do
190200
name="${item%%:*}"
@@ -198,19 +208,21 @@ jobs:
198208
199209
gpu-e2e-result:
200210
name: GPU E2E result
201-
needs: [pr_metadata, build-supervisor, gpu-e2e]
211+
needs: [pr_metadata, build-supervisor, build-cli, gpu-e2e]
202212
if: always() && needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_gpu_e2e == 'true'
203213
runs-on: ubuntu-latest
204214
steps:
205215
- name: Verify GPU E2E jobs
206216
env:
207217
BUILD_SUPERVISOR_RESULT: ${{ needs.build-supervisor.result }}
218+
BUILD_CLI_RESULT: ${{ needs.build-cli.result }}
208219
GPU_E2E_RESULT: ${{ needs.gpu-e2e.result }}
209220
run: |
210221
set -euo pipefail
211222
failed=0
212223
for item in \
213224
"build-supervisor:$BUILD_SUPERVISOR_RESULT" \
225+
"build-cli:$BUILD_CLI_RESULT" \
214226
"gpu-e2e:$GPU_E2E_RESULT"; do
215227
name="${item%%:*}"
216228
result="${item#*:}"
@@ -223,21 +235,23 @@ jobs:
223235
224236
kubernetes-ha-e2e-result:
225237
name: Kubernetes HA E2E result
226-
needs: [pr_metadata, build-gateway, build-supervisor, kubernetes-ha-e2e]
238+
needs: [pr_metadata, build-gateway, build-supervisor, build-cli, kubernetes-ha-e2e]
227239
if: always() && needs.pr_metadata.outputs.should_run == 'true' && needs.pr_metadata.outputs.run_kubernetes_ha_e2e == 'true'
228240
runs-on: ubuntu-latest
229241
steps:
230242
- name: Verify Kubernetes HA E2E jobs
231243
env:
232244
BUILD_GATEWAY_RESULT: ${{ needs.build-gateway.result }}
233245
BUILD_SUPERVISOR_RESULT: ${{ needs.build-supervisor.result }}
246+
BUILD_CLI_RESULT: ${{ needs.build-cli.result }}
234247
KUBERNETES_HA_E2E_RESULT: ${{ needs.kubernetes-ha-e2e.result }}
235248
run: |
236249
set -euo pipefail
237250
failed=0
238251
for item in \
239252
"build-gateway:$BUILD_GATEWAY_RESULT" \
240253
"build-supervisor:$BUILD_SUPERVISOR_RESULT" \
254+
"build-cli:$BUILD_CLI_RESULT" \
241255
"kubernetes-ha-e2e:$KUBERNETES_HA_E2E_RESULT"; do
242256
name="${item%%:*}"
243257
result="${item#*:}"

.github/workflows/docker-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ jobs:
154154
echo "arches=${arches[*]}"
155155
echo "binary_component=$binary_component"
156156
echo "binary_name=$binary_name"
157-
echo "artifact_prefix=rust-binary-${component}-${binary_component}"
157+
echo "artifact_prefix=rust-binary-${component}"
158158
echo "image_tag_base=$image_tag_base"
159159
echo "features=$features"
160160
echo "has_image=$has_image"

.github/workflows/e2e-gpu-test.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@ on:
77
description: "Image tag to test (typically the commit SHA)"
88
required: true
99
type: string
10+
cli-artifact-prefix:
11+
description: "Optional prebuilt CLI artifact prefix (artifact suffix is linux-<arch>)"
12+
required: false
13+
type: string
14+
default: ""
1015

1116
permissions:
17+
actions: read
1218
contents: read
1319
packages: read
1420

@@ -58,6 +64,12 @@ jobs:
5864
steps:
5965
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
6066

67+
- name: Use prebuilt OpenShell CLI
68+
if: inputs.cli-artifact-prefix != ''
69+
uses: ./.github/actions/setup-e2e-cli
70+
with:
71+
artifact-prefix: ${{ inputs.cli-artifact-prefix }}
72+
6173
- name: Log in to GHCR
6274
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
6375

.github/workflows/e2e-kubernetes-test.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,14 @@ on:
4242
required: false
4343
type: string
4444
default: "v2026.4.25"
45+
cli-artifact-prefix:
46+
description: "Optional prebuilt CLI artifact prefix (artifact suffix is linux-<arch>)"
47+
required: false
48+
type: string
49+
default: ""
4550

4651
permissions:
52+
actions: read
4753
contents: read
4854
packages: read
4955

@@ -56,6 +62,7 @@ jobs:
5662
runs-on: ${{ inputs.runner }}
5763
timeout-minutes: 60
5864
permissions:
65+
actions: read
5966
contents: read
6067
packages: read
6168
env:
@@ -68,6 +75,12 @@ jobs:
6875
with:
6976
ref: ${{ inputs['checkout-ref'] || github.sha }}
7077

78+
- name: Use prebuilt OpenShell CLI
79+
if: inputs.cli-artifact-prefix != ''
80+
uses: ./.github/actions/setup-e2e-cli
81+
with:
82+
artifact-prefix: ${{ inputs.cli-artifact-prefix }}
83+
7184
- name: Install mise
7285
run: |
7386
curl https://mise.run | MISE_VERSION=v2026.4.25 sh

.github/workflows/e2e-test.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,14 @@ on:
1717
required: false
1818
type: string
1919
default: ""
20+
cli-artifact-prefix:
21+
description: "Optional prebuilt CLI artifact prefix (artifact suffix is linux-<arch>)"
22+
required: false
23+
type: string
24+
default: ""
2025

2126
permissions:
27+
actions: read
2228
contents: read
2329
packages: read
2430

@@ -63,6 +69,12 @@ jobs:
6369
ref: ${{ inputs['checkout-ref'] || github.sha }}
6470
persist-credentials: false
6571

72+
- name: Use prebuilt OpenShell CLI
73+
if: inputs.cli-artifact-prefix != ''
74+
uses: ./.github/actions/setup-e2e-cli
75+
with:
76+
artifact-prefix: ${{ inputs.cli-artifact-prefix }}
77+
6678
- name: Check out MCP conformance tests
6779
if: matrix.suite == 'mcp'
6880
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
@@ -126,6 +138,12 @@ jobs:
126138
ref: ${{ inputs['checkout-ref'] || github.sha }}
127139
persist-credentials: false
128140

141+
- name: Use prebuilt OpenShell CLI
142+
if: inputs.cli-artifact-prefix != ''
143+
uses: ./.github/actions/setup-e2e-cli
144+
with:
145+
artifact-prefix: ${{ inputs.cli-artifact-prefix }}
146+
129147
- name: Install mise
130148
run: |
131149
curl https://mise.run | MISE_VERSION=v2026.4.25 sh

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 and fans out all enabled suites in parallel.
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. Rust and MCP E2E jobs reuse the matching prebuilt CLI instead of compiling another debug CLI in each job.
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-docker.sh

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ E2E_TEST="${OPENSHELL_E2E_DOCKER_TEST:-smoke}"
1313
E2E_FEATURES="${OPENSHELL_E2E_DOCKER_FEATURES:-e2e,e2e-docker}"
1414
DEFAULT_WORKLOAD_MANIFEST="${ROOT}/e2e/gpu/images/.build/workloads.yaml"
1515

16-
cargo build -p openshell-cli
17-
1816
if [ "${E2E_TEST}" = "gpu" ] && [ -z "${OPENSHELL_E2E_WORKLOAD_MANIFEST:-}" ] && [ ! -f "${DEFAULT_WORKLOAD_MANIFEST}" ]; then
1917
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."
2018
fi

e2e/rust/e2e-kubernetes.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
2121

2222
E2E_FEATURES="${OPENSHELL_E2E_KUBERNETES_FEATURES:-e2e,e2e-host-gateway,e2e-kubernetes}"
2323

24-
cargo build -p openshell-cli
24+
# Docker and Podman build their local gateway and CLI together in the shared
25+
# gateway wrapper. Kubernetes consumes published gateway images, so only its
26+
# local CLI needs to be built when CI has not supplied a prebuilt one.
27+
if [ -z "${OPENSHELL_BIN:-}" ]; then
28+
cargo build -p openshell-cli
29+
fi
2530

2631
test_filter=()
2732
if [ -n "${OPENSHELL_E2E_KUBE_TEST:-}" ]; then

e2e/rust/e2e-podman.sh

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ E2E_TEST="${OPENSHELL_E2E_PODMAN_TEST:-}"
1313
E2E_FEATURES="${OPENSHELL_E2E_PODMAN_FEATURES:-e2e-podman}"
1414
DEFAULT_WORKLOAD_MANIFEST="${ROOT}/e2e/gpu/images/.build/workloads.yaml"
1515

16-
cargo build -p openshell-cli
17-
1816
if [ "${E2E_TEST}" = "gpu" ] && [ -z "${OPENSHELL_E2E_WORKLOAD_MANIFEST:-}" ] && [ ! -f "${DEFAULT_WORKLOAD_MANIFEST}" ]; then
1917
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."
2018
fi

0 commit comments

Comments
 (0)