Skip to content

Commit 0336508

Browse files
Add SDK canary runtime compatibility gate workflow
Installs an explicit @github/copilot runtime version, builds the Node SDK, and runs the Node e2e suite against it as a runtime<->SDK compat gate. No publishing. A temporary branch-push trigger validates the public path. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 44ec87f commit 0336508

1 file changed

Lines changed: 195 additions & 0 deletions

File tree

.github/workflows/sdk-canary.yml

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
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

Comments
 (0)