Skip to content
Draft
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
66 changes: 66 additions & 0 deletions images/ubuntu/scripts/build/install-copilot-cli.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/bin/bash -e
################################################################################
## File: install-copilot-cli.sh
## Desc: Install GitHub Copilot CLI via npm, pinned to the version validated
## by the gh-aw agent compatibility matrix.
################################################################################

# Source the helpers for use with the script
source $HELPER_SCRIPTS/install.sh

# Pin to the catch-all max-agent published in the gh-aw compatibility matrix
# (https://github.com/github/gh-aw-actions/blob/main/.github/aw/compat.json).
# This is the highest copilot CLI version validated against the current gh-aw
# release line.
#
# Fail the bake on any fetch or parse failure (matches the install-awf.sh
# pattern). The matrix is the single source of truth for which Copilot CLI
# version is approved for the runner image; if it cannot be resolved we would
# rather rebuild later than ship an unvalidated version.
COMPAT_URL="https://raw.githubusercontent.com/github/gh-aw-actions/main/.github/aw/compat.json"

echo "Fetching agent compatibility matrix from $COMPAT_URL..."
compat_json=$(curl -fsSL --proto '=https' --proto-redir '=https' --retry 3 --retry-delay 2 "$COMPAT_URL") || {
echo "Error: Unable to fetch agent compatibility matrix from $COMPAT_URL."
exit 1
}

# Select the catch-all row (max-gh-aw == "*") and read its max-agent.
# Use jq -e so a missing/null result exits non-zero and we surface a clear
# error instead of installing whatever happens to be on the npm `latest` tag.
copilot_version=$(echo "$compat_json" | jq -er '.["agent-compat-v1"].copilot[] | select(.["max-gh-aw"] == "*") | .["max-agent"]') || {
echo "Error: catch-all max-agent (max-gh-aw == \"*\") not found in compat.json."
exit 1
}

# Guard against malformed matrix entries: there must be exactly one catch-all
# row, and its value must be a SemVer string. Multiple matches would produce a
# multi-line value that npm install cannot interpret; a non-semver value would
# install a phantom dist-tag (or fail with a confusing npm error).
if [[ $(echo "$copilot_version" | wc -l) -ne 1 ]]; then
echo "Error: compat.json has more than one catch-all copilot row; expected exactly one."
echo "Got:"
echo "$copilot_version"
exit 1
fi
if ! [[ "$copilot_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
echo "Error: compat.json catch-all max-agent ('$copilot_version') is not a valid SemVer version."
exit 1
fi

# Install into the agent tool cache so the gh-aw-actions/setup action's
# tc.find("copilot-cli", ...) lookup succeeds on hosted runners. Layout:
# $AGENT_TOOLSDIRECTORY/copilot-cli/<version>/x64/{bin,lib}
# $AGENT_TOOLSDIRECTORY/copilot-cli/<version>/x64.complete
# Mirrors the conventions used by install-awf.sh and install-codeql-bundle.sh.
copilot_toolcache_path="$AGENT_TOOLSDIRECTORY/copilot-cli/$copilot_version/x64"
mkdir -p "$copilot_toolcache_path"

echo "Installing GitHub Copilot CLI v$copilot_version into $copilot_toolcache_path (pinned via compat.json)..."
npm install -g --prefix "$copilot_toolcache_path" \
"@github/copilot@${copilot_version}" --ignore-scripts

# Mark the tool-cache entry complete so @actions/tool-cache#find returns it.
touch "$copilot_toolcache_path.complete"

invoke_tests "Tools" "Copilot CLI"
21 changes: 21 additions & 0 deletions images/ubuntu/scripts/tests/Tools.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -428,3 +428,24 @@ Describe "AWF" -Skip:(Test-IsUbuntu22) {
$bundlePath | Should -Exist
}
}

Describe "Copilot CLI" -Skip:(Test-IsUbuntu22) {
It "Copilot CLI toolcache directory exists" {
$copilotPath = Join-Path $env:AGENT_TOOLSDIRECTORY "copilot-cli"
$copilotPath | Should -Exist
}

It "Copilot CLI binary exists in toolcache" {
$copilotPath = Join-Path $env:AGENT_TOOLSDIRECTORY "copilot-cli"
$latestVersion = Get-ChildItem -Path $copilotPath -Directory | Sort-Object -Property { [version]$_.Name } -Descending | Select-Object -First 1
$binPath = Join-Path $latestVersion.FullName "x64" "bin" "copilot"
$binPath | Should -Exist
}

It "Copilot CLI toolcache .complete marker exists" {
$copilotPath = Join-Path $env:AGENT_TOOLSDIRECTORY "copilot-cli"
$latestVersion = Get-ChildItem -Path $copilotPath -Directory | Sort-Object -Property { [version]$_.Name } -Descending | Select-Object -First 1
$completeMarker = Join-Path $latestVersion.FullName "x64.complete"
$completeMarker | Should -Exist
}
}
1 change: 1 addition & 0 deletions images/ubuntu/templates/build.ubuntu-24_04.pkr.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ provisioner "shell" {
"${path.root}/../scripts/build/install-nginx.sh",
"${path.root}/../scripts/build/install-nvm.sh",
"${path.root}/../scripts/build/install-nodejs.sh",
"${path.root}/../scripts/build/install-copilot-cli.sh",
"${path.root}/../scripts/build/install-bazel.sh",
"${path.root}/../scripts/build/install-php.sh",
"${path.root}/../scripts/build/install-postgresql.sh",
Expand Down
1 change: 1 addition & 0 deletions images/ubuntu/templates/build.ubuntu-26_04.pkr.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ provisioner "shell" {
"${path.root}/../scripts/build/install-nginx.sh",
"${path.root}/../scripts/build/install-nvm.sh",
"${path.root}/../scripts/build/install-nodejs.sh",
"${path.root}/../scripts/build/install-copilot-cli.sh",
"${path.root}/../scripts/build/install-bazel.sh",
"${path.root}/../scripts/build/install-php.sh",
"${path.root}/../scripts/build/install-postgresql.sh",
Expand Down