From aceee796f175d5b4fc2741cd359038ef3bcf1cb3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 15:51:51 +0000 Subject: [PATCH 1/6] Initial plan From 63f4f148ae1cd0eeebca6bcfcc932349340bd90d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 15:54:41 +0000 Subject: [PATCH 2/6] fix(copilot-cli): use semver sort for prerelease tag resolution --- src/copilot-cli/devcontainer-feature.json | 2 +- src/copilot-cli/install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/copilot-cli/devcontainer-feature.json b/src/copilot-cli/devcontainer-feature.json index bfd97286c..f4db33ced 100644 --- a/src/copilot-cli/devcontainer-feature.json +++ b/src/copilot-cli/devcontainer-feature.json @@ -1,6 +1,6 @@ { "id": "copilot-cli", - "version": "1.1.2", + "version": "1.1.3", "name": "GitHub Copilot CLI", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/copilot-cli", "description": "Installs the GitHub Copilot CLI.", diff --git a/src/copilot-cli/install.sh b/src/copilot-cli/install.sh index 47c2e3aca..0b91ee0aa 100755 --- a/src/copilot-cli/install.sh +++ b/src/copilot-cli/install.sh @@ -63,7 +63,7 @@ install_using_github() { if [ "${CLI_VERSION}" = "latest" ]; then download_from_github "https://github.com/github/copilot-cli/releases/latest/download/${cli_filename}" elif [ "${CLI_VERSION}" = "prerelease" ]; then - prerelease_version="$(git ls-remote --tags https://github.com/github/copilot-cli | tail -1 | awk -F/ '{print $NF}')" + prerelease_version="$(git ls-remote --tags https://github.com/github/copilot-cli | awk '{print $2}' | sed 's|refs/tags/||' | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?' | sort -V | tail -n1)" download_from_github "https://github.com/github/copilot-cli/releases/download/${prerelease_version}/${cli_filename}" else # Install specific version From e38d64bac98b9d34d919484edd052f2812949aa8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 11:24:09 +0000 Subject: [PATCH 3/6] refactor(copilot-cli): extract resolve_prerelease_version function and add test Extract the prerelease tag resolution logic into a standalone resolve_prerelease_version() function that can read from stdin for testing. Add a scenario test that validates version sorting with mock git ls-remote data to prevent regressions. --- src/copilot-cli/install.sh | 17 +++++- .../copilot-cli/resolve_prerelease_version.sh | 57 +++++++++++++++++++ test/copilot-cli/scenarios.json | 10 ++++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 test/copilot-cli/resolve_prerelease_version.sh create mode 100644 test/copilot-cli/scenarios.json diff --git a/src/copilot-cli/install.sh b/src/copilot-cli/install.sh index 0b91ee0aa..519142f28 100755 --- a/src/copilot-cli/install.sh +++ b/src/copilot-cli/install.sh @@ -45,6 +45,21 @@ download_from_github() { rm -rf /tmp/copilotcli } +# Resolves the latest prerelease version tag from git ls-remote output. +# Filters to well-formed vX.Y.Z or vX.Y.Z-N tags and sorts by version. +# Reads ls-remote formatted lines from stdin if no arguments are provided, +# otherwise runs git ls-remote --tags against the given repository URL. +resolve_prerelease_version() { + local repo_url="${1:-}" + if [ -n "${repo_url}" ]; then + git ls-remote --tags "${repo_url}" + else + cat + fi | awk '{print $2}' | sed 's|refs/tags/||' \ + | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?$' \ + | sort -V | tail -n1 +} + install_using_github() { check_packages wget tar ca-certificates git echo "Finished setting up dependencies" @@ -63,7 +78,7 @@ install_using_github() { if [ "${CLI_VERSION}" = "latest" ]; then download_from_github "https://github.com/github/copilot-cli/releases/latest/download/${cli_filename}" elif [ "${CLI_VERSION}" = "prerelease" ]; then - prerelease_version="$(git ls-remote --tags https://github.com/github/copilot-cli | awk '{print $2}' | sed 's|refs/tags/||' | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?' | sort -V | tail -n1)" + prerelease_version="$(resolve_prerelease_version "https://github.com/github/copilot-cli")" download_from_github "https://github.com/github/copilot-cli/releases/download/${prerelease_version}/${cli_filename}" else # Install specific version diff --git a/test/copilot-cli/resolve_prerelease_version.sh b/test/copilot-cli/resolve_prerelease_version.sh new file mode 100644 index 000000000..1ff182a2b --- /dev/null +++ b/test/copilot-cli/resolve_prerelease_version.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +# Source the install script's resolve_prerelease_version function +# We extract it by sourcing, but we only need the function definition +eval "$(sed -n '/^resolve_prerelease_version()/,/^}/p' /usr/local/share/copilot-cli-install.sh 2>/dev/null || true)" + +# If sourcing didn't work, define it inline (matches src/copilot-cli/install.sh) +if ! type resolve_prerelease_version &>/dev/null; then + resolve_prerelease_version() { + local repo_url="${1:-}" + if [ -n "${repo_url}" ]; then + git ls-remote --tags "${repo_url}" + else + cat + fi | awk '{print $2}' | sed 's|refs/tags/||' \ + | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?$' \ + | sort -V | tail -n1 + } +fi + +# Mock git ls-remote output (alphabetically v1.0.9 > v1.0.45) +MOCK_LS_REMOTE="abc1234\trefs/tags/v1.0.1 +def5678\trefs/tags/v1.0.9 +ghi9012\trefs/tags/v1.0.10 +jkl3456\trefs/tags/v1.0.45 +mno7890\trefs/tags/v1.0.2" + +# Test: version sort picks v1.0.45, not v1.0.9 +result="$(echo "${MOCK_LS_REMOTE}" | resolve_prerelease_version)" +check "picks highest version (v1.0.45)" bash -c "[ '${result}' = 'v1.0.45' ]" + +# Test: prerelease numeric suffixes sort correctly +MOCK_PRERELEASE="abc1234\trefs/tags/v1.0.44 +def5678\trefs/tags/v1.0.45-1 +ghi9012\trefs/tags/v1.0.45-10 +jkl3456\trefs/tags/v1.0.45-2 +mno7890\trefs/tags/v1.0.45" + +result2="$(echo "${MOCK_PRERELEASE}" | resolve_prerelease_version)" +check "picks highest prerelease (v1.0.45-10)" bash -c "[ '${result2}' = 'v1.0.45-10' ]" + +# Test: filters out non-version tags +MOCK_STRAY="abc1234\trefs/tags/latest +def5678\trefs/tags/v1.0.3 +ghi9012\trefs/tags/nightly +jkl3456\trefs/tags/v1.0.20" + +result3="$(echo "${MOCK_STRAY}" | resolve_prerelease_version)" +check "ignores non-version tags" bash -c "[ '${result3}' = 'v1.0.20' ]" + +# Report result +reportResults diff --git a/test/copilot-cli/scenarios.json b/test/copilot-cli/scenarios.json new file mode 100644 index 000000000..87fcc541d --- /dev/null +++ b/test/copilot-cli/scenarios.json @@ -0,0 +1,10 @@ +{ + "resolve_prerelease_version": { + "image": "ubuntu:noble", + "features": { + "copilot-cli": { + "version": "latest" + } + } + } +} From fb8ecf611d47cfad9b1ca983170b099d530e5eea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 11:40:40 +0000 Subject: [PATCH 4/6] fix(copilot-cli): make repo_url mandatory in resolve_prerelease_version Remove the stdin/cat fallback and use ${1:?} to error if no URL is provided. Update the test to mock git via PATH instead of piping stdin. --- src/copilot-cli/install.sh | 13 +--- .../copilot-cli/resolve_prerelease_version.sh | 73 +++++++++---------- 2 files changed, 39 insertions(+), 47 deletions(-) diff --git a/src/copilot-cli/install.sh b/src/copilot-cli/install.sh index 519142f28..d4c27ad1a 100755 --- a/src/copilot-cli/install.sh +++ b/src/copilot-cli/install.sh @@ -45,17 +45,12 @@ download_from_github() { rm -rf /tmp/copilotcli } -# Resolves the latest prerelease version tag from git ls-remote output. +# Resolves the latest prerelease version tag from a remote repository. # Filters to well-formed vX.Y.Z or vX.Y.Z-N tags and sorts by version. -# Reads ls-remote formatted lines from stdin if no arguments are provided, -# otherwise runs git ls-remote --tags against the given repository URL. resolve_prerelease_version() { - local repo_url="${1:-}" - if [ -n "${repo_url}" ]; then - git ls-remote --tags "${repo_url}" - else - cat - fi | awk '{print $2}' | sed 's|refs/tags/||' \ + local repo_url="${1:?resolve_prerelease_version requires a repository URL}" + git ls-remote --tags "${repo_url}" \ + | awk '{print $2}' | sed 's|refs/tags/||' \ | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?$' \ | sort -V | tail -n1 } diff --git a/test/copilot-cli/resolve_prerelease_version.sh b/test/copilot-cli/resolve_prerelease_version.sh index 1ff182a2b..8369788c8 100644 --- a/test/copilot-cli/resolve_prerelease_version.sh +++ b/test/copilot-cli/resolve_prerelease_version.sh @@ -5,53 +5,50 @@ set -e # Optional: Import test library source dev-container-features-test-lib -# Source the install script's resolve_prerelease_version function -# We extract it by sourcing, but we only need the function definition -eval "$(sed -n '/^resolve_prerelease_version()/,/^}/p' /usr/local/share/copilot-cli-install.sh 2>/dev/null || true)" - -# If sourcing didn't work, define it inline (matches src/copilot-cli/install.sh) -if ! type resolve_prerelease_version &>/dev/null; then - resolve_prerelease_version() { - local repo_url="${1:-}" - if [ -n "${repo_url}" ]; then - git ls-remote --tags "${repo_url}" - else - cat - fi | awk '{print $2}' | sed 's|refs/tags/||' \ - | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?$' \ - | sort -V | tail -n1 - } -fi - -# Mock git ls-remote output (alphabetically v1.0.9 > v1.0.45) -MOCK_LS_REMOTE="abc1234\trefs/tags/v1.0.1 -def5678\trefs/tags/v1.0.9 -ghi9012\trefs/tags/v1.0.10 -jkl3456\trefs/tags/v1.0.45 -mno7890\trefs/tags/v1.0.2" +# Define the function under test (matches src/copilot-cli/install.sh) +resolve_prerelease_version() { + local repo_url="${1:?resolve_prerelease_version requires a repository URL}" + git ls-remote --tags "${repo_url}" \ + | awk '{print $2}' | sed 's|refs/tags/||' \ + | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?$' \ + | sort -V | tail -n1 +} + +# Create a mock git script that returns controlled output +MOCK_DIR="$(mktemp -d)" +cat > "${MOCK_DIR}/git" << 'SCRIPT' +#!/bin/bash +# Return mock ls-remote output based on the repo URL argument +repo="${*: -1}" +case "${repo}" in + "mock://basic") + printf 'abc1234\trefs/tags/v1.0.1\ndef5678\trefs/tags/v1.0.9\nghi9012\trefs/tags/v1.0.10\njkl3456\trefs/tags/v1.0.45\nmno7890\trefs/tags/v1.0.2\n' + ;; + "mock://prerelease") + printf 'abc1234\trefs/tags/v1.0.44\ndef5678\trefs/tags/v1.0.45-1\nghi9012\trefs/tags/v1.0.45-10\njkl3456\trefs/tags/v1.0.45-2\nmno7890\trefs/tags/v1.0.45\n' + ;; + "mock://stray") + printf 'abc1234\trefs/tags/latest\ndef5678\trefs/tags/v1.0.3\nghi9012\trefs/tags/nightly\njkl3456\trefs/tags/v1.0.20\n' + ;; +esac +SCRIPT +chmod +x "${MOCK_DIR}/git" +export PATH="${MOCK_DIR}:${PATH}" # Test: version sort picks v1.0.45, not v1.0.9 -result="$(echo "${MOCK_LS_REMOTE}" | resolve_prerelease_version)" +result="$(resolve_prerelease_version "mock://basic")" check "picks highest version (v1.0.45)" bash -c "[ '${result}' = 'v1.0.45' ]" # Test: prerelease numeric suffixes sort correctly -MOCK_PRERELEASE="abc1234\trefs/tags/v1.0.44 -def5678\trefs/tags/v1.0.45-1 -ghi9012\trefs/tags/v1.0.45-10 -jkl3456\trefs/tags/v1.0.45-2 -mno7890\trefs/tags/v1.0.45" - -result2="$(echo "${MOCK_PRERELEASE}" | resolve_prerelease_version)" +result2="$(resolve_prerelease_version "mock://prerelease")" check "picks highest prerelease (v1.0.45-10)" bash -c "[ '${result2}' = 'v1.0.45-10' ]" # Test: filters out non-version tags -MOCK_STRAY="abc1234\trefs/tags/latest -def5678\trefs/tags/v1.0.3 -ghi9012\trefs/tags/nightly -jkl3456\trefs/tags/v1.0.20" - -result3="$(echo "${MOCK_STRAY}" | resolve_prerelease_version)" +result3="$(resolve_prerelease_version "mock://stray")" check "ignores non-version tags" bash -c "[ '${result3}' = 'v1.0.20' ]" +# Cleanup +rm -rf "${MOCK_DIR}" + # Report result reportResults From 74de86ac3215fea2b3af5a9792bbab80dfbaa1ed Mon Sep 17 00:00:00 2001 From: Kaniska Date: Fri, 22 May 2026 09:15:14 +0000 Subject: [PATCH 5/6] Add tests as per review comments without git ls and separate test for prerelease tag --- src/copilot-cli/install.sh | 15 +-- test/copilot-cli/install_prerelease.sh | 19 ++++ .../copilot-cli/resolve_prerelease_version.sh | 106 ++++++++++-------- test/copilot-cli/scenarios.json | 8 ++ 4 files changed, 93 insertions(+), 55 deletions(-) create mode 100644 test/copilot-cli/install_prerelease.sh diff --git a/src/copilot-cli/install.sh b/src/copilot-cli/install.sh index d4c27ad1a..f53150575 100755 --- a/src/copilot-cli/install.sh +++ b/src/copilot-cli/install.sh @@ -45,16 +45,6 @@ download_from_github() { rm -rf /tmp/copilotcli } -# Resolves the latest prerelease version tag from a remote repository. -# Filters to well-formed vX.Y.Z or vX.Y.Z-N tags and sorts by version. -resolve_prerelease_version() { - local repo_url="${1:?resolve_prerelease_version requires a repository URL}" - git ls-remote --tags "${repo_url}" \ - | awk '{print $2}' | sed 's|refs/tags/||' \ - | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?$' \ - | sort -V | tail -n1 -} - install_using_github() { check_packages wget tar ca-certificates git echo "Finished setting up dependencies" @@ -73,7 +63,10 @@ install_using_github() { if [ "${CLI_VERSION}" = "latest" ]; then download_from_github "https://github.com/github/copilot-cli/releases/latest/download/${cli_filename}" elif [ "${CLI_VERSION}" = "prerelease" ]; then - prerelease_version="$(resolve_prerelease_version "https://github.com/github/copilot-cli")" + prerelease_version="$(git ls-remote --tags https://github.com/github/copilot-cli \ + | awk '{print $2}' | sed 's|refs/tags/||' \ + | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?$' \ + | sort -V | tail -n1)" download_from_github "https://github.com/github/copilot-cli/releases/download/${prerelease_version}/${cli_filename}" else # Install specific version diff --git a/test/copilot-cli/install_prerelease.sh b/test/copilot-cli/install_prerelease.sh new file mode 100644 index 000000000..6ef707418 --- /dev/null +++ b/test/copilot-cli/install_prerelease.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +# End-to-end check that the "prerelease" channel actually resolves a tag and +# installs the binary. Regression guard for the inline pipeline in +# src/copilot-cli/install.sh. + +check "copilot binary is on PATH" which copilot +check "copilot reports a version" bash -c "copilot -v" + +# Auto-update flag file must exist for prerelease channel. +check "auto-update flag created for prerelease" test -f /etc/devcontainer-copilot-cli/auto-update + +# Report result +reportResults diff --git a/test/copilot-cli/resolve_prerelease_version.sh b/test/copilot-cli/resolve_prerelease_version.sh index 8369788c8..fd92c92b4 100644 --- a/test/copilot-cli/resolve_prerelease_version.sh +++ b/test/copilot-cli/resolve_prerelease_version.sh @@ -5,50 +5,68 @@ set -e # Optional: Import test library source dev-container-features-test-lib -# Define the function under test (matches src/copilot-cli/install.sh) -resolve_prerelease_version() { - local repo_url="${1:?resolve_prerelease_version requires a repository URL}" - git ls-remote --tags "${repo_url}" \ - | awk '{print $2}' | sed 's|refs/tags/||' \ - | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?$' \ - | sort -V | tail -n1 -} - -# Create a mock git script that returns controlled output -MOCK_DIR="$(mktemp -d)" -cat > "${MOCK_DIR}/git" << 'SCRIPT' -#!/bin/bash -# Return mock ls-remote output based on the repo URL argument -repo="${*: -1}" -case "${repo}" in - "mock://basic") - printf 'abc1234\trefs/tags/v1.0.1\ndef5678\trefs/tags/v1.0.9\nghi9012\trefs/tags/v1.0.10\njkl3456\trefs/tags/v1.0.45\nmno7890\trefs/tags/v1.0.2\n' - ;; - "mock://prerelease") - printf 'abc1234\trefs/tags/v1.0.44\ndef5678\trefs/tags/v1.0.45-1\nghi9012\trefs/tags/v1.0.45-10\njkl3456\trefs/tags/v1.0.45-2\nmno7890\trefs/tags/v1.0.45\n' - ;; - "mock://stray") - printf 'abc1234\trefs/tags/latest\ndef5678\trefs/tags/v1.0.3\nghi9012\trefs/tags/nightly\njkl3456\trefs/tags/v1.0.20\n' - ;; -esac -SCRIPT -chmod +x "${MOCK_DIR}/git" -export PATH="${MOCK_DIR}:${PATH}" - -# Test: version sort picks v1.0.45, not v1.0.9 -result="$(resolve_prerelease_version "mock://basic")" -check "picks highest version (v1.0.45)" bash -c "[ '${result}' = 'v1.0.45' ]" - -# Test: prerelease numeric suffixes sort correctly -result2="$(resolve_prerelease_version "mock://prerelease")" -check "picks highest prerelease (v1.0.45-10)" bash -c "[ '${result2}' = 'v1.0.45-10' ]" - -# Test: filters out non-version tags -result3="$(resolve_prerelease_version "mock://stray")" -check "ignores non-version tags" bash -c "[ '${result3}' = 'v1.0.20' ]" - -# Cleanup -rm -rf "${MOCK_DIR}" +# Tests the tag-resolution pipeline used by src/copilot-cli/install.sh for the +# "prerelease" channel. The `git ls-remote` part is intentionally not exercised; +# fixtures are piped directly into the same awk|sed|grep|sort|tail pipeline. + +# Regression: alphabetic sort would pick v1.0.9; version sort must pick v1.0.45 (issue #1646). +basic=$'abc1234\trefs/tags/v1.0.1\ndef5678\trefs/tags/v1.0.9\nghi9012\trefs/tags/v1.0.10\njkl3456\trefs/tags/v1.0.45\nmno7890\trefs/tags/v1.0.2' +check "picks highest semver (v1.0.45, not v1.0.9)" bash -c " + result=\$(printf '%s\n' \"$basic\" \ + | awk '{print \$2}' | sed 's|refs/tags/||' \ + | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9]+)?\$' \ + | sort -V | tail -n1) + [ \"\$result\" = 'v1.0.45' ] +" + +# Numeric prerelease suffixes: -10 > -2 (not alphabetic). +prerelease=$'abc1234\trefs/tags/v1.0.44\ndef5678\trefs/tags/v1.0.45-1\nghi9012\trefs/tags/v1.0.45-10\njkl3456\trefs/tags/v1.0.45-2\nmno7890\trefs/tags/v1.0.45' +check "picks highest prerelease suffix (v1.0.45-10)" bash -c " + result=\$(printf '%s\n' \"$prerelease\" \ + | awk '{print \$2}' | sed 's|refs/tags/||' \ + | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9]+)?\$' \ + | sort -V | tail -n1) + [ \"\$result\" = 'v1.0.45-10' ] +" + +# Stray refs (latest, nightly, etc.) must be filtered out. +stray=$'abc1234\trefs/tags/latest\ndef5678\trefs/tags/v1.0.3\nghi9012\trefs/tags/nightly\njkl3456\trefs/tags/v1.0.20' +check "ignores non-version tags" bash -c " + result=\$(printf '%s\n' \"$stray\" \ + | awk '{print \$2}' | sed 's|refs/tags/||' \ + | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9]+)?\$' \ + | sort -V | tail -n1) + [ \"\$result\" = 'v1.0.20' ] +" + +# Anchored regex must reject tags with trailing junk (e.g. v1.0.5-rc1, v1.0.6foo). +anchored=$'a\trefs/tags/v1.0.5\nb\trefs/tags/v1.0.5-rc1\nc\trefs/tags/v1.0.6foo' +check "anchored regex rejects v1.0.5-rc1 and v1.0.6foo" bash -c " + result=\$(printf '%s\n' \"$anchored\" \ + | awk '{print \$2}' | sed 's|refs/tags/||' \ + | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9]+)?\$' \ + | sort -V | tail -n1) + [ \"\$result\" = 'v1.0.5' ] +" + +# Empty input must produce empty output (no crash, no garbage). +check "empty input -> empty output" bash -c " + result=\$(printf '' \ + | awk '{print \$2}' | sed 's|refs/tags/||' \ + | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9]+)?\$' \ + | sort -V | tail -n1) + [ -z \"\$result\" ] +" + +# No matching tags -> empty output. +nomatch=$'a\trefs/tags/main\nb\trefs/tags/HEAD' +check "no version tags -> empty output" bash -c " + result=\$(printf '%s\n' \"$nomatch\" \ + | awk '{print \$2}' | sed 's|refs/tags/||' \ + | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9]+)?\$' \ + | sort -V | tail -n1) + [ -z \"\$result\" ] +" # Report result reportResults diff --git a/test/copilot-cli/scenarios.json b/test/copilot-cli/scenarios.json index 87fcc541d..3d8336bec 100644 --- a/test/copilot-cli/scenarios.json +++ b/test/copilot-cli/scenarios.json @@ -6,5 +6,13 @@ "version": "latest" } } + }, + "install_prerelease": { + "image": "ubuntu:noble", + "features": { + "copilot-cli": { + "version": "prerelease" + } + } } } From 6c0eac085eee448e23c5f5e1482ac7f8b6f4ff15 Mon Sep 17 00:00:00 2001 From: Kaniska Date: Fri, 22 May 2026 15:00:09 +0000 Subject: [PATCH 6/6] Further changes in the test and code cleanup --- src/copilot-cli/install.sh | 15 +++- .../copilot-cli/resolve_prerelease_version.sh | 74 +++++-------------- 2 files changed, 28 insertions(+), 61 deletions(-) diff --git a/src/copilot-cli/install.sh b/src/copilot-cli/install.sh index f53150575..ad4a19701 100755 --- a/src/copilot-cli/install.sh +++ b/src/copilot-cli/install.sh @@ -31,6 +31,14 @@ check_packages() { fi } +resolve_prerelease_version() { + local repo_versions="${1:?resolve_prerelease_version requires the copilot-cli repo tags as input}" + printf '%s\n' "${repo_versions}" \ + | awk '{print $2}' | sed 's|refs/tags/||' \ + | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?$' \ + | sort -V | tail -n1 +} + download_from_github() { local release_url=$1 echo "Downloading GitHub Copilot CLI from ${release_url}..." @@ -63,11 +71,10 @@ install_using_github() { if [ "${CLI_VERSION}" = "latest" ]; then download_from_github "https://github.com/github/copilot-cli/releases/latest/download/${cli_filename}" elif [ "${CLI_VERSION}" = "prerelease" ]; then - prerelease_version="$(git ls-remote --tags https://github.com/github/copilot-cli \ - | awk '{print $2}' | sed 's|refs/tags/||' \ - | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?$' \ - | sort -V | tail -n1)" + + prerelease_version="$(resolve_prerelease_version "$(git ls-remote --tags https://github.com/github/copilot-cli)")" download_from_github "https://github.com/github/copilot-cli/releases/download/${prerelease_version}/${cli_filename}" + else # Install specific version # Add leading v to version if it doesn't start with v diff --git a/test/copilot-cli/resolve_prerelease_version.sh b/test/copilot-cli/resolve_prerelease_version.sh index fd92c92b4..aa4ff41fd 100644 --- a/test/copilot-cli/resolve_prerelease_version.sh +++ b/test/copilot-cli/resolve_prerelease_version.sh @@ -5,68 +5,28 @@ set -e # Optional: Import test library source dev-container-features-test-lib -# Tests the tag-resolution pipeline used by src/copilot-cli/install.sh for the -# "prerelease" channel. The `git ls-remote` part is intentionally not exercised; -# fixtures are piped directly into the same awk|sed|grep|sort|tail pipeline. - -# Regression: alphabetic sort would pick v1.0.9; version sort must pick v1.0.45 (issue #1646). -basic=$'abc1234\trefs/tags/v1.0.1\ndef5678\trefs/tags/v1.0.9\nghi9012\trefs/tags/v1.0.10\njkl3456\trefs/tags/v1.0.45\nmno7890\trefs/tags/v1.0.2' -check "picks highest semver (v1.0.45, not v1.0.9)" bash -c " - result=\$(printf '%s\n' \"$basic\" \ - | awk '{print \$2}' | sed 's|refs/tags/||' \ - | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9]+)?\$' \ - | sort -V | tail -n1) - [ \"\$result\" = 'v1.0.45' ] -" +resolve_prerelease_version() { + local repo_versions="${1:?resolve_prerelease_version requires the copilot-cli repo tags as input}" + printf '%s\n' "${repo_versions}" \ + | awk '{print $2}' | sed 's|refs/tags/||' \ + | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?$' \ + | sort -V | tail -n1 +} -# Numeric prerelease suffixes: -10 > -2 (not alphabetic). -prerelease=$'abc1234\trefs/tags/v1.0.44\ndef5678\trefs/tags/v1.0.45-1\nghi9012\trefs/tags/v1.0.45-10\njkl3456\trefs/tags/v1.0.45-2\nmno7890\trefs/tags/v1.0.45' -check "picks highest prerelease suffix (v1.0.45-10)" bash -c " - result=\$(printf '%s\n' \"$prerelease\" \ - | awk '{print \$2}' | sed 's|refs/tags/||' \ - | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9]+)?\$' \ - | sort -V | tail -n1) - [ \"\$result\" = 'v1.0.45-10' ] -" +# Tests the tag-resolution pipeline used by src/copilot-cli/install.sh for the +# "prerelease" channel. -# Stray refs (latest, nightly, etc.) must be filtered out. -stray=$'abc1234\trefs/tags/latest\ndef5678\trefs/tags/v1.0.3\nghi9012\trefs/tags/nightly\njkl3456\trefs/tags/v1.0.20' -check "ignores non-version tags" bash -c " - result=\$(printf '%s\n' \"$stray\" \ - | awk '{print \$2}' | sed 's|refs/tags/||' \ - | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9]+)?\$' \ - | sort -V | tail -n1) - [ \"\$result\" = 'v1.0.20' ] -" +check "copilot binary is on PATH" which copilot +check "copilot reports a version" bash -c "copilot -v" -# Anchored regex must reject tags with trailing junk (e.g. v1.0.5-rc1, v1.0.6foo). -anchored=$'a\trefs/tags/v1.0.5\nb\trefs/tags/v1.0.5-rc1\nc\trefs/tags/v1.0.6foo' -check "anchored regex rejects v1.0.5-rc1 and v1.0.6foo" bash -c " - result=\$(printf '%s\n' \"$anchored\" \ - | awk '{print \$2}' | sed 's|refs/tags/||' \ - | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9]+)?\$' \ - | sort -V | tail -n1) - [ \"\$result\" = 'v1.0.5' ] -" +result1="$(resolve_prerelease_version $'abc1234\trefs/tags/v1.0.1\ndef5678\trefs/tags/v1.0.9\nghi9012\trefs/tags/v1.0.10\njkl3456\trefs/tags/v1.0.45\nmno7890\trefs/tags/v1.0.2\n')" +check "picks highest version (v1.0.45)" bash -c "[ '${result1}' = 'v1.0.45' ]" -# Empty input must produce empty output (no crash, no garbage). -check "empty input -> empty output" bash -c " - result=\$(printf '' \ - | awk '{print \$2}' | sed 's|refs/tags/||' \ - | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9]+)?\$' \ - | sort -V | tail -n1) - [ -z \"\$result\" ] -" +result2="$(resolve_prerelease_version $'abc1234\trefs/tags/v1.0.44\ndef5678\trefs/tags/v1.0.45-1\nghi9012\trefs/tags/v1.0.45-10\njkl3456\trefs/tags/v1.0.45-2\nmno7890\trefs/tags/v1.0.45\n')" +check "picks highest prerelease (v1.0.45-10)" bash -c "[ '${result2}' = 'v1.0.45-10' ]" -# No matching tags -> empty output. -nomatch=$'a\trefs/tags/main\nb\trefs/tags/HEAD' -check "no version tags -> empty output" bash -c " - result=\$(printf '%s\n' \"$nomatch\" \ - | awk '{print \$2}' | sed 's|refs/tags/||' \ - | grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9]+)?\$' \ - | sort -V | tail -n1) - [ -z \"\$result\" ] -" +result3="$(resolve_prerelease_version $'abc1234\trefs/tags/latest\ndef5678\trefs/tags/v1.0.3\nghi9012\trefs/tags/nightly\njkl3456\trefs/tags/v1.0.20\n')" +check "picks highest version ignoring non-version tags (v1.0.20)" bash -c "[ '${result3}' = 'v1.0.20' ]" # Report result reportResults