forked from NVIDIA/OpenShell
-
Notifications
You must be signed in to change notification settings - Fork 0
288 lines (261 loc) · 10.5 KB
/
rust-native-build.yml
File metadata and controls
288 lines (261 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
name: Rust Image Binary Build (openshell-gateway / openshell-sandbox)
# Build Rust binaries per Linux architecture before the Docker image build
# consumes them as prebuilt artifacts. Gateway images use GNU-linked binaries
# for the NVIDIA distroless C/C++ runtime; supervisor images use musl/static
# binaries so the final image can remain scratch. Gateway GNU binaries are
# built with an explicit glibc 2.31 floor so image, package, and tarball
# artifacts share the same host portability contract.
on:
workflow_call:
inputs:
component:
description: "Binary component to build (gateway or sandbox)"
required: true
type: string
arch:
description: "Linux architecture to build (amd64 or arm64)"
required: true
type: string
cargo-version:
description: "Pre-computed cargo version (skips internal git-based computation)"
required: false
type: string
default: ""
features:
description: "Cargo features to enable"
required: false
type: string
default: "openshell-core/dev-settings"
retention-days:
description: "Artifact retention period"
required: false
type: number
default: 5
artifact-name:
description: "Artifact name override"
required: false
type: string
default: ""
checkout-ref:
description: "Git ref to check out for build inputs (defaults to the workflow SHA)"
required: false
type: string
default: ""
image-tag:
description: "Supervisor image tag to bake into gateway binaries"
required: false
type: string
default: ""
permissions:
contents: read
packages: read
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: "0"
MISE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Route sccache (already RUSTC_WRAPPER in mise.toml) to the GHA cache
# backend instead of the EKS memcached used by ARC.
SCCACHE_GHA_ENABLED: "true"
defaults:
run:
shell: bash
jobs:
rust-native-build:
name: ${{ inputs.component }} (${{ inputs.arch }})
runs-on: ${{ inputs.arch == 'arm64' && 'linux-arm64-cpu8' || 'linux-amd64-cpu8' }}
timeout-minutes: 60
env:
COMPONENT: ${{ inputs.component }}
ARCH: ${{ inputs.arch }}
FEATURES: ${{ inputs.features }}
# Partition the GHA sccache cache per (component, arch). Without this,
# concurrent jobs collide on the same cache key and later-starting
# writers hit 409 Conflict.
SCCACHE_GHA_VERSION: ${{ inputs.component }}-${{ inputs.arch }}
container:
image: ghcr.io/nvidia/openshell/ci:latest
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: ${{ inputs['checkout-ref'] || github.sha }}
fetch-depth: 0
- name: Mark workspace safe for git
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Fetch tags
run: git fetch --tags --force
- name: Configure GHA sccache backend
# Exposes ACTIONS_CACHE_URL / ACTIONS_RUNTIME_TOKEN before `mise install`
# compiles cargo-installed tools through RUSTC_WRAPPER=sccache.
uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10
- name: Install tools
run: mise install --locked
- name: Resolve build target
id: target
run: |
set -euo pipefail
case "$COMPONENT" in
gateway)
crate=openshell-server
binary=openshell-gateway
zig_target=
;;
sandbox)
crate=openshell-sandbox
binary=openshell-sandbox
zig_target=
;;
*)
echo "unsupported component: $COMPONENT" >&2
exit 1
;;
esac
case "$ARCH" in
amd64)
if [[ "$COMPONENT" == "sandbox" ]]; then
target=x86_64-unknown-linux-musl
zig_target=x86_64-linux-musl
else
target=x86_64-unknown-linux-gnu
zig_target=x86_64-unknown-linux-gnu.2.31
fi
;;
arm64)
if [[ "$COMPONENT" == "sandbox" ]]; then
target=aarch64-unknown-linux-musl
zig_target=aarch64-linux-musl
else
target=aarch64-unknown-linux-gnu
zig_target=aarch64-unknown-linux-gnu.2.31
fi
;;
*)
echo "unsupported arch: $ARCH" >&2
exit 1
;;
esac
{
echo "crate=$crate"
echo "binary=$binary"
echo "target=$target"
echo "zig_target=$zig_target"
} >> "$GITHUB_OUTPUT"
- name: Cache Rust target and registry
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2
with:
shared-key: rust-native-${{ inputs.component }}-${{ inputs.arch }}
cache-directories: .cache/sccache
cache-targets: "true"
- name: Compute cargo version
id: version
run: |
set -euo pipefail
if [[ -n "${{ inputs['cargo-version'] }}" ]]; then
echo "cargo_version=${{ inputs['cargo-version'] }}" >> "$GITHUB_OUTPUT"
else
echo "cargo_version=$(uv run python tasks/scripts/release.py get-version --cargo)" >> "$GITHUB_OUTPUT"
fi
- name: Patch workspace version
if: steps.version.outputs.cargo_version != ''
run: |
set -euo pipefail
sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${{ steps.version.outputs.cargo_version }}"'"/}' Cargo.toml
- name: Set up zig musl wrappers
if: contains(steps.target.outputs.target, 'musl')
run: |
set -euo pipefail
ZIG="$(mise which zig)"
ZIG_TARGET="${{ steps.target.outputs.zig_target }}"
mkdir -p /tmp/zig-musl
# cc-rs injects --target=<rust-triple>, which zig does not parse.
# Strip caller-provided --target and use the wrapper's zig target.
for tool in cc c++; do
printf '#!/bin/bash\nargs=()\nfor arg in "$@"; do\n case "$arg" in\n --target=*) ;;\n *) args+=("$arg") ;;\n esac\ndone\nexec "%s" %s --target=%s "${args[@]}"\n' \
"$ZIG" "$tool" "$ZIG_TARGET" > "/tmp/zig-musl/${tool}"
chmod +x "/tmp/zig-musl/${tool}"
done
TARGET_ENV=$(echo "${{ steps.target.outputs.target }}" | tr '-' '_')
TARGET_ENV_UPPER=${TARGET_ENV^^}
echo "CC_${TARGET_ENV}=/tmp/zig-musl/cc" >> "$GITHUB_ENV"
echo "CXX_${TARGET_ENV}=/tmp/zig-musl/c++" >> "$GITHUB_ENV"
echo "CARGO_TARGET_${TARGET_ENV_UPPER}_LINKER=/tmp/zig-musl/cc" >> "$GITHUB_ENV"
echo "CARGO_TARGET_${TARGET_ENV_UPPER}_RUSTFLAGS=-Clink-self-contained=no" >> "$GITHUB_ENV"
- name: Build ${{ steps.target.outputs.binary }} (${{ steps.target.outputs.zig_target || steps.target.outputs.target }})
env:
# Preserve the release-codegen setting used by the old Dockerfile
# Rust build path so image artifacts keep the same release profile.
CARGO_PROFILE_RELEASE_CODEGEN_UNITS: "1"
OPENSHELL_IMAGE_TAG: ${{ inputs['image-tag'] }}
run: |
set -euo pipefail
mise x -- rustup target add "${{ steps.target.outputs.target }}"
cargo_cmd=(cargo build)
build_target="${{ steps.target.outputs.target }}"
if [[ "${{ inputs.component }}" == "gateway" ]]; then
cargo_cmd=(cargo zigbuild)
build_target="${{ steps.target.outputs.zig_target }}"
fi
args=(
--release
--target "$build_target"
-p "${{ steps.target.outputs.crate }}"
--bin "${{ steps.target.outputs.binary }}"
)
if [[ -n "$FEATURES" ]]; then
args+=(--features "$FEATURES")
fi
if [[ -n "${{ steps.version.outputs.cargo_version }}" ]]; then
export GIT_DIR=/nonexistent
fi
mise x -- "${cargo_cmd[@]}" "${args[@]}"
- name: Verify packaged binary
run: |
set -euo pipefail
BIN="target/${{ steps.target.outputs.target }}/release/${{ steps.target.outputs.binary }}"
OUTPUT="$("$BIN" --version)"
echo "$OUTPUT"
grep -q "^${{ steps.target.outputs.binary }} " <<<"$OUTPUT"
# Record linkage so image runtime drift is visible in logs.
ldd --version
ldd "$BIN" || true
if [[ "${{ inputs.component }}" == "gateway" ]] && ldd "$BIN" | grep -q 'libz3'; then
echo "gateway binary must not depend on shared libz3; enable bundled-z3 for image artifacts" >&2
exit 1
fi
- name: Verify glibc symbol floor
if: inputs.component == 'gateway'
run: |
set -euo pipefail
BIN="target/${{ steps.target.outputs.target }}/release/${{ steps.target.outputs.binary }}"
tasks/scripts/verify-glibc-symbols.sh 2.31 "$BIN"
- name: Stage binary for prebuilt layout
run: |
set -euo pipefail
STAGE="prebuilt-binaries/$ARCH"
mkdir -p "$STAGE"
install -m 0755 \
"target/${{ steps.target.outputs.target }}/release/${{ steps.target.outputs.binary }}" \
"$STAGE/${{ steps.target.outputs.binary }}"
ls -lh "$STAGE/"
- name: Upload artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: ${{ inputs['artifact-name'] != '' && inputs['artifact-name'] || format('rust-binary-{0}-linux-{1}', inputs.component, inputs.arch) }}
path: prebuilt-binaries/${{ inputs.arch }}/${{ steps.target.outputs.binary }}
retention-days: ${{ inputs['retention-days'] }}
if-no-files-found: error
- name: sccache stats
if: always()
run: |
set +e
stats_bin="${SCCACHE_PATH:-sccache}"
"$stats_bin" --show-stats
status=$?
if [[ $status -ne 0 ]]; then
echo "::warning::sccache stats unavailable (exit $status)"
fi
exit 0