Skip to content

Commit 477398e

Browse files
committed
Pin Copilot CLI version across all CI paths via pom.xml
Previously, three different code paths determined the Copilot CLI version inconsistently: - Path A (build-test.yml) installed the CLI from the cloned reference impl pinned by .lastmerge — reproducible. - Path B (run-smoke-test.yml, update-copilot-dependency.yml via the setup-copilot action) ran `npm install -g @github/copilot` with no version pin — floated to whatever was latest on npm. - Path C (local dev) fell through to whatever `copilot` was on PATH (often whatever VS Code Insiders had installed). When the CLI protocol vocabulary changed between 1.0.35 and 1.0.39, this inconsistency made it hard to land reference-impl merges that required a specific CLI version. This change makes pom.xml the single source of truth for the @github/copilot version that all paths must use, while keeping .lastmerge as the source of truth for the reference implementation commit. Changes: - pom.xml: replace the PRIMER_TO_REPLACE placeholder in the property <readonly-copilot-sdk-ref-impl-version-from-lastmerge-file-updated-by-weekly-reference-impl-sync> with the current value (^1.0.36-0) extracted from the cloned reference impl's nodejs/package.json. Add a doc-comment explaining that the property is auto-managed and is the canonical CLI version for all CI paths. - .github/scripts/reference-impl-sync/sync-cli-version-from-reference-impl.sh (new): reads dependencies."@github/copilot" from the reference impl's nodejs/package.json (via node) and rewrites the pom.xml property in place. Locates the repo root by walking up from the script's own location until it finds a pom.xml, so it is resilient to relocation and works from any CWD. Uses portable sed for the rewrite. - .github/scripts/reference-impl-sync/merge-reference-impl-finish.sh: call the new sync script right after writing .lastmerge, and stage pom.xml alongside .lastmerge in the same commit. .lastmerge and the CLI version property now always move together. - .github/actions/setup-copilot/action.yml: read the pinned version from pom.xml using sed and run `npm install -g "@github/copilot@VERSION"` instead of installing the latest. Fail fast if the property is unset or still the primer. Expose the resolved version as an action output. - CONTRIBUTING.md: document the local-dev workflow that mirrors what build-test.yml does in CI. The cloned reference impl contains two separate package.json files that both pin @github/copilot: * target/copilot-sdk/nodejs/package.json (^1.0.36-0) — the SDK-test pin; this is the CLI the tests must run against. * target/copilot-sdk/test/harness/package.json (^1.0.32) — the replay-harness pin; incidental, NOT the CLI under test. `mvn generate-test-resources` runs `npm install` only in the harness subtree, so a generic `find target -path '*/@github/copilot/index.js'` picks the wrong (older) copy. The instructions now: 1. Run `npm ci` in target/copilot-sdk/nodejs/ explicitly. 2. Scope the COPILOT_CLI_PATH lookup to '*/nodejs/node_modules/...' so the harness copy can never be selected, even if both are present. Both POSIX (find) and PowerShell (Get-ChildItem) forms are tightened. - .gitignore: minor housekeeping. Verified mvn clean package -Pskip-test-harness -DskipTests succeeds with the new POM, the sync script correctly rewrites the property when invoked from any CWD, and the tightened CONTRIBUTING.md sequence resolves COPILOT_CLI_PATH to the 1.0.36-0 CLI under target/copilot-sdk/nodejs.
1 parent 68beab9 commit 477398e

6 files changed

Lines changed: 183 additions & 6 deletions

File tree

