diff --git a/.github/workflows/shadow-rust-native-build.yml b/.github/workflows/shadow-rust-native-build.yml new file mode 100644 index 000000000..259b34ded --- /dev/null +++ b/.github/workflows/shadow-rust-native-build.yml @@ -0,0 +1,154 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Shadow — Rust Native Build (openshell-gateway / openshell-sandbox) + +# OS-49 Phase 4 / PR 4a — non-blocking shadow that builds openshell-gateway +# and openshell-sandbox natively per-arch on the nv-gha-runners shared CPU +# pool (`linux-{amd64,arm64}-cpu8`) with a GHA-backed sccache, and uploads +# the resulting binaries as artifacts. Reuses the pattern from PR #853's +# release-dev.yml "Build standalone {gateway,supervisor} binaries" jobs. +# +# The artifacts match the layout PR 4c expects when consuming `BINARY_SOURCE= +# prebuilt` (wired up in #945): one binary per (component, arch), staged to +# `deploy/docker/.build/prebuilt-binaries//openshell-{gateway,sandbox}`. +# +# Dispatch 4-5 times after merge to collect cold + warm numbers and compare +# against the Rust portion of docker-build.yml's 17.5 m ARC baseline. Success +# criteria, gotchas, and dependency graph live on the OS-128 Linear issue. + +on: + push: + branches: [main] + workflow_dispatch: + +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" + +jobs: + rust-native-build: + name: ${{ matrix.component }} (${{ matrix.arch }}) + strategy: + fail-fast: false + matrix: + component: [gateway, sandbox] + arch: [amd64, arm64] + include: + - component: gateway + crate: openshell-server + binary: openshell-gateway + - component: sandbox + crate: openshell-sandbox + binary: openshell-sandbox + - arch: amd64 + runner: linux-amd64-cpu8 + target: x86_64-unknown-linux-gnu + - arch: arm64 + runner: linux-arm64-cpu8 + target: aarch64-unknown-linux-gnu + runs-on: ${{ matrix.runner }} + env: + # Partition the GHA sccache cache per (component, arch). Without this, + # concurrent matrix jobs collide on the same cache key and later-starting + # writers hit 409 Conflict (PR #961 fix for shadow-shared-cpu-spike). + SCCACHE_GHA_VERSION: ${{ matrix.component }}-${{ matrix.arch }} + container: + image: ghcr.io/nvidia/openshell/ci:latest + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + with: + 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: Install tools + run: mise install + + - name: Configure GHA sccache backend + # Exposes ACTIONS_CACHE_URL / ACTIONS_RUNTIME_TOKEN so sccache (wrapped + # around rustc via mise's RUSTC_WRAPPER) can initialize the GHA cache. + uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 + + - name: Cache Rust target and registry + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 + with: + shared-key: shadow-rust-native-${{ matrix.component }}-${{ matrix.arch }} + cache-directories: .cache/sccache + cache-targets: "true" + + - name: Compute cargo version + id: version + run: | + set -euo pipefail + echo "cargo_version=$(uv run python tasks/scripts/release.py get-version --cargo)" >> "$GITHUB_OUTPUT" + + - 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: Build ${{ matrix.binary }} (${{ matrix.target }}) + # Matches docker-build.yml's default EXTRA_CARGO_FEATURES so the + # binary content is byte-comparable to what Dockerfile.images produces + # today (precondition for PR 4c's drop-in swap). + run: | + set -euo pipefail + mise x -- cargo build \ + --release \ + --target ${{ matrix.target }} \ + -p ${{ matrix.crate }} \ + --bin ${{ matrix.binary }} \ + --features openshell-core/dev-settings + + - name: Verify packaged binary + run: | + set -euo pipefail + BIN="target/${{ matrix.target }}/release/${{ matrix.binary }}" + OUTPUT="$("$BIN" --version)" + echo "$OUTPUT" + grep -q "^${{ matrix.binary }} " <<<"$OUTPUT" + # Record glibc linkage so drift from the Ubuntu noble runtime base + # image is visible in logs (not asserted — the runtime check lands + # when PR 4c builds images on top of these artifacts). + ldd --version | head -1 + ldd "$BIN" | head -20 || true + + - name: sccache stats + if: always() + run: mise x -- sccache --show-stats + + - name: Stage binary for prebuilt layout + # Shape mirrors `deploy/docker/.build/prebuilt-binaries//` + # so PR 4c can download the artifact directly into the build context. + run: | + set -euo pipefail + STAGE="prebuilt-binaries/${{ matrix.arch }}" + mkdir -p "$STAGE" + install -m 0755 "target/${{ matrix.target }}/release/${{ matrix.binary }}" "$STAGE/${{ matrix.binary }}" + ls -lh "$STAGE/" + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: rust-binary-${{ matrix.component }}-linux-${{ matrix.arch }} + path: prebuilt-binaries/${{ matrix.arch }}/${{ matrix.binary }} + retention-days: 5 + if-no-files-found: error