Skip to content

Fix canary bypass audit step failing before checkout #5

Fix canary bypass audit step failing before checkout

Fix canary bypass audit step failing before checkout #5

Workflow file for this run

name: "SDK Canary (runtime compat gate)"
# Nightly-style canary compatibility gate: installs an explicit version of the
# @github/copilot runtime, builds the Node SDK, and runs the Node e2e suite
# against it. This proves runtime <-> SDK compatibility. No publishing happens
# here.
env:
HUSKY: 0
on:
workflow_dispatch:
inputs:
runtime_version:
description: "Exact @github/copilot version to test (e.g. 1.0.69 or 1.0.70-canary.<sha>)"
required: true
type: string
runtime_source:
description: "Where to install the runtime from"
required: true
type: choice
options:
- public
- feed
default: public
force_publish:
description: "Publish the SDK canary even if the e2e gate fails (use ONLY for a known flake). Bypasses the e2e signal; the build + feed-only guards still apply."
required: false
type: boolean
default: false
# TEMPORARY: branch-push trigger so we can validate the pipeline before the
# workflow_dispatch entrypoint exists on main. Push events carry no dispatch
# inputs, so the resolve job falls back to the currently pinned runtime from
# public npm. Remove this trigger once the workflow lands on main.
push: # TEMP: remove at finalize (coupled with the publish.if `event==push` clause)
branches: [mackinnonbuck-sdk-canary-compat-gate]
permissions:
contents: read
id-token: write
jobs:
resolve:
name: "Resolve runtime inputs"
if: github.event.repository.fork == false
runs-on: ubuntu-latest
outputs:
RUNTIME_VERSION: ${{ steps.normalize.outputs.RUNTIME_VERSION }}
RUNTIME_SOURCE: ${{ steps.normalize.outputs.RUNTIME_SOURCE }}
steps:
- uses: actions/checkout@v6.0.2
# Normalize whichever trigger fired into a single (RUNTIME_VERSION,
# RUNTIME_SOURCE) pair that every downstream step references. Adding a
# `repository_dispatch: types: [runtime-canary]` trigger later is purely
# additive: add one more case that reads client_payload and defaults
# source to 'feed'. No downstream rework required.
- name: Normalize inputs
id: normalize
env:
EVENT_NAME: ${{ github.event_name }}
INPUT_VERSION: ${{ inputs.runtime_version }}
INPUT_SOURCE: ${{ inputs.runtime_source }}
run: |
set -euo pipefail
case "$EVENT_NAME" in
workflow_dispatch)
VERSION="$INPUT_VERSION"
SOURCE="$INPUT_SOURCE"
;;
push)
# No dispatch inputs on push: fall back to the currently pinned
# runtime from public npm so the run is self-consistent and a green
# result proves the pipeline mechanics.
SOURCE="public"
VERSION="$(node -e "const v=require('./nodejs/package.json').dependencies['@github/copilot']; process.stdout.write(String(v).replace(/^[\^~]/, ''))")"
;;
*)
echo "::error::Unsupported event '$EVENT_NAME'."
exit 1
;;
esac
if [ -z "$VERSION" ]; then echo "::error::Could not determine runtime version."; exit 1; fi
if [ -z "$SOURCE" ]; then SOURCE="public"; fi
echo "Resolved RUNTIME_VERSION=$VERSION RUNTIME_SOURCE=$SOURCE"
echo "RUNTIME_VERSION=$VERSION" >> "$GITHUB_OUTPUT"
echo "RUNTIME_SOURCE=$SOURCE" >> "$GITHUB_OUTPUT"
- name: Validate runtime version (semver)
env:
RUNTIME_VERSION: ${{ steps.normalize.outputs.RUNTIME_VERSION }}
run: |
if [[ ! "$RUNTIME_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9._-]+)?$ ]]; then
echo "::error::Invalid runtime version '$RUNTIME_VERSION'. Expected semver (e.g. 1.0.69 or 1.0.70-canary.abc123)."
exit 1
fi
test:
name: "e2e (${{ matrix.os }})"
needs: resolve
if: github.event.repository.fork == false
environment: cicd
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
env:
POWERSHELL_UPDATECHECK: Off
RUNTIME_VERSION: ${{ needs.resolve.outputs.RUNTIME_VERSION }}
RUNTIME_SOURCE: ${{ needs.resolve.outputs.RUNTIME_SOURCE }}
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 SDK dependencies
run: npm ci --ignore-scripts
- name: Install test harness dependencies
working-directory: ./test/harness
run: npm ci --ignore-scripts
- name: Azure Login (OIDC -> id-cpd-ci)
if: env.RUNTIME_SOURCE == 'feed'
uses: azure/login@532459ea530d8321f2fb9bb10d1e0bcf23869a43 # v3.0.0
with:
client-id: "${{ vars.CPD_ID_CLIENT_ID }}" # id-cpd-ci
tenant-id: "${{ vars.CPD_ID_TENANT_ID }}"
allow-no-subscriptions: true
# Route ONLY @github/* (the runtime + its 8 platform packages) to the
# internal feed via a scoped registry. All other deps (e.g. detect-libc)
# still resolve from public npm. A global --registry would break because
# detect-libc is not on the feed.
- name: Configure canary feed (.npmrc)
if: env.RUNTIME_SOURCE == 'feed'
run: |
set -euo pipefail
TOKEN="$(az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv)"
echo "::add-mask::$TOKEN"
NPMRC="$(cat <<EOF
@github:registry=https://pkgs.dev.azure.com/devdiv/_packaging/copilot-canary-test/npm/registry/
//pkgs.dev.azure.com/devdiv/_packaging/copilot-canary-test/npm/registry/:_authToken=${TOKEN}
//pkgs.dev.azure.com/devdiv/_packaging/copilot-canary-test/npm/:_authToken=${TOKEN}
EOF
)"
printf '%s\n' "$NPMRC" > .npmrc
printf '%s\n' "$NPMRC" > ../test/harness/.npmrc
echo "Wrote scoped @github registry .npmrc to ./nodejs and ./test/harness"
- name: Override runtime version
run: |
set -euo pipefail
echo "Installing @github/copilot@${RUNTIME_VERSION} (source: ${RUNTIME_SOURCE})"
npm install "@github/copilot@${RUNTIME_VERSION}" --save-exact --ignore-scripts
( cd ../test/harness && npm install "@github/copilot@${RUNTIME_VERSION}" --save-exact --ignore-scripts )
- name: Verify installed runtime
run: |
set -euo pipefail
node -e '
const fs = require("fs");
const expected = process.env.RUNTIME_VERSION;
const pkg = require("./node_modules/@github/copilot/package.json");
if (pkg.version !== expected) {
console.error(`::error::Installed @github/copilot version ${pkg.version} does not match requested ${expected}`);
process.exit(1);
}
const dir = "./node_modules/@github";
const entries = fs.readdirSync(dir).filter((d) => d.startsWith("copilot-"));
const plat = process.platform === "win32" ? "win32" : process.platform === "darwin" ? "darwin" : "linux";
const arch = process.arch;
const match = entries.find((d) => d.includes(plat) && d.includes(arch));
if (!match) {
console.error(`::error::No @github/copilot platform optional dep for ${plat}-${arch}. Present: ${entries.join(", ") || "(none)"}`);
process.exit(1);
}
console.log(`Verified @github/copilot@${pkg.version} with platform package @github/${match}`);
'
- name: Build SDK
run: npm run build
- name: Warm up PowerShell
if: runner.os == 'Windows'
run: pwsh.exe -Command "Write-Host 'PowerShell ready'"
- name: Run Node.js SDK e2e tests
env:
COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }}
run: npm test
publish:
name: "Publish SDK canary (internal feed)"
needs: [resolve, test]
# Normally publish only runs when the e2e gate is green. Two bypasses:
# - workflow_dispatch with force_publish=true: a human-acknowledged flake
# override (audited via the actor on the run).
# - github.event_name == 'push': TEMP: remove at finalize — branch-validation
# bypass coupled with the temporary push trigger so we can exercise the
# publish + bypass paths on this branch. Drop the `github.event_name ==
# 'push'` clause from the `if:` below together with the push trigger; after
# that, force is honored only on real dispatches.
# The bypass only skips the e2e *signal* — the publish job still runs the
# build (so a broken build can't publish) and enforces feed-only + read-back.
if: >
!cancelled() &&
github.event.repository.fork == false &&
needs.resolve.result == 'success' &&
(needs.test.result == 'success' ||
(github.event_name == 'workflow_dispatch' && inputs.force_publish) ||
github.event_name == 'push')
environment: cicd
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
env:
RUNTIME_VERSION: ${{ needs.resolve.outputs.RUNTIME_VERSION }}
# Org-scoped internal TEST feed. The SDK canary MUST only ever go here,
# never to public npm (@github/copilot-sdk is a live public package).
FEED_URL: https://pkgs.dev.azure.com/devdiv/_packaging/copilot-canary-test/npm/registry/
defaults:
run:
shell: bash
working-directory: ./nodejs
steps:
- name: Warn — publishing despite failed e2e gate (bypass)
# always() so this audit is never skipped by prior-step status; it fires
# specifically when publish proceeded without a green e2e gate. Runs at
# the workspace root because it executes before checkout, so the job's
# default working-directory (./nodejs) does not exist yet.
if: always() && needs.test.result != 'success'
working-directory: ${{ github.workspace }}
run: |
echo "::warning title=e2e gate bypassed::Publishing SDK canary despite a non-passing e2e gate (test job result: ${{ needs.test.result }}). Triggered by '${{ github.actor }}' via '${{ github.event_name }}'${{ (github.event_name == 'workflow_dispatch' && inputs.force_publish) && ' with force_publish=true' || '' }}. The e2e signal was bypassed; build + feed-only + read-back guards still apply."
- uses: actions/checkout@v6.0.2
- uses: actions/setup-node@v6
with:
node-version: 22
# Default public registry: installs build deps and the currently pinned
# runtime. Do NOT write any feed .npmrc or scoped @github:registry line
# here, or npm ci would try to fetch the runtime from the upstream-less
# feed and 404.
- name: Install SDK dependencies
run: npm ci
- name: Compute SDK canary version
id: sdkver
env:
RUN_NUMBER: ${{ github.run_number }}
SHA: ${{ github.sha }}
run: |
set -euo pipefail
SHORT_SHA="${SHA:0:7}"
SDK_VERSION="0.0.0-canary.${RUN_NUMBER}.g${SHORT_SHA}"
if [[ ! "$SDK_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9._-]+)?$ ]]; then
echo "::error::Computed SDK canary version '$SDK_VERSION' is not valid semver."
exit 1
fi
echo "SDK canary version: $SDK_VERSION"
echo "SDK_VERSION=$SDK_VERSION" >> "$GITHUB_OUTPUT"
- name: Set package version and pin runtime dependency
env:
SDK_VERSION: ${{ steps.sdkver.outputs.SDK_VERSION }}
run: |
set -euo pipefail
npm version "$SDK_VERSION" --no-git-tag-version --allow-same-version
# Exact pin (no caret) so the published SDK canary depends on precisely
# the runtime version that was just tested by the e2e gate.
npm pkg set "dependencies.@github/copilot=$RUNTIME_VERSION"
echo "Pinned @github/copilot to $(npm pkg get dependencies.@github/copilot)"
- name: Build SDK
run: npm run build
- name: Azure Login (OIDC -> id-cpd-ci)
uses: azure/login@532459ea530d8321f2fb9bb10d1e0bcf23869a43 # v3.0.0
with:
client-id: "${{ vars.CPD_ID_CLIENT_ID }}" # id-cpd-ci
tenant-id: "${{ vars.CPD_ID_TENANT_ID }}"
allow-no-subscriptions: true
# Auth-only .npmrc: just the two token lines, NO scoped registry line.
# The publish target is supplied explicitly via publishConfig + --registry.
- name: Configure feed auth (.npmrc)
run: |
set -euo pipefail
TOKEN="$(az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv)"
echo "::add-mask::$TOKEN"
cat > .npmrc <<EOF
//pkgs.dev.azure.com/devdiv/_packaging/copilot-canary-test/npm/registry/:_authToken=${TOKEN}
//pkgs.dev.azure.com/devdiv/_packaging/copilot-canary-test/npm/:_authToken=${TOKEN}
EOF
echo "Wrote auth-only .npmrc to ./nodejs"
# Belt and suspenders (2 of 3): pin the publish target in the package too.
- name: Set publishConfig registry
run: npm pkg set "publishConfig.registry=$FEED_URL"
# Belt and suspenders (3 of 3): fail loudly unless the effective publish
# target is the internal feed. Guards against ever reaching public npm.
- name: Assert publish target is the internal feed
run: |
set -euo pipefail
EFFECTIVE="$(npm pkg get publishConfig.registry | tr -d '"')"
echo "Effective publishConfig.registry: $EFFECTIVE"
if [ "$EFFECTIVE" != "$FEED_URL" ]; then
echo "::error::publishConfig.registry ('$EFFECTIVE') is not the internal feed ('$FEED_URL'). Refusing to publish."
exit 1
fi
- name: Publish SDK canary to internal feed
run: npm publish --registry "$FEED_URL"
- name: Read-back verify published canary
env:
SDK_VERSION: ${{ steps.sdkver.outputs.SDK_VERSION }}
run: |
set -euo pipefail
PUBLISHED="$(npm view "@github/copilot-sdk@${SDK_VERSION}" version --registry "$FEED_URL")"
PINNED="$(npm view "@github/copilot-sdk@${SDK_VERSION}" dependencies.@github/copilot --registry "$FEED_URL")"
echo "Published version: $PUBLISHED"
echo "Pinned @github/copilot: $PINNED"
if [ "$PUBLISHED" != "$SDK_VERSION" ]; then
echo "::error::Read-back version '$PUBLISHED' != published '$SDK_VERSION'."
exit 1
fi
if [ "$PINNED" != "$RUNTIME_VERSION" ]; then
echo "::error::Read-back runtime pin '$PINNED' != tested '$RUNTIME_VERSION'."
exit 1
fi
echo "Read-back OK: @github/copilot-sdk@${SDK_VERSION} exact-depends on @github/copilot@${RUNTIME_VERSION}"