.github/actions/setup-copilot/action.yml

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,36 @@ outputs:
44
cli-path:
55
description: "Path to the Copilot CLI executable"
66
value: ${{ steps.cli-path.outputs.path }}
7+
cli-version:
8+
description: "Pinned @github/copilot version installed (read from pom.xml)"
9+
value: ${{ steps.cli-version.outputs.version }}
710
runs:
811
using: "composite"
912
steps:
1013
- uses: actions/setup-node@v6
1114
with:
1215
node-version: 22
13-
- name: Install Copilot CLI
14-
run: npm install -g @github/copilot
16+
- name: Read pinned @github/copilot version from pom.xml
17+
id: cli-version
1518
shell: bash
19+
# The version is the SINGLE SOURCE OF TRUTH for the Copilot CLI version
20+
# used across all CI paths. It is kept in sync with the reference
21+
# implementation pinned in .lastmerge by
22+
# .github/scripts/reference-impl-sync/sync-cli-version-from-reference-impl.sh.
23+
run: |
24+
PROP="readonly-copilot-sdk-ref-impl-version-from-lastmerge-file-updated-by-weekly-reference-impl-sync"
25+
VERSION=$(sed -n "s|.*<${PROP}>\(.*\)</${PROP}>.*|\1|p" pom.xml | head -n 1 | tr -d '[:space:]')
26+
if [[ -z "$VERSION" || "$VERSION" == "PRIMER_TO_REPLACE" ]]; then
27+
echo "::error::Could not read pinned @github/copilot version from pom.xml property <${PROP}>" >&2
28+
exit 1
29+
fi
30+
echo "Pinned @github/copilot version: $VERSION"
31+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
32+
- name: Install Copilot CLI (pinned to pom.xml version)
33+
shell: bash
34+
env:
35+
CLI_VERSION: ${{ steps.cli-version.outputs.version }}
36+
run: npm install -g "@github/copilot@${CLI_VERSION}"
1637
- name: Set CLI path
1738
id: cli-path
1839
run: echo "path=$(which copilot)" >> $GITHUB_OUTPUT

.github/scripts/reference-impl-sync/merge-reference-impl-finish.sh

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
# Finalises a reference implementation merge:
66
# 1. Runs format + test + build (via format-and-test.sh)
77
# 2. Updates .lastmerge to reference implementation HEAD
8-
# 3. Commits the .lastmerge update
9-
# 4. Pushes the branch to origin
8+
# 3. Syncs the @github/copilot version property in pom.xml from the
9+
# cloned reference implementation's nodejs/package.json
10+
# 4. Commits the .lastmerge + pom.xml updates
11+
# 5. Pushes the branch to origin
1012
#
1113
# Usage: ./.github/scripts/reference-impl-sync/merge-reference-impl-finish.sh
1214
# ./.github/scripts/reference-impl-sync/merge-reference-impl-finish.sh --skip-tests
@@ -48,7 +50,13 @@ echo "▸ Updating .lastmerge…"
4850
NEW_COMMIT=$(cd "$REFERENCE_IMPL_DIR" && git rev-parse origin/main)
4951
echo "$NEW_COMMIT" > "$ROOT_DIR/.lastmerge"
5052

51-
git add .lastmerge
53+
# ── 2b. Sync pom.xml @github/copilot version ─────────────────
54+
# Keeps the canonical CLI version in pom.xml aligned with what the
55+
# reference implementation pinned in .lastmerge depends on.
56+
echo "▸ Syncing @github/copilot version in pom.xml from reference implementation…"
57+
"$ROOT_DIR/.github/scripts/reference-impl-sync/sync-cli-version-from-reference-impl.sh" "$REFERENCE_IMPL_DIR"
58+
59+
git add .lastmerge pom.xml
5260
git commit -m "Update .lastmerge to $NEW_COMMIT"
5361

