Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions .github/actions/setup-copilot/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,36 @@ outputs:
cli-path:
description: "Path to the Copilot CLI executable"
value: ${{ steps.cli-path.outputs.path }}
cli-version:
description: "Pinned @github/copilot version installed (read from pom.xml)"
value: ${{ steps.cli-version.outputs.version }}
runs:
using: "composite"
steps:
- uses: actions/setup-node@v6
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v6
with:
node-version: 22
- name: Install Copilot CLI
run: npm install -g @github/copilot
- name: Read pinned @github/copilot version from pom.xml
id: cli-version
shell: bash
# The version is the SINGLE SOURCE OF TRUTH for the Copilot CLI version
# used across all CI paths. It is kept in sync with the reference
# implementation pinned in .lastmerge by
# .github/scripts/reference-impl-sync/sync-cli-version-from-reference-impl.sh.
run: |
PROP="readonly-copilot-sdk-ref-impl-version-from-lastmerge-file-updated-by-weekly-reference-impl-sync"
VERSION=$(sed -n "s|.*<${PROP}>\(.*\)</${PROP}>.*|\1|p" pom.xml | head -n 1 | tr -d '[:space:]')
if [[ -z "$VERSION" || "$VERSION" == "PRIMER_TO_REPLACE" ]]; then
echo "::error::Could not read pinned @github/copilot version from pom.xml property <${PROP}>" >&2
exit 1
fi
echo "Pinned @github/copilot version: $VERSION"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Install Copilot CLI (pinned to pom.xml version)
shell: bash
env:
CLI_VERSION: ${{ steps.cli-version.outputs.version }}
run: npm install -g "@github/copilot@${CLI_VERSION}"
Comment thread
edburns marked this conversation as resolved.
- name: Set CLI path
id: cli-path
run: echo "path=$(which copilot)" >> $GITHUB_OUTPUT
Expand Down
16 changes: 12 additions & 4 deletions .github/scripts/reference-impl-sync/merge-reference-impl-finish.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
# Finalises a reference implementation merge:
# 1. Runs format + test + build (via format-and-test.sh)
# 2. Updates .lastmerge to reference implementation HEAD
# 3. Commits the .lastmerge update
# 4. Pushes the branch to origin
# 3. Syncs the @github/copilot version property in pom.xml from the
# cloned reference implementation's nodejs/package.json
# 4. Commits the .lastmerge + pom.xml updates
# 5. Pushes the branch to origin
#
# Usage: ./.github/scripts/reference-impl-sync/merge-reference-impl-finish.sh
# ./.github/scripts/reference-impl-sync/merge-reference-impl-finish.sh --skip-tests
Expand Down Expand Up @@ -48,8 +50,14 @@ echo "▸ Updating .lastmerge…"
NEW_COMMIT=$(cd "$REFERENCE_IMPL_DIR" && git rev-parse origin/main)
echo "$NEW_COMMIT" > "$ROOT_DIR/.lastmerge"

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

git add .lastmerge pom.xml
git commit -m "Update .lastmerge to $NEW_COMMIT and sync pom.xml CLI version"

# ── 3. Push branch ───────────────────────────────────────────
echo "▸ Pushing branch $BRANCH_NAME to origin…"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env bash
# ──────────────────────────────────────────────────────────────
# sync-cli-version-from-reference-impl.sh
#
# Reads the @github/copilot version specifier from the cloned
# reference implementation's nodejs/package.json, and updates the
# corresponding property in pom.xml:
#
# <readonly-copilot-sdk-ref-impl-version-from-lastmerge-file-updated-by-weekly-reference-impl-sync>
#
# This keeps the canonical Copilot CLI version (declared in pom.xml)
# in sync with whatever the reference implementation pinned in
# .lastmerge depends on. All workflows that install the Copilot CLI
# (build-test.yml — implicitly via cloned SDK, run-smoke-test.yml and
# update-copilot-dependency.yml — via the setup-copilot action) read
# this single property so every CI path uses the same CLI version.
#
# Usage:
# ./sync-cli-version-from-reference-impl.sh <reference-impl-dir>
#
# Or, when invoked from merge-reference-impl-finish.sh, sources
# REFERENCE_IMPL_DIR from the .merge-env file.
# ──────────────────────────────────────────────────────────────
set -euo pipefail

