|
| 1 | +name: "SDK Canary (runtime compat gate)" |
| 2 | + |
| 3 | +# Nightly-style canary compatibility gate: installs an explicit version of the |
| 4 | +# @github/copilot runtime, builds the Node SDK, and runs the Node e2e suite |
| 5 | +# against it. This proves runtime <-> SDK compatibility. No publishing happens |
| 6 | +# here. |
| 7 | + |
| 8 | +env: |
| 9 | + HUSKY: 0 |
| 10 | + |
| 11 | +on: |
| 12 | + workflow_dispatch: |
| 13 | + inputs: |
| 14 | + runtime_version: |
| 15 | + description: "Exact @github/copilot version to test (e.g. 1.0.69 or 1.0.70-canary.<sha>)" |
| 16 | + required: true |
| 17 | + type: string |
| 18 | + runtime_source: |
| 19 | + description: "Where to install the runtime from" |
| 20 | + required: true |
| 21 | + type: choice |
| 22 | + options: |
| 23 | + - public |
| 24 | + - feed |
| 25 | + default: public |
| 26 | + # TEMPORARY: branch-push trigger so we can validate the pipeline before the |
| 27 | + # workflow_dispatch entrypoint exists on main. Push events carry no dispatch |
| 28 | + # inputs, so the resolve job falls back to the currently pinned runtime from |
| 29 | + # public npm. Remove this trigger once the workflow lands on main. |
| 30 | + push: |
| 31 | + branches: [mackinnonbuck-sdk-canary-compat-gate] |
| 32 | + |
| 33 | +permissions: |
| 34 | + contents: read |
| 35 | + id-token: write |
| 36 | + |
| 37 | +jobs: |
| 38 | + resolve: |
| 39 | + name: "Resolve runtime inputs" |
| 40 | + if: github.event.repository.fork == false |
| 41 | + runs-on: ubuntu-latest |
| 42 | + outputs: |
| 43 | + RUNTIME_VERSION: ${{ steps.normalize.outputs.RUNTIME_VERSION }} |
| 44 | + RUNTIME_SOURCE: ${{ steps.normalize.outputs.RUNTIME_SOURCE }} |
| 45 | + steps: |
| 46 | + - uses: actions/checkout@v6.0.2 |
| 47 | + |
| 48 | + # Normalize whichever trigger fired into a single (RUNTIME_VERSION, |
| 49 | + # RUNTIME_SOURCE) pair that every downstream step references. Adding a |
| 50 | + # `repository_dispatch: types: [runtime-canary]` trigger later is purely |
| 51 | + # additive: add one more case that reads client_payload and defaults |
| 52 | + # source to 'feed'. No downstream rework required. |
| 53 | + - name: Normalize inputs |
| 54 | + id: normalize |
| 55 | + env: |
| 56 | + EVENT_NAME: ${{ github.event_name }} |
| 57 | + INPUT_VERSION: ${{ inputs.runtime_version }} |
| 58 | + INPUT_SOURCE: ${{ inputs.runtime_source }} |
| 59 | + run: | |
| 60 | + set -euo pipefail |
| 61 | + case "$EVENT_NAME" in |
| 62 | + workflow_dispatch) |
| 63 | + VERSION="$INPUT_VERSION" |
| 64 | + SOURCE="$INPUT_SOURCE" |
| 65 | + ;; |
| 66 | + push) |
| 67 | + # No dispatch inputs on push: fall back to the currently pinned |
| 68 | + # runtime from public npm so the run is self-consistent and a green |
| 69 | + # result proves the pipeline mechanics. |
| 70 | + SOURCE="public" |
| 71 | + VERSION="$(node -e "const v=require('./nodejs/package.json').dependencies['@github/copilot']; process.stdout.write(String(v).replace(/^[\^~]/, ''))")" |
| 72 | + ;; |
| 73 | + *) |
| 74 | + echo "::error::Unsupported event '$EVENT_NAME'." |
| 75 | + exit 1 |
| 76 | + ;; |
| 77 | + esac |
| 78 | + if [ -z "$VERSION" ]; then echo "::error::Could not determine runtime version."; exit 1; fi |
| 79 | + if [ -z "$SOURCE" ]; then SOURCE="public"; fi |
| 80 | + echo "Resolved RUNTIME_VERSION=$VERSION RUNTIME_SOURCE=$SOURCE" |
| 81 | + echo "RUNTIME_VERSION=$VERSION" >> "$GITHUB_OUTPUT" |
| 82 | + echo "RUNTIME_SOURCE=$SOURCE" >> "$GITHUB_OUTPUT" |
| 83 | +
|
| 84 | + - name: Validate runtime version (semver) |
| 85 | + env: |
| 86 | + RUNTIME_VERSION: ${{ steps.normalize.outputs.RUNTIME_VERSION }} |
| 87 | + run: | |
| 88 | + if [[ ! "$RUNTIME_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9._-]+)?$ ]]; then |
| 89 | + echo "::error::Invalid runtime version '$RUNTIME_VERSION'. Expected semver (e.g. 1.0.69 or 1.0.70-canary.abc123)." |
| 90 | + exit 1 |
| 91 | + fi |
| 92 | +
|
| 93 | + test: |
| 94 | + name: "e2e (${{ matrix.os }})" |
| 95 | + needs: resolve |
| 96 | + if: github.event.repository.fork == false |
| 97 | + environment: cicd |
| 98 | + strategy: |
| 99 | + fail-fast: false |
| 100 | + matrix: |
| 101 | + os: [ubuntu-latest, macos-latest, windows-latest] |
| 102 | + runs-on: ${{ matrix.os }} |
| 103 | + env: |
| 104 | + POWERSHELL_UPDATECHECK: Off |
| 105 | + RUNTIME_VERSION: ${{ needs.resolve.outputs.RUNTIME_VERSION }} |
| 106 | + RUNTIME_SOURCE: ${{ needs.resolve.outputs.RUNTIME_SOURCE }} |
| 107 | + defaults: |
| 108 | + run: |
| 109 | + shell: bash |
| 110 | + working-directory: ./nodejs |
| 111 | + steps: |
| 112 | + - uses: actions/checkout@v6.0.2 |
| 113 | + |
| 114 | + - uses: actions/setup-node@v6 |
| 115 | + with: |
| 116 | + cache: "npm" |
| 117 | + cache-dependency-path: "./nodejs/package-lock.json" |
| 118 | + node-version: 22 |
| 119 | + |
| 120 | + - name: Install SDK dependencies |
| 121 | + run: npm ci --ignore-scripts |
| 122 | + |
| 123 | + - name: Install test harness dependencies |
| 124 | + working-directory: ./test/harness |
| 125 | + run: npm ci --ignore-scripts |
| 126 | + |
| 127 | + - name: Azure Login (OIDC -> id-cpd-ci) |
| 128 | + if: env.RUNTIME_SOURCE == 'feed' |
| 129 | + uses: azure/login@532459ea530d8321f2fb9bb10d1e0bcf23869a43 # v3.0.0 |
| 130 | + with: |
| 131 | + client-id: "${{ vars.CPD_ID_CLIENT_ID }}" # id-cpd-ci |
| 132 | + tenant-id: "${{ vars.CPD_ID_TENANT_ID }}" |
| 133 | + allow-no-subscriptions: true |
| 134 | + |
| 135 | + # Route ONLY @github/* (the runtime + its 8 platform packages) to the |
| 136 | + # internal feed via a scoped registry. All other deps (e.g. detect-libc) |
| 137 | + # still resolve from public npm. A global --registry would break because |
| 138 | + # detect-libc is not on the feed. |
| 139 | + - name: Configure canary feed (.npmrc) |
| 140 | + if: env.RUNTIME_SOURCE == 'feed' |
| 141 | + run: | |
| 142 | + set -euo pipefail |
| 143 | + TOKEN="$(az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv)" |
| 144 | + echo "::add-mask::$TOKEN" |
| 145 | + NPMRC="$(cat <<EOF |
| 146 | + @github:registry=https://pkgs.dev.azure.com/devdiv/_packaging/copilot-canary-test/npm/registry/ |
| 147 | + //pkgs.dev.azure.com/devdiv/_packaging/copilot-canary-test/npm/registry/:_authToken=${TOKEN} |
| 148 | + //pkgs.dev.azure.com/devdiv/_packaging/copilot-canary-test/npm/:_authToken=${TOKEN} |
| 149 | + EOF |
| 150 | + )" |
| 151 | + printf '%s\n' "$NPMRC" > .npmrc |
| 152 | + printf '%s\n' "$NPMRC" > ../test/harness/.npmrc |
| 153 | + echo "Wrote scoped @github registry .npmrc to ./nodejs and ./test/harness" |
| 154 | +
|
| 155 | + - name: Override runtime version |
| 156 | + run: | |
| 157 | + set -euo pipefail |
| 158 | + echo "Installing @github/copilot@${RUNTIME_VERSION} (source: ${RUNTIME_SOURCE})" |
| 159 | + npm install "@github/copilot@${RUNTIME_VERSION}" --save-exact --ignore-scripts |
| 160 | + ( cd ../test/harness && npm install "@github/copilot@${RUNTIME_VERSION}" --save-exact --ignore-scripts ) |
| 161 | +
|
| 162 | + - name: Verify installed runtime |
| 163 | + run: | |
| 164 | + set -euo pipefail |
| 165 | + node -e ' |
| 166 | + const fs = require("fs"); |
| 167 | + const expected = process.env.RUNTIME_VERSION; |
| 168 | + const pkg = require("./node_modules/@github/copilot/package.json"); |
| 169 | + if (pkg.version !== expected) { |
| 170 | + console.error(`::error::Installed @github/copilot version ${pkg.version} does not match requested ${expected}`); |
| 171 | + process.exit(1); |
| 172 | + } |
| 173 | + const dir = "./node_modules/@github"; |
| 174 | + const entries = fs.readdirSync(dir).filter((d) => d.startsWith("copilot-")); |
| 175 | + const plat = process.platform === "win32" ? "win32" : process.platform === "darwin" ? "darwin" : "linux"; |
| 176 | + const arch = process.arch; |
| 177 | + const match = entries.find((d) => d.includes(plat) && d.includes(arch)); |
| 178 | + if (!match) { |
| 179 | + console.error(`::error::No @github/copilot platform optional dep for ${plat}-${arch}. Present: ${entries.join(", ") || "(none)"}`); |
| 180 | + process.exit(1); |
| 181 | + } |
| 182 | + console.log(`Verified @github/copilot@${pkg.version} with platform package @github/${match}`); |
| 183 | + ' |
| 184 | +
|
| 185 | + - name: Build SDK |
| 186 | + run: npm run build |
| 187 | + |
| 188 | + - name: Warm up PowerShell |
| 189 | + if: runner.os == 'Windows' |
| 190 | + run: pwsh.exe -Command "Write-Host 'PowerShell ready'" |
| 191 | + |
| 192 | + - name: Run Node.js SDK e2e tests |
| 193 | + env: |
| 194 | + COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} |
| 195 | + run: npm test |
0 commit comments