Skip to content
Closed
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
16 changes: 16 additions & 0 deletions .github/releases.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"channels": {
"stable": {
"version": "v0.57.2",
"engines": {
"copilot": "latest"
}
},
"latest": {
"version": "latest",
"engines": {
"copilot": "latest"
}
}
}
}
76 changes: 68 additions & 8 deletions actions/setup-cli/install.sh

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

76 changes: 68 additions & 8 deletions install-gh-aw.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@

# Script to download and install gh-aw binary for the current OS and architecture
# Supports: Linux, macOS (Darwin), FreeBSD, Windows (Git Bash/MSYS/Cygwin)
# Usage: ./install-gh-aw.sh [version] [options]
# If no version is specified, it will use "latest" (GitHub automatically resolves to the latest release)
# Usage: ./install-gh-aw.sh [version|channel] [options]
# If no version is specified, it will resolve the "stable" channel from .github/releases.json
# A semver version (e.g. v1.0.0) is used directly; a channel name (e.g. "stable", "latest")
# is resolved via .github/releases.json fetched from the raw GitHub URL (no token required).
# Note: Checksum validation is currently skipped by default (will be enabled in future releases)
#
# Examples:
# ./install-gh-aw.sh # Install latest version
# ./install-gh-aw.sh v1.0.0 # Install specific version
# ./install-gh-aw.sh # Install stable channel (default)
# ./install-gh-aw.sh stable # Install stable channel explicitly
# ./install-gh-aw.sh latest # Install latest channel
# ./install-gh-aw.sh v1.0.0 # Install specific semver version directly
# ./install-gh-aw.sh --skip-checksum # Skip checksum validation
#
# Options:
Expand Down Expand Up @@ -224,15 +228,71 @@ fetch_release_data() {
return 1
}

# Get version (use provided version or default to "latest")
# Get version (use provided version/channel, or default to "stable" channel)
# VERSION is already set from argument parsing
REPO="github/gh-aw"
RELEASES_JSON_URL="https://raw.githubusercontent.com/$REPO/main/.github/releases.json"
DEFAULT_CHANNEL="stable"

# Resolve a channel name to a version using the releases.json config file.
# The file is fetched from the raw GitHub URL without authentication.
resolve_channel_version() {
local channel=$1
print_info "Resolving version for channel '$channel' from releases config..." >&2

local releases_data
releases_data=$(curl -sf "$RELEASES_JSON_URL" 2>/dev/null)
local curl_exit=$?

if [ $curl_exit -ne 0 ] || [ -z "$releases_data" ]; then
print_warning "Failed to fetch releases config from $RELEASES_JSON_URL" >&2
return 1
fi

local version=""
if [ "$HAS_JQ" = true ]; then
version=$(echo "$releases_data" | jq -r ".channels[\"$channel\"].version // empty" 2>/dev/null)
elif command -v python3 &>/dev/null; then
version=$(echo "$releases_data" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('channels',{}).get('$channel',{}).get('version',''))" 2>/dev/null)
elif command -v python &>/dev/null; then
version=$(echo "$releases_data" | python -c "import sys,json; d=json.load(sys.stdin); print(d.get('channels',{}).get('$channel',{}).get('version',''))" 2>/dev/null)
else
# Grep/awk fallback: find the channel block and extract its version field
version=$(echo "$releases_data" | grep -A 10 "\"$channel\":" | grep '"version":' | head -1 | sed 's/.*"version": *"\([^"]*\)".*/\1/')
fi

if [ -z "$version" ]; then
print_warning "Channel '$channel' not found in releases config" >&2
return 1
fi

echo "$version"
return 0
}

if [ -z "$VERSION" ]; then
print_info "No version specified, using 'latest'..."
VERSION="latest"
else
print_info "No version specified, resolving from '$DEFAULT_CHANNEL' channel..."
resolved=$(resolve_channel_version "$DEFAULT_CHANNEL")
if [ $? -eq 0 ] && [ -n "$resolved" ]; then
VERSION="$resolved"
print_info "Resolved version: $VERSION (from '$DEFAULT_CHANNEL' channel)"
else
print_warning "Failed to resolve '$DEFAULT_CHANNEL' channel, falling back to 'latest'..."
VERSION="latest"
fi
elif [[ "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then
# Semver detected (no end anchor so pre-release suffixes like -beta.1 also match)
print_info "Using specified version: $VERSION"
else
print_info "Resolving version from channel '$VERSION'..."
resolved=$(resolve_channel_version "$VERSION")
if [ $? -eq 0 ] && [ -n "$resolved" ]; then
print_info "Resolved version: $resolved (from '$VERSION' channel)"
VERSION="$resolved"
else
print_warning "Failed to resolve channel '$VERSION'. Trying as direct version..."
print_info "Using: $VERSION"
fi
fi

# Try gh extension install if requested (and gh is available)
Expand Down
18 changes: 11 additions & 7 deletions pkg/cli/setup_cli_action_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,21 @@ func TestSetupCLIAction(t *testing.T) {
}
})