# Locate the repo root by walking up from this script until we find a pom.xml.
# This is resilient to the script being moved to a different depth under
# .github/scripts/ in the future.
find_repo_root() {
local dir
dir="$(cd "$(dirname "$0")" && pwd)"
while [[ "$dir" != "/" ]]; do
if [[ -f "$dir/pom.xml" ]]; then
echo "$dir"
return 0
fi
dir="$(dirname "$dir")"
done
echo "❌ Could not locate repo root (no pom.xml found above $(dirname "$0"))" >&2
return 1
}
ROOT_DIR="$(find_repo_root)"

REFERENCE_IMPL_DIR="${1:-${REFERENCE_IMPL_DIR:-}}"
if [[ -z "$REFERENCE_IMPL_DIR" ]]; then
echo "❌ Usage: $0 <reference-impl-dir>" >&2
echo " or set REFERENCE_IMPL_DIR in the environment." >&2
exit 1
fi

PKG_JSON="$REFERENCE_IMPL_DIR/nodejs/package.json"
if [[ ! -f "$PKG_JSON" ]]; then
echo "❌ Cannot find $PKG_JSON" >&2
exit 1
fi

# node is always available since the reference implementation uses npm.
CLI_VERSION=$(node -e \
"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']);process.stdout.write(v||'');" \
"$PKG_JSON")
Comment thread
edburns marked this conversation as resolved.

if [[ -z "$CLI_VERSION" ]]; then
echo "❌ Could not extract @github/copilot version from $PKG_JSON" >&2
exit 1
fi

POM="$ROOT_DIR/pom.xml"
PROP="readonly-copilot-sdk-ref-impl-version-from-lastmerge-file-updated-by-weekly-reference-impl-sync"

if ! grep -q "<${PROP}>" "$POM"; then
echo "❌ Property <${PROP}> not found in $POM" >&2
exit 1
fi

# Use a portable sed invocation (works on both BSD/macOS and GNU/Linux).
TMP="$(mktemp)"
sed -E "s|<${PROP}>[^<]*</${PROP}>|<${PROP}>${CLI_VERSION}</${PROP}>|" "$POM" > "$TMP"
mv "$TMP" "$POM"

echo "▸ Updated pom.xml: <${PROP}> = ${CLI_VERSION}"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ changebundle.txt*
.settings
scripts/codegen/node_modules/
*~
*.sln
20 changes: 19 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ If you have ideas for entirely new features, please post an issue or start a dis
1. Push to your fork and [submit a pull request][pr]
1. Pat yourself on the back and wait for your pull request to be reviewed and merged.

### Running tests and linters
### Running locally, including tests and linters

The POM has logic to ensure a known correct installation of Copilot CLI is used when executing the tests. If you want to test against a different installation of Copilot CLI, set the value of the `copilot.cli.path` maven property to the fully qualified path to the Copilot CLI.

```bash
# Build and run all tests
Expand All @@ -54,6 +56,22 @@ mvn spotless:apply
mvn spotless:check
```

## Running the known correct Copilot CLI installation

### POSIX

```bash
export COPILOT_CLI_PATH="$(find "$PWD/target" -type f -path '*/nodejs/node_modules/@github/copilot/index.js' | head -n 1)"
node ${COPILOT_CLI_PATH}
```
Comment thread
edburns marked this conversation as resolved.

### PowerShell

```PowerShell
$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)
node $env:COPILOT_CLI_PATH
```

Here are a few things you can do that will increase the likelihood of your pull request being accepted:

- Write tests.
Expand Down
114 changes: 114 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,53 @@
<!-- Directory where the copilot-sdk repo will be cloned for tests -->
<copilot.sdk.clone.dir>${project.build.directory}/copilot-sdk</copilot.sdk.clone.dir>
<copilot.tests.dir>${copilot.sdk.clone.dir}/test</copilot.tests.dir>
<!--
Path to the Copilot CLI entry point used by the SDK tests. Defaults
to the CLI installed under target/copilot-sdk/nodejs by the
install-nodejs-cli-dependencies execution. Surefire injects this
into the test JVM as the COPILOT_CLI_PATH environment variable, so
`mvn verify` is self-contained and the developer never has to set
it manually. Override on the command line to point at a different
CLI build, e.g.:
mvn verify -Dcopilot.cli.path=/some/other/copilot/index.js
-->
<copilot.cli.path>${copilot.sdk.clone.dir}/nodejs/node_modules/@github/copilot/index.js</copilot.cli.path>
<!-- Set to true (via -Pskip-test-harness) to skip git-clone + npm install of test harness -->
<skip.test.harness>false</skip.test.harness>
<!--
Whether to skip the install-nodejs-cli-dependencies execution
(npm ci of the @github/copilot CLI). Defaults to ${skip.test.harness}
so it tracks the rest of the test-harness setup, but is also
forced to true when Maven tests are skipped (-DskipTests or
-Dmaven.test.skip) via the skip-cli-install-when-tests-skipped
and skip-cli-install-when-maven-test-skip profiles below, so that
non-test builds (e.g. package/deploy with tests skipped) do not
require npm or network access.
-->
<skip.cli.install>${skip.test.harness}</skip.cli.install>
<!-- Extra JVM args for Surefire; overridden by the jdk21+ profile -->
<surefire.jvm.args />
<!--
The version of the @github/copilot npm package that the reference implementation
commit pinned in .lastmerge depends on. Mirrors the value of dependencies."@github/copilot"
in target/copilot-sdk/nodejs/package.json after the reference impl is cloned/reset to the
commit in .lastmerge.

