# Official Go archive bootstrap. URLs and install policy come from runtime config. function Get-GoVersion([string]$Executable) { $output = & $Executable version 2>&1 if ($LASTEXITCODE -ne 0 -or "$output" -notmatch '\bgo(\d+\.\d+(?:\.\d+)?)\b') { return $null } return [version]$Matches[1] } function Resolve-GoToolchain($Config) { $directive = Select-String -LiteralPath (Resolve-ProjectPath 'go.mod') -Pattern '^go\s+(\d+\.\d+(?:\.\d+)?)\s*$' | Select-Object -First 1 if (-not $directive) { throw 'go.mod has no valid minimum Go version.' } $minimum = [version]$directive.Matches[0].Groups[1].Value $goName = $env:GAC_GO_COMMAND if ([string]::IsNullOrWhiteSpace($goName)) { $goName = $Config.go_command } if ([string]::IsNullOrWhiteSpace($goName)) { throw 'Set launcher.go_command or GAC_GO_COMMAND.' } $found = Get-Command -Name $goName -CommandType Application -ErrorAction SilentlyContinue | Select-Object -First 1 if ($found) { $version = Get-GoVersion $found.Source if ($version -and $version -ge $minimum) { return $found.Source } Write-Host "Configured Go is older than go.mod requires ($minimum)." } $install = $Config.go_install if (-not $install -or $install.enabled -ne $true) { throw "Go >= $minimum is required. Install it or enable launcher.go_install." } $root = Resolve-ProjectPath $install.directory $receiptPath = Join-Path $root 'toolchain.json' if (Test-Path -LiteralPath $receiptPath -PathType Leaf) { $receipt = Get-Content -LiteralPath $receiptPath -Raw | ConvertFrom-Json $cached = [IO.Path]::GetFullPath((Join-Path $root $receipt.executable)) if (-not $cached.StartsWith($root.TrimEnd('\') + '\', [StringComparison]::OrdinalIgnoreCase)) { throw 'Managed Go receipt points outside its configured directory.' } if (Test-Path -LiteralPath $cached -PathType Leaf) { $version = Get-GoVersion $cached if ($version -and $version -ge $minimum) { return $cached } } } $machineArch = $env:PROCESSOR_ARCHITEW6432 if (-not $machineArch) { $machineArch = $env:PROCESSOR_ARCHITECTURE } $arch = switch ($machineArch.ToUpperInvariant()) { 'AMD64' {'amd64'} 'ARM64' {'arm64'} 'X86' {'386'} default {throw "Unsupported Windows architecture: $machineArch"} } [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 Write-Host "Downloading Go release metadata ($($install.manifest_url))..." $releases = Invoke-RestMethod -Uri $install.manifest_url $release = $releases | Where-Object { $_.stable -and $_.version -match '^go\d+\.\d+\.\d+$' -and [version]$_.version.Substring(2) -ge $minimum } | Sort-Object { [version]$_.version.Substring(2) } -Descending | Select-Object -First 1 if (-not $release) { throw "No stable Go >= $minimum in the configured official manifest." } $archive = $release.files | Where-Object { $_.os -eq 'windows' -and $_.arch -eq $arch -and $_.kind -eq 'archive' -and $_.filename -match '^[a-zA-Z0-9._-]+\.zip$' } | Select-Object -First 1 if (-not $archive -or $archive.sha256 -notmatch '^[a-fA-F0-9]{64}$') { throw 'Official manifest has no valid Windows ZIP/checksum for this machine.' } New-Item -ItemType Directory -Path $root -Force | Out-Null # Always extract to a new directory; never overlay an older Go installation. $folderName = [Guid]::NewGuid().ToString('N') $folder = Join-Path $root $folderName $zip = Join-Path $root ($folderName + '.zip') $download = $install.download_url.TrimEnd('/') + '/' + $archive.filename Write-Host "Installing $($release.version) ($arch), without administrator rights..." Invoke-WebRequest -Uri $download -OutFile $zip -UseBasicParsing if ((Get-FileHash -LiteralPath $zip -Algorithm SHA256).Hash -ine $archive.sha256) { throw "Go archive checksum mismatch: $zip" } Expand-Archive -LiteralPath $zip -DestinationPath $folder $relative = Join-Path $folderName 'go/bin/go.exe' $executable = Join-Path $root $relative $version = Get-GoVersion $executable if (-not $version -or $version -lt $minimum) { throw 'Downloaded Go did not pass its version check.' } @{executable=$relative; version=$release.version; sha256=$archive.sha256} | ConvertTo-Json | Set-Content -LiteralPath $receiptPath -Encoding UTF8 Remove-Item -LiteralPath $zip Write-Host "Go ready: $version" return $executable }