219 lines
8.5 KiB
PowerShell
219 lines
8.5 KiB
PowerShell
#---------------------------------------------------------------------------------------------
|
||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||
#---------------------------------------------------------------------------------------------
|
||
|
||
# Windows GitHub Copilot CLI bootstrapper
|
||
#
|
||
# Responsibilities:
|
||
# 1. Locate the real Copilot CLI binary (avoid recursion if this file shadows it).
|
||
# 2. Offer to install if missing (npm -g @github/copilot).
|
||
# 3. Enforce minimum version (>= REQUIRED_VERSION) with interactive update.
|
||
# 4. Execute the real binary with original arguments and exit with its status.
|
||
#
|
||
# NOTE: This file intentionally keeps logic self‑contained (no external deps) so it can be dropped into PATH directly.
|
||
|
||
# Minimum required Copilot CLI version
|
||
$RequiredVersion = "0.0.394"
|
||
$PackageName = "@github/copilot"
|
||
|
||
function Invoke-NpmGlobalCommand {
|
||
param(
|
||
[Parameter(Mandatory = $true)][ValidateSet('install', 'update')][string]$Command,
|
||
[Parameter(Mandatory = $true)][string]$Package
|
||
)
|
||
|
||
$npmArgs = @($Command, '-g', $Package)
|
||
|
||
$npmCmd = Get-Command npm.cmd -ErrorAction SilentlyContinue
|
||
if ($npmCmd) {
|
||
& npm.cmd @npmArgs
|
||
} else {
|
||
& npm @npmArgs
|
||
}
|
||
}
|
||
|
||
function Find-RealCopilot {
|
||
# Find the real copilot binary, avoiding this script if it's in PATH
|
||
$CurrentScript = $MyInvocation.PSCommandPath
|
||
if (-not $CurrentScript) { $CurrentScript = $PSCommandPath }
|
||
$CopilotPath = (Get-Command copilot -ErrorAction SilentlyContinue).Source
|
||
|
||
# Check if the copilot command would point to this script
|
||
$CurrentScriptResolved = if ($CurrentScript) { (Resolve-Path $CurrentScript -ErrorAction SilentlyContinue).Path } else { $null }
|
||
$CopilotPathResolved = if ($CopilotPath) { (Resolve-Path $CopilotPath -ErrorAction SilentlyContinue).Path } else { $null }
|
||
|
||
if ($CurrentScript -eq $CopilotPath -or (Split-Path $CurrentScript -Parent) -eq (Split-Path $CopilotPath -Parent) -or ($CurrentScriptResolved -and $CopilotPathResolved -and $CurrentScriptResolved -eq $CopilotPathResolved)) {
|
||
# The copilot in PATH is this script, find the real one by temporarily removing this script's directory from PATH
|
||
$ScriptDir = Split-Path $CurrentScript -Parent
|
||
$OldPath = $env:PATH
|
||
# Use appropriate path delimiter based on OS
|
||
$PathDelimiter = if ($IsWindows -or $env:OS -eq "Windows_NT") { ';' } else { ':' }
|
||
$env:PATH = ($env:PATH -split $PathDelimiter | Where-Object { $_ -ne $ScriptDir }) -join $PathDelimiter
|
||
$RealCopilot = (Get-Command copilot -ErrorAction SilentlyContinue).Source
|
||
$env:PATH = $OldPath
|
||
|
||
if ($RealCopilot -and (Test-Path $RealCopilot)) {
|
||
return $RealCopilot
|
||
} else {
|
||
return $null
|
||
}
|
||
} else {
|
||
# The copilot in PATH is different from this script, use it
|
||
if ($CopilotPath -and (Test-Path $CopilotPath)) {
|
||
return $CopilotPath
|
||
} else {
|
||
return $null
|
||
}
|
||
}
|
||
}
|
||
|
||
function Test-VersionCompatibility {
|
||
param([string]$Version)
|
||
$cleanInstalled = $Version -replace '^v',''
|
||
$cleanRequired = $RequiredVersion -replace '^v',''
|
||
try {
|
||
$installedVer = [version]$cleanInstalled
|
||
$requiredVer = [version]$cleanRequired
|
||
} catch {
|
||
return $false
|
||
}
|
||
return ($installedVer -ge $requiredVer)
|
||
}
|
||
|
||
function Test-AndLaunchCopilot {
|
||
param([string[]]$Arguments)
|
||
|
||
# Check if real copilot command exists
|
||
$realCopilot = Find-RealCopilot
|
||
if (-not $realCopilot) {
|
||
Write-Host "Cannot find GitHub Copilot CLI (https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli)"
|
||
$answer = Read-Host "Install GitHub Copilot CLI? (y/N)"
|
||
if ($answer -eq "y" -or $answer -eq "Y") {
|
||
try {
|
||
Invoke-NpmGlobalCommand -Command 'install' -Package $PackageName
|
||
if ($LASTEXITCODE -eq 0) {
|
||
Test-AndLaunchCopilot $Arguments
|
||
return
|
||
} else {
|
||
Read-Host "Installation failed. Please check your npm configuration and try again (or run: npm install -g @github/copilot)."
|
||
return
|
||
}
|
||
} catch {
|
||
Read-Host "Installation failed. Please check your npm configuration and try again (or run: npm install -g @github/copilot)."
|
||
return
|
||
}
|
||
} else {
|
||
exit 0
|
||
}
|
||
}
|
||
|
||
# Check version compatibility
|
||
$realCopilot = Find-RealCopilot
|
||
if (-not $realCopilot) {
|
||
Write-Host "Error: Unable to find copilot binary."
|
||
$answer = Read-Host "Would you like to reinstall GitHub Copilot CLI? (y/N)"
|
||
if ($answer -eq "y" -or $answer -eq "Y") {
|
||
Write-Host "Reinstalling GitHub Copilot CLI..."
|
||
try {
|
||
Invoke-NpmGlobalCommand -Command 'install' -Package $PackageName
|
||
if ($LASTEXITCODE -eq 0) {
|
||
Test-AndLaunchCopilot $Arguments
|
||
return
|
||
} else {
|
||
Read-Host "Reinstallation failed. Please check your npm configuration and try again (or run: npm install -g @github/copilot)."
|
||
return
|
||
}
|
||
} catch {
|
||
Read-Host "Reinstallation failed. Please check your npm configuration and try again (or run: npm install -g @github/copilot)."
|
||
return
|
||
}
|
||
} else {
|
||
exit 0
|
||
}
|
||
}
|
||
|
||
try {
|
||
$versionOutput = & $realCopilot --version 2>$null
|
||
if ($LASTEXITCODE -ne 0) {
|
||
throw "Command failed"
|
||
}
|
||
} catch {
|
||
# Write-Host "Error: Unable to check copilot version."
|
||
$answer = Read-Host "Would you like to reinstall GitHub Copilot CLI? (y/N)"
|
||
if ($answer -eq "y" -or $answer -eq "Y") {
|
||
try {
|
||
Invoke-NpmGlobalCommand -Command 'install' -Package $PackageName
|
||
if ($LASTEXITCODE -eq 0) {
|
||
Test-AndLaunchCopilot $Arguments
|
||
return
|
||
} else {
|
||
Read-Host "Reinstallation failed. Please check your npm configuration and try again (or run: npm install -g @github/copilot)."
|
||
return
|
||
}
|
||
} catch {
|
||
Read-Host "Reinstallation failed. Please check your npm configuration and try again (or run: npm install -g @github/copilot)."
|
||
return
|
||
}
|
||
} else {
|
||
exit 0
|
||
}
|
||
}
|
||
|
||
# Extract version number from output (search through all lines)
|
||
$version = $null
|
||
if ($versionOutput) {
|
||
foreach ($line in ($versionOutput -split "`n")) {
|
||
$trimmedLine = $line.Trim()
|
||
if ($trimmedLine -match '[0-9]+\.[0-9]+\.[0-9]+') {
|
||
$version = $matches[0]
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
# Command succeeded - assume CLI is installed even if we can't parse the version
|
||
|
||
# Only check version compatibility if we have a valid version
|
||
if ($version -and -not (Test-VersionCompatibility $version)) {
|
||
Write-Host "GitHub Copilot CLI version $version is not compatible."
|
||
Write-Host "Version $RequiredVersion or later is required."
|
||
$answer = Read-Host "Update GitHub Copilot CLI? (y/N)"
|
||
if ($answer -eq "y" -or $answer -eq "Y") {
|
||
try {
|
||
Invoke-NpmGlobalCommand -Command 'update' -Package $PackageName
|
||
if ($LASTEXITCODE -eq 0) {
|
||
Test-AndLaunchCopilot $Arguments
|
||
return
|
||
} else {
|
||
Read-Host "Update failed. Please check your npm configuration and try again (or run: npm update -g @github/copilot)."
|
||
return
|
||
}
|
||
} catch {
|
||
Read-Host "Update failed. Please check your npm configuration and try again (or run: npm update -g @github/copilot)."
|
||
return
|
||
}
|
||
} else {
|
||
exit 0
|
||
}
|
||
}
|
||
|
||
# All checks passed, execute the real copilot binary
|
||
$realCopilot = Find-RealCopilot
|
||
if ($realCopilot -and (Test-Path $realCopilot)) {
|
||
& $realCopilot @Arguments
|
||
} else {
|
||
Write-Host "Error: Could not find the real GitHub Copilot CLI binary"
|
||
Read-Host "Please ensure it's properly installed with: npm install -g @github/copilot"
|
||
return
|
||
}
|
||
}
|
||
|
||
# Start the check and launch process
|
||
$finalArgs = $args
|
||
# Handle --clear argument
|
||
if ($args.Length -gt 0 -and $args[0] -eq '--clear') {
|
||
Clear-Host
|
||
$finalArgs = $args[1..($args.Length - 1)]
|
||
}
|
||
|
||
Test-AndLaunchCopilot $finalArgs |