From 8edffede232c26e07e54d8e44564c3a27236ea23 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Fri, 4 Sep 2026 18:46:18 +0200 Subject: [PATCH 1/4] ci: add Alpine musl SDK coverage Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 41f8d874-82fa-4398-9863-998a615d6550 --- .github/actions/run-alpine-tests/action.yml | 61 +++++++++++++ .github/workflows/dotnet-sdk-tests.yml | 53 ++++++++++++ .github/workflows/go-sdk-tests.yml | 92 ++++++++++++++++---- .github/workflows/nodejs-sdk-tests.yml | 94 ++++++++++++++++----- .github/workflows/python-sdk-tests.yml | 74 ++++++++++++++-- .github/workflows/required-checks.yml | 10 +-- .github/workflows/rust-sdk-tests.yml | 46 ++++++++++ dotnet/test/Unit/MSBuildTargetsTests.cs | 19 ++++- dotnet/test/Unit/RuntimeWrapperTests.cs | 6 +- 9 files changed, 399 insertions(+), 56 deletions(-) create mode 100644 .github/actions/run-alpine-tests/action.yml diff --git a/.github/actions/run-alpine-tests/action.yml b/.github/actions/run-alpine-tests/action.yml new file mode 100644 index 0000000000..a29e113daf --- /dev/null +++ b/.github/actions/run-alpine-tests/action.yml @@ -0,0 +1,61 @@ +name: "Run Alpine SDK Tests" +description: "Run an SDK test command in an x64 Alpine container." +inputs: + image: + description: "Container image used to run the tests." + required: true + workdir: + description: "Working directory inside the container." + required: true + transport: + description: "SDK transport to test: default or inprocess." + required: true + command: + description: "Language-specific setup and test command." + required: true +runs: + using: "composite" + steps: + - name: Run ${{ inputs.transport }} tests in Alpine + shell: bash + env: + ALPINE_TEST_COMMAND: ${{ inputs.command }} + ALPINE_TEST_IMAGE: ${{ inputs.image }} + ALPINE_TEST_TRANSPORT: ${{ inputs.transport }} + ALPINE_TEST_WORKDIR: ${{ inputs.workdir }} + run: | + script_path=$(mktemp "$RUNNER_TEMP/copilot-sdk-alpine-test.XXXXXX") + trap 'rm -f "$script_path"' EXIT + cat > "$script_path" <<'SCRIPT' + #!/bin/sh + set -eux + + test "$(uname -m)" = "x86_64" + ldd --version 2>&1 | grep -qi musl + + case "$COPILOT_SDK_TEST_TRANSPORT" in + inprocess) export COPILOT_SDK_DEFAULT_CONNECTION=inprocess ;; + default) unset COPILOT_SDK_DEFAULT_CONNECTION ;; + *) echo "Unknown transport: $COPILOT_SDK_TEST_TRANSPORT" >&2; exit 1 ;; + esac + SCRIPT + printf '%s\n' "$ALPINE_TEST_COMMAND" >> "$script_path" + chmod +x "$script_path" + + docker run --rm \ + --platform linux/amd64 \ + --volume "$GITHUB_WORKSPACE:/workspace" \ + --volume "$script_path:/tmp/copilot-sdk-alpine-test.sh:ro" \ + --workdir "$ALPINE_TEST_WORKDIR" \ + --env CI=true \ + --env GITHUB_ACTIONS=true \ + --env GITHUB_WORKSPACE=/workspace \ + --env COPILOT_HMAC_KEY \ + --env COPILOT_SDK_E2E_BACKEND \ + --env BUNDLED_CLI_CACHE_DIR \ + --env CARGO_TERM_COLOR \ + --env RUST_BACKTRACE \ + --env RUSTFLAGS \ + --env COPILOT_SDK_TEST_TRANSPORT="$ALPINE_TEST_TRANSPORT" \ + "$ALPINE_TEST_IMAGE" \ + /bin/sh /tmp/copilot-sdk-alpine-test.sh diff --git a/.github/workflows/dotnet-sdk-tests.yml b/.github/workflows/dotnet-sdk-tests.yml index 9b7216ad20..3b19e60b78 100644 --- a/.github/workflows/dotnet-sdk-tests.yml +++ b/.github/workflows/dotnet-sdk-tests.yml @@ -349,3 +349,56 @@ jobs: path: dotnet/TestResults/ if-no-files-found: warn retention-days: 7 + + # JavaScript actions use a glibc-linked Node runtime, so Alpine runs through Docker. + alpine: + name: ".NET SDK Tests (Alpine x64, ${{ matrix.transport }}, CAPI)" + if: github.event.repository.fork == false + runs-on: ubuntu-latest + timeout-minutes: 20 + env: + COPILOT_SDK_E2E_BACKEND: capi + strategy: + fail-fast: false + matrix: + transport: ["default", "inprocess"] + steps: + - uses: actions/checkout@v6.0.2 + + - name: Run ${{ matrix.transport }} tests on x64 Alpine + uses: ./.github/actions/run-alpine-tests + env: + COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + with: + image: mcr.microsoft.com/dotnet/sdk:10.0-alpine + workdir: /workspace + transport: ${{ matrix.transport }} + command: | + dotnet --info | grep -Eq "RID:[[:space:]]+linux-musl-x64" + + apk add --no-cache bash git nodejs npm + (cd nodejs && npm ci --ignore-scripts) + (cd test/harness && npm ci --ignore-scripts) + + export DOTNET_ROLL_FORWARD=Major + dotnet restore dotnet/test/GitHub.Copilot.SDK.Test.csproj + + dotnet test dotnet/test/GitHub.Copilot.SDK.Test.csproj \ + --no-restore \ + --framework net8.0 \ + -v n \ + --blame-hang \ + --blame-hang-timeout 10m \ + --blame-hang-dump-type none \ + --logger "trx;LogFilePrefix=test-results" \ + --results-directory /workspace/dotnet/TestResults \ + -p:RunAnalyzers=false + + - name: Upload .NET test diagnostics + if: failure() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: dotnet-test-diagnostics-alpine-x64-${{ matrix.transport }}-capi-${{ github.run_attempt }} + path: dotnet/TestResults/ + if-no-files-found: warn + retention-days: 7 diff --git a/.github/workflows/go-sdk-tests.yml b/.github/workflows/go-sdk-tests.yml index 4fb6fd0184..4e8a9bfb74 100644 --- a/.github/workflows/go-sdk-tests.yml +++ b/.github/workflows/go-sdk-tests.yml @@ -8,17 +8,10 @@ permissions: contents: read jobs: - test: - name: "Go SDK Tests (${{ matrix.os }}, ${{ matrix.transport }})" + validate: + name: "Go SDK Format and Lint" if: github.event.repository.fork == false - env: - POWERSHELL_UPDATECHECK: Off - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - transport: ["default", "inprocess"] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest timeout-minutes: 20 defaults: run: @@ -26,15 +19,10 @@ jobs: working-directory: ./go steps: - uses: actions/checkout@v6.0.2 - - uses: ./.github/actions/setup-copilot - id: setup-copilot - uses: actions/setup-go@v6 with: go-version: "1.24" - - name: Run go fmt - if: runner.os == 'Linux' - working-directory: ./go run: | go fmt ./... if [ -n "$(git status --porcelain)" ]; then @@ -43,15 +31,37 @@ jobs: exit 1 fi echo "✅ go fmt produced no changes" - - name: Install golangci-lint - if: runner.os == 'Linux' uses: golangci/golangci-lint-action@v9 with: working-directory: ./go version: latest args: --timeout=5m + test: + name: "Go SDK Tests (${{ matrix.os }}, ${{ matrix.transport }})" + if: github.event.repository.fork == false + env: + POWERSHELL_UPDATECHECK: Off + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + transport: ["default", "inprocess"] + runs-on: ${{ matrix.os }} + timeout-minutes: 20 + defaults: + run: + shell: bash + working-directory: ./go + steps: + - uses: actions/checkout@v6.0.2 + - uses: ./.github/actions/setup-copilot + id: setup-copilot + - uses: actions/setup-go@v6 + with: + go-version: "1.24" + - name: Install test harness dependencies working-directory: ./test/harness run: npm ci --ignore-scripts @@ -71,3 +81,51 @@ jobs: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} COPILOT_CLI_PATH: ${{ steps.setup-copilot.outputs.cli-path }} run: /bin/bash test.sh + + # JavaScript actions use a glibc-linked Node runtime, so Alpine runs through Docker. + test-musl: + name: "Go SDK Tests (Alpine x64, ${{ matrix.transport }})" + if: github.event.repository.fork == false + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + transport: ["default", "inprocess"] + steps: + - uses: actions/checkout@v6.0.2 + + - name: Run Go SDK tests on musl x64 (${{ matrix.transport }}) + uses: ./.github/actions/run-alpine-tests + env: + COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + with: + image: golang:1.24-alpine + workdir: /workspace + transport: ${{ matrix.transport }} + command: | + apk add --no-cache bash build-base git nodejs npm + npm --prefix nodejs ci --ignore-scripts + npm --prefix test/harness ci --ignore-scripts + runtime_path="$(npm --prefix nodejs run --silent prepare:runtime -- --print-path)" + case "$runtime_path" in + */prebuilds/linuxmusl-x64/copilot-runtime) ;; + *) + echo "Expected the linuxmusl-x64 runtime bundle, got: $runtime_path" >&2 + exit 1 + ;; + esac + test -x "$runtime_path" + test -f "$(dirname "$runtime_path")/runtime.node" + + export COPILOT_CLI_PATH="$runtime_path" + if [ "$COPILOT_SDK_TEST_TRANSPORT" = "inprocess" ]; then + export GOFLAGS="-tags=copilot_inprocess" + else + unset GOFLAGS + fi + export CGO_ENABLED=1 + test "$(go env GOARCH)" = "amd64" + test "$(go env CGO_ENABLED)" = "1" + cd go + /bin/bash test.sh diff --git a/.github/workflows/nodejs-sdk-tests.yml b/.github/workflows/nodejs-sdk-tests.yml index 894f3b5d0d..6bd7473eea 100644 --- a/.github/workflows/nodejs-sdk-tests.yml +++ b/.github/workflows/nodejs-sdk-tests.yml @@ -11,17 +11,11 @@ permissions: contents: read jobs: - test: - name: "Node.js SDK Tests (${{ matrix.os }}, ${{ matrix.transport }})" + validate: + name: "Node.js SDK Build and Validation" if: github.event.repository.fork == false - env: - POWERSHELL_UPDATECHECK: Off - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - transport: ["default", "inprocess"] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest + timeout-minutes: 20 defaults: run: shell: bash @@ -35,35 +29,57 @@ jobs: node-version: 22 - name: Install dependencies run: npm ci --ignore-scripts - - name: Run prettier check - if: runner.os == 'Linux' run: npm run format:check - - name: Run ESLint run: npm run lint - - name: Typecheck SDK run: npm run typecheck - - name: Build SDK run: npm run build - - name: Build and verify release packages - if: runner.os == 'Linux' && matrix.transport == 'default' run: | npm run pack:release npm run verify:release-packages - - name: Install test harness dependencies working-directory: ./test/harness run: npm ci --ignore-scripts - - name: Run test harness tests - if: runner.os == 'Linux' && matrix.transport == 'default' working-directory: ./test/harness run: npm test + test: + name: "Node.js SDK Tests (${{ matrix.os }}, ${{ matrix.transport }})" + if: github.event.repository.fork == false + env: + POWERSHELL_UPDATECHECK: Off + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + transport: ["default", "inprocess"] + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + working-directory: ./nodejs + steps: + - uses: actions/checkout@v6.0.2 + - uses: actions/setup-node@v6 + with: + cache: "npm" + cache-dependency-path: "./nodejs/package-lock.json" + node-version: 22 + - name: Install dependencies + run: npm ci --ignore-scripts + + - name: Build SDK + run: npm run build + + - name: Install test harness dependencies + working-directory: ./test/harness + run: npm ci --ignore-scripts + - name: Prepare Copilot CLI runtime run: | runtime_path=$(npm run --silent prepare:runtime -- --print-path) @@ -82,3 +98,41 @@ jobs: env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} run: npm test + + # JavaScript actions use a glibc-linked Node runtime, so Alpine runs through Docker. + test-musl-x64: + name: "Node.js SDK Tests (Alpine x64, ${{ matrix.transport }})" + if: github.event.repository.fork == false + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + transport: ["default", "inprocess"] + steps: + - uses: actions/checkout@v6.0.2 + - name: Run Alpine x64 tests (${{ matrix.transport }}) + uses: ./.github/actions/run-alpine-tests + env: + COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + with: + image: node:22-alpine + workdir: /workspace/nodejs + transport: ${{ matrix.transport }} + command: | + apk add --no-cache bash git + npm ci --ignore-scripts + npm run build + (cd /workspace/test/harness && npm ci --ignore-scripts) + + runtime_path="$(npm run --silent prepare:runtime -- --print-path)" + case "$runtime_path" in + */prebuilds/linuxmusl-x64/copilot-runtime) ;; + *) + echo "Expected the linuxmusl-x64 runtime bundle, got: $runtime_path" >&2 + exit 1 + ;; + esac + test -x "$runtime_path" + test -f "$(dirname "$runtime_path")/runtime.node" + export COPILOT_CLI_PATH="$runtime_path" + npm test diff --git a/.github/workflows/python-sdk-tests.yml b/.github/workflows/python-sdk-tests.yml index aa6e803372..2977a59bf0 100644 --- a/.github/workflows/python-sdk-tests.yml +++ b/.github/workflows/python-sdk-tests.yml @@ -11,6 +11,33 @@ permissions: contents: read jobs: + validate: + name: "Python SDK Format and Typecheck" + if: github.event.repository.fork == false + runs-on: ubuntu-latest + timeout-minutes: 20 + defaults: + run: + shell: bash + working-directory: ./python + steps: + - uses: actions/checkout@v6.0.2 + - uses: actions/setup-python@v6 + with: + python-version: "3.11" + - name: Set up uv + uses: astral-sh/setup-uv@v7 + with: + enable-cache: true + - name: Install Python dev dependencies + run: uv sync --all-extras --dev + - name: Run ruff format check + run: uv run ruff format --check . + - name: Run ruff lint + run: uv run ruff check + - name: Run ty type checking + run: uv run ty check copilot + test: name: "Python SDK Tests (${{ matrix.os }}, ${{ matrix.transport }})" if: github.event.repository.fork == false @@ -52,15 +79,6 @@ jobs: working-directory: ./nodejs run: npm ci --ignore-scripts --fetch-retries=4 --fetch-retry-mintimeout=10000 --fetch-retry-maxtimeout=60000 - - name: Run ruff format check - run: uv run ruff format --check . - - - name: Run ruff lint - run: uv run ruff check - - - name: Run ty type checking - run: uv run ty check copilot - - name: Install test harness dependencies working-directory: ./test/harness run: npm ci --ignore-scripts --fetch-retries=4 --fetch-retry-mintimeout=10000 --fetch-retry-maxtimeout=60000 @@ -80,3 +98,41 @@ jobs: # Keep each module's shared E2E client and proxy on one process while # running independent modules concurrently in isolated workers. run: uv run pytest -v -s -n 2 --dist=loadfile + + # JavaScript actions use a glibc-linked Node runtime, so Alpine runs through Docker. + test-musl-x64: + name: "Python SDK Tests (Alpine x64, ${{ matrix.transport }})" + if: github.event.repository.fork == false + strategy: + fail-fast: false + matrix: + transport: ["default", "inprocess"] + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v6.0.2 + + - name: Run Python SDK tests on musl + uses: ./.github/actions/run-alpine-tests + env: + COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + with: + image: python:3.11-alpine + workdir: /workspace + transport: ${{ matrix.transport }} + command: | + apk add --no-cache bash git nodejs npm + python -m pip install --no-cache-dir uv + + ( + cd nodejs + npm ci --ignore-scripts --fetch-retries=4 --fetch-retry-mintimeout=10000 --fetch-retry-maxtimeout=60000 + ) + ( + cd test/harness + npm ci --ignore-scripts --fetch-retries=4 --fetch-retry-mintimeout=10000 --fetch-retry-maxtimeout=60000 + ) + + cd python + uv sync --all-extras --dev + uv run pytest -v -s -n 2 --dist=loadfile diff --git a/.github/workflows/required-checks.yml b/.github/workflows/required-checks.yml index 0fb8acf70b..020ce41969 100644 --- a/.github/workflows/required-checks.yml +++ b/.github/workflows/required-checks.yml @@ -36,28 +36,28 @@ jobs: orchestrator: - '.github/workflows/required-checks.yml' nodejs: - - '{nodejs/**,test/**,.github/workflows/nodejs-sdk-tests.yml}' + - '{nodejs/**,test/**,.github/workflows/nodejs-sdk-tests.yml,.github/actions/run-alpine-tests/**}' - '!**/*.md' - '!**/LICENSE*' - '!**/.gitignore' - '!**/.editorconfig' - '!**/*.{png,jpg,jpeg,gif,svg}' python: - - '{python/**,test/**,nodejs/package.json,nodejs/scripts/**,.github/workflows/python-sdk-tests.yml}' + - '{python/**,test/**,nodejs/package.json,nodejs/scripts/**,.github/workflows/python-sdk-tests.yml,.github/actions/run-alpine-tests/**}' - '!**/*.md' - '!**/LICENSE*' - '!**/.gitignore' - '!**/.editorconfig' - '!**/*.{png,jpg,jpeg,gif,svg}' go: - - '{go/**,test/**,nodejs/package.json,nodejs/scripts/**,.github/workflows/go-sdk-tests.yml,.github/actions/setup-copilot/**}' + - '{go/**,test/**,nodejs/package.json,nodejs/scripts/**,.github/workflows/go-sdk-tests.yml,.github/actions/setup-copilot/**,.github/actions/run-alpine-tests/**}' - '!**/*.md' - '!**/LICENSE*' - '!**/.gitignore' - '!**/.editorconfig' - '!**/*.{png,jpg,jpeg,gif,svg}' dotnet: - - '{dotnet/**,test/**,nodejs/package.json,nodejs/scripts/**,.github/workflows/dotnet-sdk-tests.yml}' + - '{dotnet/**,test/**,nodejs/package.json,nodejs/scripts/**,.github/workflows/dotnet-sdk-tests.yml,.github/actions/run-alpine-tests/**}' - '!**/*.md' - '!**/LICENSE*' - '!**/.gitignore' @@ -71,7 +71,7 @@ jobs: - '!**/.editorconfig' - '!**/*.{png,jpg,jpeg,gif,svg}' rust: - - '{rust/**,test/**,nodejs/package.json,nodejs/scripts/**,.github/workflows/rust-sdk-tests.yml,.github/actions/setup-copilot/**}' + - '{rust/**,test/**,nodejs/package.json,nodejs/scripts/**,.github/workflows/rust-sdk-tests.yml,.github/actions/setup-copilot/**,.github/actions/run-alpine-tests/**}' - '!**/*.md' - '!**/LICENSE*' - '!**/.gitignore' diff --git a/.github/workflows/rust-sdk-tests.yml b/.github/workflows/rust-sdk-tests.yml index 828459126b..e8bb611403 100644 --- a/.github/workflows/rust-sdk-tests.yml +++ b/.github/workflows/rust-sdk-tests.yml @@ -331,3 +331,49 @@ jobs: 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-x64 runtime artifact, + # then runs the same default and in-process coverage as glibc Linux. + test-musl-x64: + name: "Rust SDK Tests (Alpine x64, ${{ matrix.transport }})" + if: github.event.repository.fork == false + runs-on: ubuntu-latest + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + transport: [default, inprocess] + steps: + - uses: actions/checkout@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" + with: + image: rust:1.94.0-alpine + workdir: /workspace/rust + transport: ${{ matrix.transport }} + command: | + rustc --print cfg | grep -Fqx "target_arch=\"x86_64\"" + rustc --print cfg | grep -Fqx "target_env=\"musl\"" + apk add --no-cache bash build-base git 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 + 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 diff --git a/dotnet/test/Unit/MSBuildTargetsTests.cs b/dotnet/test/Unit/MSBuildTargetsTests.cs index 0dd3073956..3b8b97281f 100644 --- a/dotnet/test/Unit/MSBuildTargetsTests.cs +++ b/dotnet/test/Unit/MSBuildTargetsTests.cs @@ -194,7 +194,12 @@ private static string GetNpmPlatform() : "x64"; if (OperatingSystem.IsWindows()) return $"win32-{arch}"; if (OperatingSystem.IsMacOS()) return $"darwin-{arch}"; - return $"linux-{arch}"; + var platform = System.Runtime.InteropServices.RuntimeInformation.RuntimeIdentifier.StartsWith( + "linux-musl-", + StringComparison.Ordinal) + ? "linuxmusl" + : "linux"; + return $"{platform}-{arch}"; } /// @@ -364,11 +369,17 @@ private static string GetPortableRid() _ => "osx-x64", }; } - return System.Runtime.InteropServices.RuntimeInformation.OSArchitecture switch + var os = System.Runtime.InteropServices.RuntimeInformation.RuntimeIdentifier.StartsWith( + "linux-musl-", + StringComparison.Ordinal) + ? "linux-musl" + : "linux"; + var architecture = System.Runtime.InteropServices.RuntimeInformation.OSArchitecture switch { - System.Runtime.InteropServices.Architecture.Arm64 => "linux-arm64", - _ => "linux-x64", + System.Runtime.InteropServices.Architecture.Arm64 => "arm64", + _ => "x64", }; + return $"{os}-{architecture}"; } } diff --git a/dotnet/test/Unit/RuntimeWrapperTests.cs b/dotnet/test/Unit/RuntimeWrapperTests.cs index 9a9dacb7d3..2c6a92171b 100644 --- a/dotnet/test/Unit/RuntimeWrapperTests.cs +++ b/dotnet/test/Unit/RuntimeWrapperTests.cs @@ -122,7 +122,11 @@ private static string GetPortableRid() { var os = OperatingSystem.IsWindows() ? "win" : OperatingSystem.IsMacOS() ? "osx" - : "linux"; + : System.Runtime.InteropServices.RuntimeInformation.RuntimeIdentifier.StartsWith( + "linux-musl-", + StringComparison.Ordinal) + ? "linux-musl" + : "linux"; var architecture = System.Runtime.InteropServices.RuntimeInformation.OSArchitecture switch { System.Runtime.InteropServices.Architecture.X64 => "x64", From 77fc5bf0cb0e32efeef7e9bf34ddf0b31bb5b523 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Sun, 6 Sep 2026 11:03:33 +0200 Subject: [PATCH 2/4] ci: run Alpine coverage on ARM64 Upgrade Koffi for its fixed musl ARM64 native binary and isolate Rust's native container artifacts from other host architectures. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 41f8d874-82fa-4398-9863-998a615d6550 --- .github/actions/run-alpine-tests/action.yml | 7 +- .github/workflows/dotnet-sdk-tests.yml | 12 +- .github/workflows/go-sdk-tests.yml | 14 +- .github/workflows/nodejs-sdk-tests.yml | 12 +- .github/workflows/python-sdk-tests.yml | 6 +- .github/workflows/rust-sdk-tests.yml | 14 +- nodejs/package-lock.json | 154 ++++++++++++++------ nodejs/package.json | 2 +- 8 files changed, 145 insertions(+), 76 deletions(-) diff --git a/.github/actions/run-alpine-tests/action.yml b/.github/actions/run-alpine-tests/action.yml index a29e113daf..36ad293b19 100644 --- a/.github/actions/run-alpine-tests/action.yml +++ b/.github/actions/run-alpine-tests/action.yml @@ -1,5 +1,5 @@ name: "Run Alpine SDK Tests" -description: "Run an SDK test command in an x64 Alpine container." +description: "Run an SDK test command in an ARM64 Alpine container." inputs: image: description: "Container image used to run the tests." @@ -30,7 +30,7 @@ runs: #!/bin/sh set -eux - test "$(uname -m)" = "x86_64" + test "$(uname -m)" = "aarch64" ldd --version 2>&1 | grep -qi musl case "$COPILOT_SDK_TEST_TRANSPORT" in @@ -43,7 +43,7 @@ runs: chmod +x "$script_path" docker run --rm \ - --platform linux/amd64 \ + --platform linux/arm64 \ --volume "$GITHUB_WORKSPACE:/workspace" \ --volume "$script_path:/tmp/copilot-sdk-alpine-test.sh:ro" \ --workdir "$ALPINE_TEST_WORKDIR" \ @@ -55,6 +55,7 @@ runs: --env BUNDLED_CLI_CACHE_DIR \ --env CARGO_TERM_COLOR \ --env RUST_BACKTRACE \ + --env RUSTDOCFLAGS \ --env RUSTFLAGS \ --env COPILOT_SDK_TEST_TRANSPORT="$ALPINE_TEST_TRANSPORT" \ "$ALPINE_TEST_IMAGE" \ diff --git a/.github/workflows/dotnet-sdk-tests.yml b/.github/workflows/dotnet-sdk-tests.yml index 3b19e60b78..787e2fcc7d 100644 --- a/.github/workflows/dotnet-sdk-tests.yml +++ b/.github/workflows/dotnet-sdk-tests.yml @@ -351,10 +351,10 @@ jobs: retention-days: 7 # JavaScript actions use a glibc-linked Node runtime, so Alpine runs through Docker. - alpine: - name: ".NET SDK Tests (Alpine x64, ${{ matrix.transport }}, CAPI)" + alpine-arm64: + name: ".NET SDK Tests (Alpine ARM64, ${{ matrix.transport }}, CAPI)" if: github.event.repository.fork == false - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm timeout-minutes: 20 env: COPILOT_SDK_E2E_BACKEND: capi @@ -365,7 +365,7 @@ jobs: steps: - uses: actions/checkout@v6.0.2 - - name: Run ${{ matrix.transport }} tests on x64 Alpine + - name: Run ${{ matrix.transport }} tests on ARM64 Alpine uses: ./.github/actions/run-alpine-tests env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} @@ -374,7 +374,7 @@ jobs: workdir: /workspace transport: ${{ matrix.transport }} command: | - dotnet --info | grep -Eq "RID:[[:space:]]+linux-musl-x64" + dotnet --info | grep -Eq "RID:[[:space:]]+linux-musl-arm64" apk add --no-cache bash git nodejs npm (cd nodejs && npm ci --ignore-scripts) @@ -398,7 +398,7 @@ jobs: if: failure() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: dotnet-test-diagnostics-alpine-x64-${{ matrix.transport }}-capi-${{ github.run_attempt }} + name: dotnet-test-diagnostics-alpine-arm64-${{ matrix.transport }}-capi-${{ github.run_attempt }} path: dotnet/TestResults/ if-no-files-found: warn retention-days: 7 diff --git a/.github/workflows/go-sdk-tests.yml b/.github/workflows/go-sdk-tests.yml index 4e8a9bfb74..139010b267 100644 --- a/.github/workflows/go-sdk-tests.yml +++ b/.github/workflows/go-sdk-tests.yml @@ -83,10 +83,10 @@ jobs: run: /bin/bash test.sh # JavaScript actions use a glibc-linked Node runtime, so Alpine runs through Docker. - test-musl: - name: "Go SDK Tests (Alpine x64, ${{ matrix.transport }})" + test-musl-arm64: + name: "Go SDK Tests (Alpine ARM64, ${{ matrix.transport }})" if: github.event.repository.fork == false - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm timeout-minutes: 20 strategy: fail-fast: false @@ -95,7 +95,7 @@ jobs: steps: - uses: actions/checkout@v6.0.2 - - name: Run Go SDK tests on musl x64 (${{ matrix.transport }}) + - name: Run Go SDK tests on musl ARM64 (${{ matrix.transport }}) uses: ./.github/actions/run-alpine-tests env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} @@ -109,9 +109,9 @@ jobs: npm --prefix test/harness ci --ignore-scripts runtime_path="$(npm --prefix nodejs run --silent prepare:runtime -- --print-path)" case "$runtime_path" in - */prebuilds/linuxmusl-x64/copilot-runtime) ;; + */prebuilds/linuxmusl-arm64/copilot-runtime) ;; *) - echo "Expected the linuxmusl-x64 runtime bundle, got: $runtime_path" >&2 + echo "Expected the linuxmusl-arm64 runtime bundle, got: $runtime_path" >&2 exit 1 ;; esac @@ -125,7 +125,7 @@ jobs: unset GOFLAGS fi export CGO_ENABLED=1 - test "$(go env GOARCH)" = "amd64" + test "$(go env GOARCH)" = "arm64" test "$(go env CGO_ENABLED)" = "1" cd go /bin/bash test.sh diff --git a/.github/workflows/nodejs-sdk-tests.yml b/.github/workflows/nodejs-sdk-tests.yml index 6bd7473eea..8c4b5ca204 100644 --- a/.github/workflows/nodejs-sdk-tests.yml +++ b/.github/workflows/nodejs-sdk-tests.yml @@ -100,17 +100,17 @@ jobs: run: npm test # JavaScript actions use a glibc-linked Node runtime, so Alpine runs through Docker. - test-musl-x64: - name: "Node.js SDK Tests (Alpine x64, ${{ matrix.transport }})" + test-musl-arm64: + name: "Node.js SDK Tests (Alpine ARM64, ${{ matrix.transport }})" if: github.event.repository.fork == false - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm strategy: fail-fast: false matrix: transport: ["default", "inprocess"] steps: - uses: actions/checkout@v6.0.2 - - name: Run Alpine x64 tests (${{ matrix.transport }}) + - name: Run Alpine ARM64 tests (${{ matrix.transport }}) uses: ./.github/actions/run-alpine-tests env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} @@ -126,9 +126,9 @@ jobs: runtime_path="$(npm run --silent prepare:runtime -- --print-path)" case "$runtime_path" in - */prebuilds/linuxmusl-x64/copilot-runtime) ;; + */prebuilds/linuxmusl-arm64/copilot-runtime) ;; *) - echo "Expected the linuxmusl-x64 runtime bundle, got: $runtime_path" >&2 + echo "Expected the linuxmusl-arm64 runtime bundle, got: $runtime_path" >&2 exit 1 ;; esac diff --git a/.github/workflows/python-sdk-tests.yml b/.github/workflows/python-sdk-tests.yml index 2977a59bf0..be40f1ec9d 100644 --- a/.github/workflows/python-sdk-tests.yml +++ b/.github/workflows/python-sdk-tests.yml @@ -100,14 +100,14 @@ jobs: run: uv run pytest -v -s -n 2 --dist=loadfile # JavaScript actions use a glibc-linked Node runtime, so Alpine runs through Docker. - test-musl-x64: - name: "Python SDK Tests (Alpine x64, ${{ matrix.transport }})" + test-musl-arm64: + name: "Python SDK Tests (Alpine ARM64, ${{ matrix.transport }})" if: github.event.repository.fork == false strategy: fail-fast: false matrix: transport: ["default", "inprocess"] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm timeout-minutes: 20 steps: - uses: actions/checkout@v6.0.2 diff --git a/.github/workflows/rust-sdk-tests.yml b/.github/workflows/rust-sdk-tests.yml index e8bb611403..fd9b301a77 100644 --- a/.github/workflows/rust-sdk-tests.yml +++ b/.github/workflows/rust-sdk-tests.yml @@ -333,12 +333,12 @@ jobs: 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-x64 runtime artifact, + # exercises selection and extraction of the linuxmusl-arm64 runtime artifact, # then runs the same default and in-process coverage as glibc Linux. - test-musl-x64: - name: "Rust SDK Tests (Alpine x64, ${{ matrix.transport }})" + test-musl-arm64: + name: "Rust SDK Tests (Alpine ARM64, ${{ matrix.transport }})" if: github.event.repository.fork == false - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm timeout-minutes: 60 strategy: fail-fast: false @@ -356,12 +356,13 @@ jobs: 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=\"x86_64\"" + rustc --print cfg | grep -Fqx "target_arch=\"aarch64\"" rustc --print cfg | grep -Fqx "target_env=\"musl\"" apk add --no-cache bash build-base git pkgconf openssl-dev nodejs npm npm --prefix /workspace/nodejs ci --ignore-scripts @@ -370,6 +371,9 @@ jobs: 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 diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index a10153a1d3..0de83bc883 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "koffi": "^3.1.0", + "koffi": "^3.2.1", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, @@ -735,9 +735,39 @@ "dev": true, "license": "MIT" }, + "node_modules/@koromix/koffi-android-arm64": { + "version": "3.2.1", + "integrity": "sha512-1pJQ4jnZlUJduK9u9DC5CGy3aOgDUPvIXpNb6syV3+Dh5Q/ugezAIGCqvY+w+1mgXsve0pd0NVvJRjdZNHQ6MA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "funding": { + "url": "https://liberapay.com/Koromix" + } + }, + "node_modules/@koromix/koffi-android-x64": { + "version": "3.2.1", + "integrity": "sha512-HH40xGh3gVQifjOBnhwT2tECC0lL1lYe+nxHvWNSzxDIyQNcVPXg38ta7vuONRFpD+uIrw7fqGYLzbZIagkVcg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "funding": { + "url": "https://liberapay.com/Koromix" + } + }, "node_modules/@koromix/koffi-darwin-arm64": { - "version": "3.1.0", - "integrity": "sha512-VEt5r3fXTfbejr83PnuOP0H7s9Zmazcs+lofu96DOcRkistlMsn59wYyWiKpyAjs9PCgm0Ykh62ChZ3CGMmIOg==", + "version": "3.2.1", + "integrity": "sha512-Vj4h+xcjc5+Cn0DhPHjgRX4omKAv96Kehtcd+1YgYuY2W7FvQn9vS+3SmzVwhC5Qmg9bIwUZObYQ8T/4hBqQqA==", "cpu": [ "arm64" ], @@ -751,8 +781,8 @@ } }, "node_modules/@koromix/koffi-darwin-x64": { - "version": "3.1.0", - "integrity": "sha1-r1z9mNiPAynDUcq8q6sPqKFY+xs=", + "version": "3.2.1", + "integrity": "sha512-gFCWxNBTZIvxo1p+PURWfsy2Ctj5FGnVVs1f03lTLhBvmxEto70pdIiFztdFLDFkAJ1pmtQmruRKapeK+E8YPA==", "cpu": [ "x64" ], @@ -766,8 +796,8 @@ } }, "node_modules/@koromix/koffi-freebsd-arm64": { - "version": "3.1.0", - "integrity": "sha1-rOu888/eXKu5jj4KgZb0gCzt+xE=", + "version": "3.2.1", + "integrity": "sha512-qj+f1s2e6vULaUG1cdlTcCXmunCq2t+rjxku1+esaMIqVnHpOwj0QzPuInG0AFdXjwBNQhyVR/HpDj8daEwwsQ==", "cpu": [ "arm64" ], @@ -781,8 +811,8 @@ } }, "node_modules/@koromix/koffi-freebsd-ia32": { - "version": "3.1.0", - "integrity": "sha1-0mky/r//V0JaXd+q9IRmdsZrkPI=", + "version": "3.2.1", + "integrity": "sha512-6olHb1Qfgai0jjs6ddlDDD0ZfsCxy7SPi8rMRpuYQWH0qhgtyQu82hw5b1p7z+TJ0zZP3ZeQQ6l+U/MlM1ICHQ==", "cpu": [ "ia32" ], @@ -796,8 +826,8 @@ } }, "node_modules/@koromix/koffi-freebsd-x64": { - "version": "3.1.0", - "integrity": "sha1-2INR65Jz3bqyIPXHyqZ+PRGVkCs=", + "version": "3.2.1", + "integrity": "sha512-Dikhw1ySYNVMkmeFvFVjnU5Wdk6mffNoOjJxm9bTG96vg7OlemylxqdEven47R1YJ3yzNVJn/MlQ207ORWfi2w==", "cpu": [ "x64" ], @@ -810,9 +840,24 @@ "url": "https://liberapay.com/Koromix" } }, + "node_modules/@koromix/koffi-linux-arm": { + "version": "3.2.1", + "integrity": "sha512-OfwUwZylidq95wQKp6ClInULrfB2giu7dqM6Rhe0zAe6lES5I2SXNw15T9+GnRHk3/9hKT2XZ37OZLaKSyWNLA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://liberapay.com/Koromix" + } + }, "node_modules/@koromix/koffi-linux-arm64": { - "version": "3.1.0", - "integrity": "sha1-9h8pjdDbtKufG4JXoxDFRNzAbnI=", + "version": "3.2.1", + "integrity": "sha512-K+cGUL5iBcDqxmsocrjmlASqDf24gc7artbVW3PewG2c9AqwC63lezgwvB85Nx4lZAQjB6zIFHh9A7t1yGbwhw==", "cpu": [ "arm64" ], @@ -826,8 +871,8 @@ } }, "node_modules/@koromix/koffi-linux-ia32": { - "version": "3.1.0", - "integrity": "sha1-8AbgHpYQoyx0uSoDgGpHwc1sGvM=", + "version": "3.2.1", + "integrity": "sha512-rxj6UYjU1qd98gxNQOSCdLpc5cPRi5Giq9rNd3jnGuSNIyMkwa6Dxw4cUjmhIBCYESMJtmNt5NWnJp5u9wTfYQ==", "cpu": [ "ia32" ], @@ -841,8 +886,8 @@ } }, "node_modules/@koromix/koffi-linux-loong64": { - "version": "3.1.0", - "integrity": "sha1-bQvI/dvGGdARiMeDkZg0R+POkPI=", + "version": "3.2.1", + "integrity": "sha512-aHhnHzkPRmT/IHDlGvESJ/Bs32m8N6UE6Ab6kMeJzgk74IN8af2m/81/wZJtybR1M2UxCV4NmlVNUYQQvSAO3Q==", "cpu": [ "loong64" ], @@ -856,8 +901,8 @@ } }, "node_modules/@koromix/koffi-linux-riscv64": { - "version": "3.1.0", - "integrity": "sha1-V5aXxWe4DH2j2r1dnJcMH9w44co=", + "version": "3.2.1", + "integrity": "sha512-qtQBsjbm3LiirLJvajWmKkNb7ARk7fvJVXdftJ7NtAnF3Xw8EbDvrtvmvtNI1yLPlYcBmlzCCD71hwhWYk0SIA==", "cpu": [ "riscv64" ], @@ -871,8 +916,8 @@ } }, "node_modules/@koromix/koffi-linux-x64": { - "version": "3.1.0", - "integrity": "sha1-eJX/wAVe0HJASLRTgubjJrHppuo=", + "version": "3.2.1", + "integrity": "sha512-c7hw7Qs/r5gnFRTQLcbifBwRU7wiocj+2pVuDQ5Ahb3r36SZmupmgYbTWcLvTW+hul1jd7SKRV0d14ZJq/tvSw==", "cpu": [ "x64" ], @@ -886,8 +931,8 @@ } }, "node_modules/@koromix/koffi-openbsd-ia32": { - "version": "3.1.0", - "integrity": "sha1-LwhKcVohSKoVCgY945O/4/gyoSk=", + "version": "3.2.1", + "integrity": "sha512-mmY8fY8LQ/CB52+h3yrMYmVyoxzW3x08S0yI6VNfHWdfU6yJtZkKCbhjmQCYMrWbYKC4gMvwZwCywIGPAkLyeA==", "cpu": [ "ia32" ], @@ -901,8 +946,8 @@ } }, "node_modules/@koromix/koffi-openbsd-x64": { - "version": "3.1.0", - "integrity": "sha1-wWm472ijVY45okrdH/8EIAVtzAo=", + "version": "3.2.1", + "integrity": "sha512-k4ig6aAPbFSRATOIIOfdf/KtlOGH4SVls6L9fy0QnTxRJYvY2oSltTsQtBDANgEQldlq8Kl5WnpRa1VSibP4Lw==", "cpu": [ "x64" ], @@ -915,9 +960,24 @@ "url": "https://liberapay.com/Koromix" } }, + "node_modules/@koromix/koffi-win32-arm64": { + "version": "3.2.1", + "integrity": "sha512-cTWBJGK//pDMeKQJE/79Aq9MiOAF4H8QyLZHSQ9IWm8czOfwjG4J1AhsQ9DjI9KFOykH77hhnpmQTGVMIubGig==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "funding": { + "url": "https://liberapay.com/Koromix" + } + }, "node_modules/@koromix/koffi-win32-ia32": { - "version": "3.1.0", - "integrity": "sha1-nDF+wS4sK934tD6A6e4I036PANM=", + "version": "3.2.1", + "integrity": "sha512-Z50EM6TAZ7CFyMmyX6thv8eNpJchqe9eenhibSIy2Eq/FQYF76gU2VK/LEoaF46L8hfC7TpTp9b10MvReHEyFA==", "cpu": [ "ia32" ], @@ -931,8 +991,8 @@ } }, "node_modules/@koromix/koffi-win32-x64": { - "version": "3.1.0", - "integrity": "sha1-FyJ7F9SaNAIYddhe6gJ533eKOI4=", + "version": "3.2.1", + "integrity": "sha512-ZmZNiBO6bkOSh3QNzgfvb1cMY0yMobn6ZQrSMqbAce21qyYL8niIbyipz9N/PIRDciGhsV0wUxnZsxIO+yWsHQ==", "cpu": [ "x64" ], @@ -2511,28 +2571,32 @@ } }, "node_modules/koffi": { - "version": "3.1.0", - "integrity": "sha512-0mCvdjTJBXioiaKNz0vajAEdWtfM5qyhVXSq+wQrrU3odzNvl/J7Cqna79QpNo9mfoKpQgGsyFFDRtDACCwGrQ==", + "version": "3.2.1", + "integrity": "sha512-0qE3lZ8jllRqPN4Ob6Ajl7c2bJSJDhQWuKLGP5hIEpHLllJWv1ydHFMhHmHc5p/W9GticKVDbYzZd7TBoQ4CZg==", "hasInstallScript": true, "license": "MIT", "funding": { "url": "https://liberapay.com/Koromix" }, "optionalDependencies": { - "@koromix/koffi-darwin-arm64": "3.1.0", - "@koromix/koffi-darwin-x64": "3.1.0", - "@koromix/koffi-freebsd-arm64": "3.1.0", - "@koromix/koffi-freebsd-ia32": "3.1.0", - "@koromix/koffi-freebsd-x64": "3.1.0", - "@koromix/koffi-linux-arm64": "3.1.0", - "@koromix/koffi-linux-ia32": "3.1.0", - "@koromix/koffi-linux-loong64": "3.1.0", - "@koromix/koffi-linux-riscv64": "3.1.0", - "@koromix/koffi-linux-x64": "3.1.0", - "@koromix/koffi-openbsd-ia32": "3.1.0", - "@koromix/koffi-openbsd-x64": "3.1.0", - "@koromix/koffi-win32-ia32": "3.1.0", - "@koromix/koffi-win32-x64": "3.1.0" + "@koromix/koffi-android-arm64": "3.2.1", + "@koromix/koffi-android-x64": "3.2.1", + "@koromix/koffi-darwin-arm64": "3.2.1", + "@koromix/koffi-darwin-x64": "3.2.1", + "@koromix/koffi-freebsd-arm64": "3.2.1", + "@koromix/koffi-freebsd-ia32": "3.2.1", + "@koromix/koffi-freebsd-x64": "3.2.1", + "@koromix/koffi-linux-arm": "3.2.1", + "@koromix/koffi-linux-arm64": "3.2.1", + "@koromix/koffi-linux-ia32": "3.2.1", + "@koromix/koffi-linux-loong64": "3.2.1", + "@koromix/koffi-linux-riscv64": "3.2.1", + "@koromix/koffi-linux-x64": "3.2.1", + "@koromix/koffi-openbsd-ia32": "3.2.1", + "@koromix/koffi-openbsd-x64": "3.2.1", + "@koromix/koffi-win32-arm64": "3.2.1", + "@koromix/koffi-win32-ia32": "3.2.1", + "@koromix/koffi-win32-x64": "3.2.1" } }, "node_modules/levn": { diff --git a/nodejs/package.json b/nodejs/package.json index 7bb3159f73..10c76b37a5 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -61,7 +61,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "koffi": "^3.1.0", + "koffi": "^3.2.1", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, From 7f79b52a671416ed42a2c7e6d6b912173af28bc3 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Sun, 6 Sep 2026 11:12:46 +0200 Subject: [PATCH 3/4] ci: pin setup-uv action Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 41f8d874-82fa-4398-9863-998a615d6550 --- .github/workflows/python-sdk-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-sdk-tests.yml b/.github/workflows/python-sdk-tests.yml index be40f1ec9d..eaf67bb903 100644 --- a/.github/workflows/python-sdk-tests.yml +++ b/.github/workflows/python-sdk-tests.yml @@ -26,7 +26,7 @@ jobs: with: python-version: "3.11" - name: Set up uv - uses: astral-sh/setup-uv@v7 + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 with: enable-cache: true - name: Install Python dev dependencies @@ -68,7 +68,7 @@ jobs: cache-dependency-path: "./nodejs/package-lock.json" - name: Set up uv - uses: astral-sh/setup-uv@v7 + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 with: enable-cache: true From 3cb3af42c955984dfcfdb3d4afa9bf1f2782e201 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Sun, 6 Sep 2026 11:30:02 +0200 Subject: [PATCH 4/4] ci: fix Alpine and downlevel test setup Configure mounted workspaces as trusted Git directories in the shared Alpine action, centralize common Alpine packages, and keep musl detection compatible with the net472 test target. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 41f8d874-82fa-4398-9863-998a615d6550 --- .github/actions/run-alpine-tests/action.yml | 3 +++ .github/workflows/dotnet-sdk-tests.yml | 2 +- .github/workflows/go-sdk-tests.yml | 2 +- .github/workflows/nodejs-sdk-tests.yml | 1 - .github/workflows/python-sdk-tests.yml | 2 +- .github/workflows/rust-sdk-tests.yml | 2 +- dotnet/test/Unit/MSBuildTargetsTests.cs | 23 ++++++++++++--------- dotnet/test/Unit/RuntimeWrapperTests.cs | 15 +++++++++++--- 8 files changed, 32 insertions(+), 18 deletions(-) diff --git a/.github/actions/run-alpine-tests/action.yml b/.github/actions/run-alpine-tests/action.yml index 36ad293b19..610cdb37a6 100644 --- a/.github/actions/run-alpine-tests/action.yml +++ b/.github/actions/run-alpine-tests/action.yml @@ -30,6 +30,9 @@ runs: #!/bin/sh set -eux + apk add --no-cache bash git + git config --global --add safe.directory "$GITHUB_WORKSPACE" + test "$(uname -m)" = "aarch64" ldd --version 2>&1 | grep -qi musl diff --git a/.github/workflows/dotnet-sdk-tests.yml b/.github/workflows/dotnet-sdk-tests.yml index 787e2fcc7d..b49c6e33e7 100644 --- a/.github/workflows/dotnet-sdk-tests.yml +++ b/.github/workflows/dotnet-sdk-tests.yml @@ -376,7 +376,7 @@ jobs: command: | dotnet --info | grep -Eq "RID:[[:space:]]+linux-musl-arm64" - apk add --no-cache bash git nodejs npm + apk add --no-cache nodejs npm (cd nodejs && npm ci --ignore-scripts) (cd test/harness && npm ci --ignore-scripts) diff --git a/.github/workflows/go-sdk-tests.yml b/.github/workflows/go-sdk-tests.yml index 139010b267..5f06016c3b 100644 --- a/.github/workflows/go-sdk-tests.yml +++ b/.github/workflows/go-sdk-tests.yml @@ -104,7 +104,7 @@ jobs: workdir: /workspace transport: ${{ matrix.transport }} command: | - apk add --no-cache bash build-base git nodejs npm + apk add --no-cache build-base nodejs npm npm --prefix nodejs ci --ignore-scripts npm --prefix test/harness ci --ignore-scripts runtime_path="$(npm --prefix nodejs run --silent prepare:runtime -- --print-path)" diff --git a/.github/workflows/nodejs-sdk-tests.yml b/.github/workflows/nodejs-sdk-tests.yml index 8c4b5ca204..c7674d5809 100644 --- a/.github/workflows/nodejs-sdk-tests.yml +++ b/.github/workflows/nodejs-sdk-tests.yml @@ -119,7 +119,6 @@ jobs: workdir: /workspace/nodejs transport: ${{ matrix.transport }} command: | - apk add --no-cache bash git npm ci --ignore-scripts npm run build (cd /workspace/test/harness && npm ci --ignore-scripts) diff --git a/.github/workflows/python-sdk-tests.yml b/.github/workflows/python-sdk-tests.yml index eaf67bb903..b4c8b89f10 100644 --- a/.github/workflows/python-sdk-tests.yml +++ b/.github/workflows/python-sdk-tests.yml @@ -121,7 +121,7 @@ jobs: workdir: /workspace transport: ${{ matrix.transport }} command: | - apk add --no-cache bash git nodejs npm + apk add --no-cache nodejs npm python -m pip install --no-cache-dir uv ( diff --git a/.github/workflows/rust-sdk-tests.yml b/.github/workflows/rust-sdk-tests.yml index fd9b301a77..29dec07506 100644 --- a/.github/workflows/rust-sdk-tests.yml +++ b/.github/workflows/rust-sdk-tests.yml @@ -364,7 +364,7 @@ jobs: command: | rustc --print cfg | grep -Fqx "target_arch=\"aarch64\"" rustc --print cfg | grep -Fqx "target_env=\"musl\"" - apk add --no-cache bash build-base git pkgconf openssl-dev nodejs npm + 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" diff --git a/dotnet/test/Unit/MSBuildTargetsTests.cs b/dotnet/test/Unit/MSBuildTargetsTests.cs index 09da4cbdbf..fd6ffa6ac8 100644 --- a/dotnet/test/Unit/MSBuildTargetsTests.cs +++ b/dotnet/test/Unit/MSBuildTargetsTests.cs @@ -289,14 +289,21 @@ private static string GetReleasePlatform() : "x64"; if (OperatingSystem.IsWindows()) return $"win32-{arch}"; if (OperatingSystem.IsMacOS()) return $"darwin-{arch}"; - var platform = System.Runtime.InteropServices.RuntimeInformation.RuntimeIdentifier.StartsWith( - "linux-musl-", - StringComparison.Ordinal) - ? "linuxmusl" - : "linux"; + var platform = IsMusl() ? "linuxmusl" : "linux"; return $"{platform}-{arch}"; } + private static bool IsMusl() + { +#if NETFRAMEWORK + return false; +#else + return System.Runtime.InteropServices.RuntimeInformation.RuntimeIdentifier.StartsWith( + "linux-musl-", + StringComparison.Ordinal); +#endif + } + private static string ComputeSha256(byte[] contents) { #if NETFRAMEWORK @@ -510,11 +517,7 @@ private static string GetPortableRid() _ => "osx-x64", }; } - var os = System.Runtime.InteropServices.RuntimeInformation.RuntimeIdentifier.StartsWith( - "linux-musl-", - StringComparison.Ordinal) - ? "linux-musl" - : "linux"; + var os = IsMusl() ? "linux-musl" : "linux"; var architecture = System.Runtime.InteropServices.RuntimeInformation.OSArchitecture switch { System.Runtime.InteropServices.Architecture.Arm64 => "arm64", diff --git a/dotnet/test/Unit/RuntimeWrapperTests.cs b/dotnet/test/Unit/RuntimeWrapperTests.cs index 2c6a92171b..e7c77ab92d 100644 --- a/dotnet/test/Unit/RuntimeWrapperTests.cs +++ b/dotnet/test/Unit/RuntimeWrapperTests.cs @@ -122,9 +122,7 @@ private static string GetPortableRid() { var os = OperatingSystem.IsWindows() ? "win" : OperatingSystem.IsMacOS() ? "osx" - : System.Runtime.InteropServices.RuntimeInformation.RuntimeIdentifier.StartsWith( - "linux-musl-", - StringComparison.Ordinal) + : IsMusl() ? "linux-musl" : "linux"; var architecture = System.Runtime.InteropServices.RuntimeInformation.OSArchitecture switch @@ -135,4 +133,15 @@ private static string GetPortableRid() }; return $"{os}-{architecture}"; } + + private static bool IsMusl() + { +#if NETFRAMEWORK + return false; +#else + return System.Runtime.InteropServices.RuntimeInformation.RuntimeIdentifier.StartsWith( + "linux-musl-", + StringComparison.Ordinal); +#endif + } }