// Test that script can fetch latest version when INPUT_VERSION is not provided
t.Run("can_fetch_latest_without_input_version", func(t *testing.T) {
// This test would actually try to fetch from GitHub API
// We just verify the script doesn't immediately fail
// Test that script resolves "stable" channel by default when INPUT_VERSION is not provided
t.Run("defaults_to_stable_channel", func(t *testing.T) {
// This test would actually try to fetch from GitHub
// We just verify the script has the correct default channel logic
content, err := os.ReadFile(installScript)
if err != nil {
t.Fatalf("Failed to read install.sh: %v", err)
}
// Verify script has fallback to fetch latest
if !strings.Contains(string(content), "No version specified") || !strings.Contains(string(content), "using 'latest'") {
t.Errorf("Script should support fetching latest release when no version is provided")
// Verify script defaults to "stable" channel
if !strings.Contains(string(content), `DEFAULT_CHANNEL="stable"`) {
t.Errorf("Script should default to 'stable' channel when no version is provided")
}
// Verify script has channel resolution function
if !strings.Contains(string(content), "resolve_channel_version") {
t.Errorf("Script should have resolve_channel_version function")
}
})

Expand Down
46 changes: 39 additions & 7 deletions scripts/test-install-script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -351,19 +351,51 @@ else
exit 1
fi

# Test 11: Verify "latest" version functionality
# Test 11: Verify channel-based version resolution
echo ""
echo "Test 11: Verify 'latest' version functionality"
echo "Test 11: Verify channel-based version resolution"

# Check for "latest" as default version
if grep -q "using 'latest'" "$PROJECT_ROOT/install-gh-aw.sh"; then
echo " ✓ PASS: Script uses 'latest' as default version"
# Check for DEFAULT_CHANNEL variable set to "stable"
if grep -q 'DEFAULT_CHANNEL="stable"' "$PROJECT_ROOT/install-gh-aw.sh"; then
echo " ✓ PASS: Script defaults to 'stable' channel"
else
echo " ✗ FAIL: Script does not use 'latest' as default version"
echo " ✗ FAIL: Script does not default to 'stable' channel"
exit 1
fi

# Check for latest URL construction
# Check for resolve_channel_version function
if grep -q "resolve_channel_version()" "$PROJECT_ROOT/install-gh-aw.sh"; then
echo " ✓ PASS: resolve_channel_version function exists"
else
echo " ✗ FAIL: resolve_channel_version function not found"
exit 1
fi

# Check for RELEASES_JSON_URL variable
if grep -q 'RELEASES_JSON_URL=' "$PROJECT_ROOT/install-gh-aw.sh"; then
echo " ✓ PASS: RELEASES_JSON_URL variable exists"
else
echo " ✗ FAIL: RELEASES_JSON_URL variable not found"
exit 1
fi

# Check that releases.json URL uses raw.githubusercontent.com (no token required)
if grep -q 'raw.githubusercontent.com' "$PROJECT_ROOT/install-gh-aw.sh"; then
echo " ✓ PASS: Releases config fetched from raw.githubusercontent.com (no token)"
else
echo " ✗ FAIL: Releases config URL not using raw.githubusercontent.com"
exit 1
fi

# Check for semver detection logic (bash regex: ^v[0-9]+\.[0-9]+\.[0-9]+)
if grep -qF '^v[0-9]+\.[0-9]+\.[0-9]+' "$PROJECT_ROOT/install-gh-aw.sh"; then
echo " ✓ PASS: Semver detection regex exists"
else
echo " ✗ FAIL: Semver detection regex not found"
exit 1
fi

# Check for latest URL construction (still needed for "latest" channel)
if grep -q 'releases/latest/download' "$PROJECT_ROOT/install-gh-aw.sh"; then
echo " ✓ PASS: Latest release URL pattern is correct"
else
Expand Down