5462
# ── 3. Push branch ───────────────────────────────────────────
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/env bash
2+
# ──────────────────────────────────────────────────────────────
3+
# sync-cli-version-from-reference-impl.sh
4+
#
5+
# Reads the @github/copilot version specifier from the cloned
6+
# reference implementation's nodejs/package.json, and updates the
7+
# corresponding property in pom.xml:
8+
#
9+
# <readonly-copilot-sdk-ref-impl-version-from-lastmerge-file-updated-by-weekly-reference-impl-sync>
10+
#
11+
# This keeps the canonical Copilot CLI version (declared in pom.xml)
12+
# in sync with whatever the reference implementation pinned in
13+
# .lastmerge depends on. All workflows that install the Copilot CLI
14+
# (build-test.yml — implicitly via cloned SDK, run-smoke-test.yml and
15+
# update-copilot-dependency.yml — via the setup-copilot action) read
16+
# this single property so every CI path uses the same CLI version.
17+
#
18+
# Usage:
19+
# ./sync-cli-version-from-reference-impl.sh <reference-impl-dir>
20+
#
21+
# Or, when invoked from merge-reference-impl-finish.sh, sources
22+
# REFERENCE_IMPL_DIR from the .merge-env file.
23+
# ──────────────────────────────────────────────────────────────
24+
set -euo pipefail
25+
26+
# Locate the repo root by walking up from this script until we find a pom.xml.
27+
# This is resilient to the script being moved to a different depth under
28+
# .github/scripts/ in the future.
29+
find_repo_root() {
30+
local dir
31+
dir="$(cd "$(dirname "$0")" && pwd)"
32+
while [[ "$dir" != "/" ]]; do
33+
if [[ -f "$dir/pom.xml" ]]; then
34+
echo "$dir"
35+
return 0
36+
fi
37+
dir="$(dirname "$dir")"
38+
done
39+
echo "❌ Could not locate repo root (no pom.xml found above $(dirname "$0"))" >&2
40+
return 1
41+
}
42+
ROOT_DIR="$(find_repo_root)"
43+
44+
REFERENCE_IMPL_DIR="${1:-${REFERENCE_IMPL_DIR:-}}"
45+
if [[ -z "$REFERENCE_IMPL_DIR" ]]; then
46+
echo "❌ Usage: $0 <reference-impl-dir>" >&2
47+
echo " or set REFERENCE_IMPL_DIR in the environment." >&2
48+
exit 1
49+
fi
50+
51+
PKG_JSON="$REFERENCE_IMPL_DIR/nodejs/package.json"
52+
if [[ ! -f "$PKG_JSON" ]]; then
53+
echo "❌ Cannot find $PKG_JSON" >&2
54+
exit 1
55+
fi
56+
57+
# node is always available since the reference implementation uses npm.
58+
CLI_VERSION=$(node -e \
59+
"const fs=require('fs');const p=JSON.parse(fs.readFileSync(process.argv[1],'utf8'));const v=(p.dependencies&&p.dependencies['@github/copilot'])||(p.devDependencies&&p.devDependencies['@github/copilot']);if(!v){process.exit(2);}process.stdout.write(v);" \
60+
"$PKG_JSON")
61+
62+
if [[ -z "$CLI_VERSION" ]]; then
63+
echo "❌ Could not extract @github/copilot version from $PKG_JSON" >&2
64+
exit 1
65+
fi
66+
67+
POM="$ROOT_DIR/pom.xml"
68+
PROP="readonly-copilot-sdk-ref-impl-version-from-lastmerge-file-updated-by-weekly-reference-impl-sync"
69+
70+
if ! grep -q "<${PROP}>" "$POM"; then
71+
echo "❌ Property <${PROP}> not found in $POM" >&2
72+
exit 1
73+
fi
74+
75+
# Use a portable sed invocation (works on both BSD/macOS and GNU/Linux).
76+
TMP="$(mktemp)"
77+
sed -E "s|<${PROP}>[^<]*</${PROP}>|<${PROP}>${CLI_VERSION}</${PROP}>|" "$POM" > "$TMP"
78+
mv "$TMP" "$POM"
79+
80+
echo "▸ Updated pom.xml: <${PROP}> = ${CLI_VERSION}"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ changebundle.txt*
1313
.settings
1414
scripts/codegen/node_modules/
1515
*~
16+
*.sln

CONTRIBUTING.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,41 @@ If you have ideas for entirely new features, please post an issue or start a dis
3838
1. Push to your fork and [submit a pull request][pr]
3939
1. Pat yourself on the back and wait for your pull request to be reviewed and merged.
4040

41-
### Running tests and linters
41+
### Running locally, including tests and linters
4242

