name: "Rust SDK Tests" on: workflow_dispatch: workflow_call: permissions: contents: read jobs: test: name: "Rust SDK Tests (${{ matrix.os }}, default)" if: github.event.repository.fork == false env: POWERSHELL_UPDATECHECK: Off CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} timeout-minutes: 20 defaults: run: shell: bash working-directory: ./rust steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: ./.github/actions/setup-copilot id: setup-copilot # rust-toolchain.toml in rust/ pins the stable channel + components. - name: Install Rust toolchain uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable with: toolchain: "1.94.0" - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: workspaces: "rust" prefix-key: v1-rust-no-bin cache-bin: false - name: Read pinned Copilot CLI version id: cli-version working-directory: ./nodejs run: | version=$(node -p "require('./package.json').copilotCliVersion") echo "version=$version" >> "$GITHUB_OUTPUT" echo "Pinned CLI version: $version" # Share the bundled-CLI archive cache with the `bundle` job: build.rs # now downloads in both modes (embed for `bundle`, extract-to-cache # for this `test` job's `--no-default-features` build). - name: Cache bundled CLI archives uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: ./rust/.bundled-cli-cache key: bundled-cli-${{ matrix.os }}-${{ steps.cli-version.outputs.version }} - name: Install test harness dependencies working-directory: ./test/harness run: npm ci --ignore-scripts - name: Warm up PowerShell if: runner.os == 'Windows' run: pwsh.exe -Command "Write-Host 'PowerShell ready'" - name: cargo test timeout-minutes: 90 env: RUST_E2E_CONCURRENCY: 4 COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} COPILOT_CLI_PATH: ${{ steps.setup-copilot.outputs.cli-path }} BUNDLED_CLI_CACHE_DIR: ${{ github.workspace }}/rust/.bundled-cli-cache # `--no-default-features` selects dev mode: build.rs still downloads # + verifies + extracts the CLI to the per-user cache, but doesn't # embed it. Tests exec against the setup-copilot CLI via # COPILOT_CLI_PATH (the env override wins over the dev cache). # The dedicated `bundle` job below exercises the embed pipeline. run: cargo test --no-default-features --features test-support -- --test-threads=4 --nocapture clippy: name: "Rust SDK Format and Clippy" if: github.event.repository.fork == false env: POWERSHELL_UPDATECHECK: Off CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 runs-on: ubuntu-latest timeout-minutes: 20 defaults: run: shell: bash working-directory: ./rust steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable with: toolchain: "1.94.0" components: clippy # Nightly rustfmt for unstable format options (group_imports, # imports_granularity, reorder_impl_items) — pinned in # `.rustfmt.nightly.toml`. - name: Install nightly rustfmt uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 with: toolchain: nightly-2026-04-14 components: rustfmt - uses: Swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae # v2 with: workspaces: "rust" prefix-key: v1-rust-no-bin cache-bin: false - name: Read pinned Copilot CLI version id: cli-version working-directory: ./nodejs run: | version=$(node -p "require('./package.json').copilotCliVersion") echo "version=$version" >> "$GITHUB_OUTPUT" echo "Pinned CLI version: $version" - name: Cache bundled CLI archives uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: ./rust/.bundled-cli-cache key: bundled-cli-ubuntu-latest-${{ steps.cli-version.outputs.version }} - name: cargo fmt --check (nightly) run: cargo +nightly-2026-04-14 fmt --all -- --config-path .rustfmt.nightly.toml --check - name: cargo clippy env: BUNDLED_CLI_CACHE_DIR: ${{ github.workspace }}/rust/.bundled-cli-cache run: cargo clippy --all-targets --features test-support,bundled-in-process -- --no-deps -D warnings -D clippy::unwrap_used -D clippy::disallowed_macros -D clippy::await_holding_invalid_type doc: name: "Rust SDK Docs" if: github.event.repository.fork == false env: POWERSHELL_UPDATECHECK: Off CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 runs-on: ubuntu-latest timeout-minutes: 20 defaults: run: shell: bash working-directory: ./rust steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable with: toolchain: "1.94.0" - uses: Swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae # v2 with: workspaces: "rust" prefix-key: v1-rust-no-bin cache-bin: false - name: Read pinned Copilot CLI version id: cli-version working-directory: ./nodejs run: | version=$(node -p "require('./package.json').copilotCliVersion") echo "version=$version" >> "$GITHUB_OUTPUT" echo "Pinned CLI version: $version" - name: Cache bundled CLI archives uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: ./rust/.bundled-cli-cache key: bundled-cli-ubuntu-latest-${{ steps.cli-version.outputs.version }} - name: cargo doc env: RUSTDOCFLAGS: "-D warnings" BUNDLED_CLI_CACHE_DIR: ${{ github.workspace }}/rust/.bundled-cli-cache run: cargo doc --no-deps --all-features # Exercises the in-process FFI transport (`Transport::InProcess`, the Rust # analogue of the .NET `RuntimeConnection.ForInProcess()`), mirroring the # `inprocess` transport cell in dotnet-sdk-tests.yml. Sets # COPILOT_SDK_DEFAULT_CONNECTION=inprocess so the client hosts the runtime # cdylib in-process instead of spawning a stdio child, then runs the whole # E2E suite over the in-process transport. The suite runs serially in-process # (the harness forces concurrency to 1) because it mirrors each test's # environment onto the shared process environment the in-process worker inherits. # Runs the whole E2E suite over the in-process transport on supported hosts. test-inprocess: name: "Rust SDK Tests (${{ matrix.os }}, inprocess)" if: github.event.repository.fork == false env: CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 strategy: fail-fast: false matrix: # TODO: Re-enable Windows after fixing the napi-oop peer shutdown crash. os: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} timeout-minutes: 20 defaults: run: shell: bash working-directory: ./rust steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: ./.github/actions/setup-copilot id: setup-copilot - name: Install Rust toolchain uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable with: toolchain: "1.94.0" - uses: Swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae # v2 with: workspaces: "rust" prefix-key: v1-rust-no-bin cache-bin: false - name: Read pinned Copilot CLI version id: cli-version working-directory: ./nodejs run: | version=$(node -p "require('./package.json').copilotCliVersion") echo "version=$version" >> "$GITHUB_OUTPUT" echo "Pinned CLI version: $version" - name: Cache bundled CLI archives uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: ./rust/.bundled-cli-cache key: bundled-cli-${{ matrix.os }}-${{ steps.cli-version.outputs.version }} - name: Install test harness dependencies working-directory: ./test/harness run: npm ci --ignore-scripts - name: Warm up PowerShell if: runner.os == 'Windows' run: pwsh.exe -Command "Write-Host 'PowerShell ready'" - name: Select in-process transport run: echo "COPILOT_SDK_DEFAULT_CONNECTION=inprocess" >> "$GITHUB_ENV" - name: cargo test (in-process transport, full E2E suite) timeout-minutes: 60 env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} COPILOT_CLI_PATH: ${{ steps.setup-copilot.outputs.cli-path }} BUNDLED_CLI_CACHE_DIR: ${{ github.workspace }}/rust/.bundled-cli-cache # The harness forces serial execution in-process (both the async semaphore and # libtest via --test-threads=1) because it mirrors each test's environment onto # the shared process environment, so RUST_E2E_CONCURRENCY is not set here. run: cargo test --no-default-features --features test-support,bundled-in-process --test e2e -- --test-threads=1 --nocapture # Validates the bundled-CLI build path on all three supported # platforms. While the regular `cargo test` job above also exercises # build.rs (bundling is on by default now), this matrix job is the # dedicated cross-platform smoke test for the download / verify / # extract / embed pipeline. Catches regressions before they ship to # crates.io and before bundling consumers hit them downstream. bundle: name: "Rust SDK Bundled CLI Build (${{ matrix.os }})" if: github.event.repository.fork == false env: CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} timeout-minutes: 20 defaults: run: shell: bash working-directory: ./rust steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable with: toolchain: "1.94.0" - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 # Cache is only an optimization; the Windows bundled smoke test should # not fail when rust-cache's post-job save flakes after a successful build. continue-on-error: ${{ runner.os == 'Windows' }} with: workspaces: "rust" key: bundled-cli prefix-key: v1-rust-no-bin cache-bin: false - name: Read pinned Copilot CLI version id: cli-version working-directory: ./nodejs run: | version=$(node -p "require('./package.json').copilotCliVersion") echo "version=$version" >> "$GITHUB_OUTPUT" echo "Pinned CLI version: $version" # Cache the downloaded archive across runs so we don't refetch # ~130 MB on every CI invocation. Keyed by OS + CLI version so old # archives drop out when the pinned version bumps, keeping the # cache bounded. - name: Cache bundled CLI archives uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: ./rust/.bundled-cli-cache key: bundled-cli-${{ matrix.os }}-${{ steps.cli-version.outputs.version }} - name: Test bundled CLI build paths env: BUNDLED_CLI_CACHE_DIR: ${{ github.workspace }}/rust/.bundled-cli-cache run: | cargo build cargo test --features bundled-in-process --lib embedded_archive_contains_only_expected_files # Build natively inside Alpine so Cargo reports target_env="musl". This # exercises selection and extraction of the linuxmusl-arm64 runtime artifact, # then runs the same default and in-process coverage as glibc Linux. test-musl-arm64: name: "Rust SDK Tests (Alpine ARM64, ${{ matrix.transport }})" if: github.event.repository.fork == false runs-on: ubuntu-24.04-arm timeout-minutes: 60 strategy: fail-fast: false matrix: transport: [default, inprocess] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Test ${{ matrix.transport }} transport in Alpine uses: ./.github/actions/run-alpine-tests env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 BUNDLED_CLI_CACHE_DIR: /workspace/rust/.bundled-cli-cache # Rust's musl target defaults to a static CRT, which cannot load the in-process runtime. RUSTFLAGS: "-C target-feature=-crt-static" RUSTDOCFLAGS: "-C target-feature=-crt-static" with: image: rust:1.94.0-alpine workdir: /workspace/rust transport: ${{ matrix.transport }} command: | rustc --print cfg | grep -Fqx "target_arch=\"aarch64\"" rustc --print cfg | grep -Fqx "target_env=\"musl\"" apk add --no-cache build-base pkgconf openssl-dev nodejs npm npm --prefix /workspace/nodejs ci --ignore-scripts export COPILOT_CLI_PATH="$(npm --prefix /workspace/nodejs run --silent prepare:runtime -- --print-path)" test -f "$COPILOT_CLI_PATH" cd /workspace/test/harness npm ci --ignore-scripts cd /workspace/rust # Keep native musl artifacts out of the mounted workspace so they cannot # collide with glibc or another architecture's host-build artifacts. export CARGO_TARGET_DIR=/tmp/copilot-sdk-rust-target if [ "$COPILOT_SDK_TEST_TRANSPORT" = "inprocess" ]; then unset RUST_E2E_CONCURRENCY cargo test --no-default-features --features test-support,bundled-in-process --test e2e -- --test-threads=1 --nocapture else export RUST_E2E_CONCURRENCY=4 cargo test --no-default-features --features test-support -- --test-threads=4 --nocapture fi