The previously mentioned package.json contains the SINGLE
SOURCE OF TRUTH for the Copilot CLI version that all paths
(build-test.yml, run-smoke-test.yml,
update-copilot-dependency.yml, setup-copilot action) must
pin to. It is updated automatically by
.github/scripts/reference-impl-sync/sync-cli-version-from-reference-impl.sh,
which is called from merge-reference-impl-finish.sh
whenever .lastmerge is updated.

DO NOT EDIT MANUALLY. To update, run the
reference-impl-sync workflow and deal with the subsequent
PR.
-->
<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>
Comment thread
edburns marked this conversation as resolved.

</properties>

<dependencies>
Expand Down Expand Up @@ -240,6 +283,35 @@
</arguments>
</configuration>
</execution>
<!--
Install the @github/copilot CLI declared by
target/copilot-sdk/nodejs/package.json. This is the CLI
version the SDK tests must run against (independent of
the older pin in test/harness/package.json which is
incidental to harness internals). Mirrors what
.github/workflows/build-test.yml does manually so that
`mvn clean verify` is self-contained: the prior
target/copilot-sdk/nodejs/node_modules is wiped by
clean, but this re-creates it before tests run.
Uses npm ci with the ignore-scripts flag, matching
build-test.yml.
-->
<execution>
<id>install-nodejs-cli-dependencies</id>
<phase>generate-test-resources</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<skip>${skip.cli.install}</skip>
<executable>npm</executable>
<workingDirectory>${copilot.sdk.clone.dir}/nodejs</workingDirectory>
<arguments>
<argument>ci</argument>
<argument>--ignore-scripts</argument>
</arguments>
Comment thread
edburns marked this conversation as resolved.
</configuration>
</execution>
</executions>
</plugin>
<plugin>
Expand All @@ -253,6 +325,15 @@
<copilot.tests.dir>${copilot.tests.dir}</copilot.tests.dir>
<copilot.sdk.dir>${copilot.sdk.clone.dir}</copilot.sdk.dir>
</systemPropertyVariables>
<!--
Set COPILOT_CLI_PATH for the forked test JVM so the SDK
tests transparently use the pinned CLI under
target/copilot-sdk/nodejs/. See the copilot.cli.path
property above for the override mechanism.
-->
<environmentVariables>
<COPILOT_CLI_PATH>${copilot.cli.path}</COPILOT_CLI_PATH>
</environmentVariables>
</configuration>
</plugin>
<!-- Add src/generated/java as an additional source root -->
Expand Down Expand Up @@ -587,6 +668,39 @@
<skip.test.harness>true</skip.test.harness>
</properties>
</profile>
<!--
Skip the install-nodejs-cli-dependencies (npm ci) execution when
tests are skipped via -DskipTests, so non-test builds do not
require npm or network access. Activates automatically; no manual
-P needed.
-->
<profile>
<id>skip-cli-install-when-tests-skipped</id>
<activation>
<property>
<name>skipTests</name>
<value>true</value>
</property>
</activation>
<properties>
<skip.cli.install>true</skip.cli.install>
</properties>
</profile>
<!--
Same as above, but for -Dmaven.test.skip=true.
-->
<profile>
<id>skip-cli-install-when-maven-test-skip</id>
<activation>
<property>
<name>maven.test.skip</name>
<value>true</value>
</property>
</activation>
<properties>
<skip.cli.install>true</skip.cli.install>
</properties>
</profile>
<!-- Debug profile for FINE logging during tests -->
<profile>
<id>debug</id>
Expand Down
Loading