4343
```bash
44+
# Obtain the pinned version of Copilot CLI to the local workarea.
45+
# This clones the reference implementation at the commit pinned in
46+
# .lastmerge, but does NOT run `npm ci` inside its nodejs/ subdir.
47+
mvn generate-test-resources
48+
49+
# Install the pinned Copilot CLI into target/copilot-sdk/nodejs/node_modules
50+
# so the SDK tests use the version declared in
51+
# target/copilot-sdk/nodejs/package.json (the SDK-test pin), NOT the version
52+
# in target/copilot-sdk/test/harness/package.json (the replay-harness pin,
53+
# which is incidental and may be older).
54+
55+
## POSIX
56+
57+
(cd target/copilot-sdk/nodejs && npm ci --ignore-scripts)
58+
59+
## PowerShell
60+
61+
Push-Location target\copilot-sdk\nodejs; if ($?) { npm ci --ignore-scripts }; Pop-Location
62+
63+
# Make it so the pinned Copilot CLI is used for the tests. The patterns
64+
# below are scoped to the `nodejs/node_modules/` subtree so they cannot
65+
# accidentally pick up the older harness copy under
66+
# target/copilot-sdk/test/harness/node_modules/.
67+
68+
## POSIX
69+
70+
export COPILOT_CLI_PATH="$(find "$PWD/target" -type f -path '*/nodejs/node_modules/@github/copilot/index.js' | head -n 1)"
71+
72+
## PowerShell
73+
74+
$env:COPILOT_CLI_PATH = (Get-ChildItem -Path "$PWD\target" -Recurse -Filter 'index.js' -File | Where-Object { $_.FullName -match '[\\/]nodejs[\\/]node_modules[\\/]@github[\\/]copilot[\\/]index\.js$' } | Select-Object -First 1 -ExpandProperty FullName)
75+
4476
# Build and run all tests
4577
mvn clean verify
4678

@@ -54,6 +86,20 @@ mvn spotless:apply
5486
mvn spotless:check
5587
```
5688
89+
Assuming you are in the same shell you used to run the above commands, to run this exact Copilot CLI locally you can do the following.
90+
91+
### POSIX
92+
93+
```bash
94+
node ${COPILOT_CLI_PATH}
95+
```
96+
97+
### PowerShell
98+
99+
```PowerShell
100+
node $env:COPILOT_CLI_PATH
101+
```
102+
57103
Here are a few things you can do that will increase the likelihood of your pull request being accepted:
58104
59105
- Write tests.

pom.xml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,27 @@
5353
<skip.test.harness>false</skip.test.harness>
5454
<!-- Extra JVM args for Surefire; overridden by the jdk21+ profile -->
5555
<surefire.jvm.args />
56+
<!--
57+
The version of the @github/copilot npm package that the reference implementation
58+
commit pinned in .lastmerge depends on. Mirrors the value of dependencies."@github/copilot"
59+
in target/copilot-sdk/nodejs/package.json after the reference impl is cloned/reset to the
60+
commit in .lastmerge.
61+
62+
The previously mentioned package.json contains the SINGLE
63+
SOURCE OF TRUTH for the Copilot CLI version that all paths
64+
(build-test.yml, run-smoke-test.yml,
65+
update-copilot-dependency.yml, setup-copilot action) must
66+
pin to. It is updated automatically by
67+
.github/scripts/reference-impl-sync/sync-cli-version-from-reference-impl.sh,
68+
which is called from merge-reference-impl-finish.sh
69+
whenever .lastmerge is updated.
70+
71+
DO NOT EDIT MANUALLY. To update, run the
72+
reference-impl-sync workflow and deal with the subsequent
73+
PR.
74+
-->
75+
<readonly-copilot-sdk-ref-impl-version-from-lastmerge-file-updated-by-weekly-reference-impl-sync>^1.0.36-0</readonly-copilot-sdk-ref-impl-version-from-lastmerge-file-updated-by-weekly-reference-impl-sync>
76+
5677
</properties>
5778

5879
<dependencies>

0 commit comments

Comments
 (0)