|
| 1 | +#!/bin/bash |
| 2 | +set -e |
| 3 | + |
| 4 | +# copilot_preflight_diagnostic.sh - Pre-flight diagnostic for Copilot engine on GHES |
| 5 | +# |
| 6 | +# This script performs diagnostic checks before executing Copilot CLI to provide |
| 7 | +# clear error messages when Copilot is not properly configured on GHES instances. |
| 8 | +# |
| 9 | +# Checks performed: |
| 10 | +# 1. Token exchange test - Validates COPILOT_GITHUB_TOKEN can exchange for Copilot access |
| 11 | +# 2. GHES detection - Identifies GHES environments and validates configuration |
| 12 | +# 3. API target validation - Ensures engine.api-target matches GITHUB_API_URL on GHES |
| 13 | +# |
| 14 | +# Exit codes: |
| 15 | +# 0 - All checks passed, safe to proceed |
| 16 | +# 1 - Critical failure, should fail the workflow |
| 17 | + |
| 18 | +# Check if we're on GHES (non-GitHub.com environment) |
| 19 | +IS_GHES=false |
| 20 | +if [ "$GITHUB_SERVER_URL" != "https://github.com" ]; then |
| 21 | + IS_GHES=true |
| 22 | + echo "🔍 Detected GitHub Enterprise Server environment" |
| 23 | + echo " Server URL: $GITHUB_SERVER_URL" |
| 24 | + echo " API URL: $GITHUB_API_URL" |
| 25 | +fi |
| 26 | + |
| 27 | +# Test 1: Token exchange to Copilot inference API |
| 28 | +echo "" |
| 29 | +echo "🔍 Testing Copilot token exchange..." |
| 30 | + |
| 31 | +# Construct the token exchange endpoint |
| 32 | +TOKEN_EXCHANGE_URL="${GITHUB_API_URL}/copilot_internal/v2/token" |
| 33 | + |
| 34 | +# Attempt token exchange using COPILOT_GITHUB_TOKEN |
| 35 | +HTTP_STATUS=$(curl -s -o /tmp/copilot_token_exchange.json -w "%{http_code}" \ |
| 36 | + -H "Authorization: Bearer ${COPILOT_GITHUB_TOKEN}" \ |
| 37 | + -H "Accept: application/json" \ |
| 38 | + "$TOKEN_EXCHANGE_URL" 2>&1 || echo "000") |
| 39 | + |
| 40 | +if [ "$HTTP_STATUS" = "200" ]; then |
| 41 | + echo "✅ Token exchange successful (HTTP 200)" |
| 42 | + echo " Copilot is licensed and accessible" |
| 43 | +elif [ "$HTTP_STATUS" = "403" ]; then |
| 44 | + # Parse error message from response |
| 45 | + ERROR_MSG=$(cat /tmp/copilot_token_exchange.json 2>/dev/null | grep -o '"message":"[^"]*"' | cut -d'"' -f4 || echo "") |
| 46 | + |
| 47 | + echo "❌ Token exchange failed (HTTP 403)" |
| 48 | + echo "" |
| 49 | + |
| 50 | + # Check for specific error messages |
| 51 | + if echo "$ERROR_MSG" | grep -qi "not licensed"; then |
| 52 | + { |
| 53 | + echo "## ❌ Copilot Not Licensed" |
| 54 | + echo "" |
| 55 | + echo "The token exchange endpoint returned HTTP 403 with message:" |
| 56 | + echo "\`\`\`" |
| 57 | + echo "$ERROR_MSG" |
| 58 | + echo "\`\`\`" |
| 59 | + echo "" |
| 60 | + echo "**This means Copilot is not licensed for this user/organization on GHES.**" |
| 61 | + echo "" |
| 62 | + echo "### How to fix:" |
| 63 | + echo "1. Ask your GHES administrator to enable Copilot at the **enterprise level**" |
| 64 | + echo "2. Ensure a Copilot seat is assigned to your user account" |
| 65 | + echo "3. Verify your organization has Copilot enabled" |
| 66 | + echo "" |
| 67 | + echo "### GHES Admin Steps:" |
| 68 | + echo "- Navigate to Enterprise settings → Copilot" |
| 69 | + echo "- Enable Copilot for the enterprise" |
| 70 | + echo "- Assign licenses to organizations" |
| 71 | + echo "- Ensure users have seats assigned" |
| 72 | + echo "" |
| 73 | + echo "**Note:** This is a licensing issue, not a configuration problem with gh-aw." |
| 74 | + } >> "$GITHUB_STEP_SUMMARY" |
| 75 | + |
| 76 | + echo "Copilot is not licensed for this user/org on GHES." >&2 |
| 77 | + echo "Ask your GHES admin to enable Copilot at the enterprise level and assign a seat." >&2 |
| 78 | + exit 1 |
| 79 | + |
| 80 | + elif echo "$ERROR_MSG" | grep -qi "not accessible by personal access token\|token type"; then |
| 81 | + { |
| 82 | + echo "## ❌ Incorrect Token Type" |
| 83 | + echo "" |
| 84 | + echo "The token exchange endpoint returned HTTP 403 with message:" |
| 85 | + echo "\`\`\`" |
| 86 | + echo "$ERROR_MSG" |
| 87 | + echo "\`\`\`" |
| 88 | + echo "" |
| 89 | + echo "**The token type is not supported for Copilot access.**" |
| 90 | + echo "" |
| 91 | + echo "### How to fix:" |
| 92 | + echo "- Ensure you're using a **fine-grained Personal Access Token** (starts with \`github_pat_\`)" |
| 93 | + echo "- Configure the token with **Copilot Requests: Read-only** permission" |
| 94 | + echo "- Do NOT use classic PATs (\`ghp_\`) or OAuth tokens (\`gho_\`)" |
| 95 | + echo "" |
| 96 | + echo "Create a fine-grained PAT at: https://${GITHUB_SERVER_URL#https://}/settings/personal-access-tokens/new" |
| 97 | + } >> "$GITHUB_STEP_SUMMARY" |
| 98 | + |
| 99 | + echo "Token type is not supported for Copilot." >&2 |
| 100 | + echo "Use a fine-grained PAT with Copilot Requests permission." >&2 |
| 101 | + exit 1 |
| 102 | + |
| 103 | + else |
| 104 | + # Generic 403 error |
| 105 | + { |
| 106 | + echo "## ❌ Copilot Access Denied" |
| 107 | + echo "" |
| 108 | + echo "The token exchange endpoint returned HTTP 403:" |
| 109 | + echo "\`\`\`" |
| 110 | + echo "$ERROR_MSG" |
| 111 | + echo "\`\`\`" |
| 112 | + echo "" |
| 113 | + echo "**Common causes:**" |
| 114 | + echo "- Copilot not licensed for this user/organization" |
| 115 | + echo "- Incorrect token permissions" |
| 116 | + echo "- Token type not supported" |
| 117 | + echo "" |
| 118 | + echo "Contact your GHES administrator for assistance." |
| 119 | + } >> "$GITHUB_STEP_SUMMARY" |
| 120 | + |
| 121 | + echo "Token exchange failed with HTTP 403: $ERROR_MSG" >&2 |
| 122 | + exit 1 |
| 123 | + fi |
| 124 | + |
| 125 | +elif [ "$HTTP_STATUS" = "401" ]; then |
| 126 | + { |
| 127 | + echo "## ❌ Invalid or Expired Token" |
| 128 | + echo "" |
| 129 | + echo "The token exchange endpoint returned HTTP 401 (Unauthorized)." |
| 130 | + echo "" |
| 131 | + echo "**This means COPILOT_GITHUB_TOKEN is invalid or expired.**" |
| 132 | + echo "" |
| 133 | + echo "### How to fix:" |
| 134 | + echo "1. Verify the secret is correctly configured in repository settings" |
| 135 | + echo "2. Check if the token has expired (fine-grained PATs have expiration dates)" |
| 136 | + echo "3. Regenerate the token if needed" |
| 137 | + echo "4. Ensure the token has **Copilot Requests: Read-only** permission" |
| 138 | + } >> "$GITHUB_STEP_SUMMARY" |
| 139 | + |
| 140 | + echo "COPILOT_GITHUB_TOKEN is invalid or expired (HTTP 401)" >&2 |
| 141 | + exit 1 |
| 142 | + |
| 143 | +elif [ "$HTTP_STATUS" = "404" ]; then |
| 144 | + { |
| 145 | + echo "## ❌ Copilot Endpoint Not Found" |
| 146 | + echo "" |
| 147 | + echo "The token exchange endpoint returned HTTP 404 (Not Found)." |
| 148 | + echo "" |
| 149 | + echo "**This may indicate:**" |
| 150 | + echo "- GHES version does not support Copilot" |
| 151 | + echo "- Copilot infrastructure is not enabled on this instance" |
| 152 | + echo "" |
| 153 | + echo "### How to fix:" |
| 154 | + echo "- Verify GHES version supports GitHub Copilot" |
| 155 | + echo "- Ask your GHES admin to enable Copilot infrastructure" |
| 156 | + echo "- Check endpoint URL: \`$TOKEN_EXCHANGE_URL\`" |
| 157 | + } >> "$GITHUB_STEP_SUMMARY" |
| 158 | + |
| 159 | + echo "Copilot endpoint not found (HTTP 404) - GHES may not support Copilot" >&2 |
| 160 | + exit 1 |
| 161 | + |
| 162 | +elif [ "$HTTP_STATUS" = "000" ] || [ -z "$HTTP_STATUS" ]; then |
| 163 | + echo "⚠️ Could not connect to token exchange endpoint" |
| 164 | + echo " This may indicate network issues or firewall blocking" |
| 165 | + echo " Proceeding with Copilot execution (will fail if endpoint is truly unavailable)" |
| 166 | + # Don't exit - let Copilot CLI fail with its own error if needed |
| 167 | + |
| 168 | +else |
| 169 | + echo "⚠️ Unexpected response from token exchange endpoint (HTTP $HTTP_STATUS)" |
| 170 | + echo " Proceeding with Copilot execution" |
| 171 | + # Don't exit - unexpected statuses should not block execution |
| 172 | +fi |
| 173 | + |
| 174 | +# Test 2: GHES-specific validation |
| 175 | +if [ "$IS_GHES" = true ]; then |
| 176 | + echo "" |
| 177 | + echo "🔍 Running GHES-specific checks..." |
| 178 | + |
| 179 | + # Check if engine.api-target is set (should match GITHUB_API_URL) |
| 180 | + # This env var would be set by the compiler if engine.api-target is configured |
| 181 | + if [ -n "$COPILOT_API_TARGET" ]; then |
| 182 | + if [ "$COPILOT_API_TARGET" != "$GITHUB_API_URL" ]; then |
| 183 | + echo "⚠️ Warning: engine.api-target ($COPILOT_API_TARGET) does not match GITHUB_API_URL ($GITHUB_API_URL)" |
| 184 | + echo " This may cause API routing issues" |
| 185 | + else |
| 186 | + echo "✅ engine.api-target matches GITHUB_API_URL" |
| 187 | + fi |
| 188 | + else |
| 189 | + echo "ℹ️ engine.api-target not configured (using default GITHUB_API_URL)" |
| 190 | + fi |
| 191 | + |
| 192 | + # Verify GHES API domain is accessible |
| 193 | + GHES_DOMAIN=$(echo "$GITHUB_API_URL" | sed -E 's|https?://([^/]+).*|\1|') |
| 194 | + if [ -n "$GHES_DOMAIN" ]; then |
| 195 | + echo "ℹ️ GHES API domain: $GHES_DOMAIN" |
| 196 | + echo " Ensure this domain is in network.allowed if using firewall" |
| 197 | + fi |
| 198 | +fi |
| 199 | + |
| 200 | +echo "" |
| 201 | +echo "✅ Pre-flight diagnostic completed" |
| 202 | +echo " Proceeding with Copilot CLI execution..." |
| 203 | + |
| 204 | +# Clean up temporary files |
| 205 | +rm -f /tmp/copilot_token_exchange.json |
| 206 | + |
| 207 | +exit 0 |
0 commit comments