mirror of
https://github.com/tailscale/tailscale.git
synced 2026-04-04 06:36:01 -04:00
-Wait does not just wait for the created process; it waits for the entire process tree rooted at that process! This can cause the shell to wait indefinitely if something in that tree fired up any background processes. Instead we call WaitForExit on the returned process. Updates https://github.com/tailscale/corp/issues/29940 Signed-off-by: Aaron Klotz <aaron@tailscale.com>
235 lines
8.7 KiB
PowerShell
235 lines
8.7 KiB
PowerShell
# Copyright (c) Tailscale Inc & contributors
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
#Requires -Version 7.4
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
Set-StrictMode -Version 3.0
|
|
|
|
if (($Env:CI -eq 'true') -and ($Env:NOPWSHDEBUG -ne 'true')) {
|
|
Set-PSDebug -Trace 1
|
|
}
|
|
|
|
<#
|
|
.DESCRIPTION
|
|
Copies the script's $args variable into an array, which is easier to work with
|
|
when preparing to start child processes.
|
|
#>
|
|
function Copy-ScriptArgs {
|
|
$list = [System.Collections.Generic.List[string]]::new($Script:args.Count)
|
|
foreach ($arg in $Script:args) {
|
|
$list.Add($arg)
|
|
}
|
|
return $list.ToArray()
|
|
}
|
|
|
|
<#
|
|
.DESCRIPTION
|
|
Copies the current environment into a hashtable, which is easier to work with
|
|
when preparing to start child processes.
|
|
#>
|
|
function Copy-Environment {
|
|
$result = @{}
|
|
foreach ($pair in (Get-Item -Path Env:)) {
|
|
$result[$pair.Key] = $pair.Value
|
|
}
|
|
return $result
|
|
}
|
|
|
|
<#
|
|
.DESCRIPTION
|
|
Outputs the fully-qualified path to the repository's root directory. This
|
|
function expects to be run from somewhere within a git repository.
|
|
The directory containing the git executable must be somewhere in the PATH.
|
|
#>
|
|
function Get-RepoRoot {
|
|
Get-Command -Name 'git' | Out-Null
|
|
$repoRoot = & git rev-parse --show-toplevel
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "failed obtaining repo root: git failed with code $LASTEXITCODE"
|
|
}
|
|
|
|
# Git outputs a path containing forward slashes. Canonicalize.
|
|
return [System.IO.Path]::GetFullPath($repoRoot)
|
|
}
|
|
|
|
<#
|
|
.DESCRIPTION
|
|
Runs the provided ScriptBlock in a child scope, restoring any changes to the
|
|
current working directory once the script block completes.
|
|
#>
|
|
function Start-ChildScope {
|
|
param (
|
|
[Parameter(Mandatory = $true)]
|
|
[ScriptBlock]$ScriptBlock
|
|
)
|
|
|
|
$initialLocation = Get-Location
|
|
try {
|
|
Invoke-Command -ScriptBlock $ScriptBlock
|
|
}
|
|
finally {
|
|
Set-Location -Path $initialLocation
|
|
}
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Write-Output with timestamps prepended to each line.
|
|
#>
|
|
function Write-Log {
|
|
param ($message)
|
|
$timestamp = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
|
|
Write-Output "$timestamp - $message"
|
|
}
|
|
|
|
$bootstrapScriptBlock = {
|
|
|
|
$repoRoot = Get-RepoRoot
|
|
|
|
Set-Location -LiteralPath $repoRoot
|
|
|
|
switch -Wildcard -File .\go.toolchain.rev {
|
|
"/*" { $toolchain = $_ }
|
|
default {
|
|
$rev = $_
|
|
$tsgo = Join-Path $Env:USERPROFILE '.cache' 'tsgo'
|
|
$toolchain = Join-Path $tsgo $rev
|
|
if (-not (Test-Path -LiteralPath "$toolchain.extracted" -PathType Leaf -ErrorAction SilentlyContinue)) {
|
|
New-Item -Force -Path $tsgo -ItemType Directory | Out-Null
|
|
Remove-Item -Force -Recurse -LiteralPath $toolchain -ErrorAction SilentlyContinue
|
|
Write-Log "Downloading Go toolchain $rev"
|
|
|
|
# Values from https://web.archive.org/web/20250227081443/https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.architecture?view=net-9.0
|
|
$cpuArch = ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture | Out-String -NoNewline)
|
|
# Comparison in switch is case-insensitive by default.
|
|
switch ($cpuArch) {
|
|
'x86' { $goArch = '386' }
|
|
'x64' { $goArch = 'amd64' }
|
|
default { $goArch = $cpuArch }
|
|
}
|
|
|
|
Invoke-WebRequest -Uri "https://github.com/tailscale/go/releases/download/build-$rev/windows-$goArch.tar.gz" -OutFile "$toolchain.tar.gz"
|
|
try {
|
|
New-Item -Force -Path $toolchain -ItemType Directory | Out-Null
|
|
Start-ChildScope -ScriptBlock {
|
|
Set-Location -LiteralPath $toolchain
|
|
|
|
# Using an absolute path to the tar that ships with Windows
|
|
# to avoid conflicts with others (eg msys2).
|
|
$system32 = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::System)
|
|
$tar = Join-Path $system32 'tar.exe' -Resolve
|
|
& $tar --strip-components=1 -xf "$toolchain.tar.gz"
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "tar failed with exit code $LASTEXITCODE"
|
|
}
|
|
}
|
|
$rev | Out-File -FilePath "$toolchain.extracted"
|
|
}
|
|
finally {
|
|
Remove-Item -Force "$toolchain.tar.gz" -ErrorAction Continue
|
|
}
|
|
|
|
# Cleanup old toolchains.
|
|
$maxDays = 90
|
|
$oldFiles = Get-ChildItem -Path $tsgo -Filter '*.extracted' -File -Recurse -Depth 1 | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$maxDays) }
|
|
foreach ($file in $oldFiles) {
|
|
Write-Log "Cleaning up old Go toolchain $($file.Basename)"
|
|
Remove-Item -LiteralPath $file.FullName -Force -ErrorAction Continue
|
|
$dirName = Join-Path $file.DirectoryName $file.Basename -Resolve -ErrorAction Continue
|
|
if ($dirName -and (Test-Path -LiteralPath $dirName -PathType Container -ErrorAction Continue)) {
|
|
Remove-Item -LiteralPath $dirName -Recurse -Force -ErrorAction Continue
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($Env:TS_USE_GOCROSS -ne '1') {
|
|
return
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $toolchain -PathType Container -ErrorAction SilentlyContinue) {
|
|
$goMod = Join-Path $repoRoot 'go.mod' -Resolve
|
|
$goLine = Get-Content -LiteralPath $goMod | Select-String -Pattern '^go (.*)$' -List
|
|
$wantGoMinor = $goLine.Matches.Groups[1].Value.split('.')[1]
|
|
$versionFile = Join-Path $toolchain 'VERSION'
|
|
if (Test-Path -LiteralPath $versionFile -PathType Leaf -ErrorAction SilentlyContinue) {
|
|
try {
|
|
$haveGoMinor = ((Get-Content -LiteralPath $versionFile -TotalCount 1).split('.')[1]) -replace 'rc.*', ''
|
|
}
|
|
catch {
|
|
}
|
|
}
|
|
|
|
if ([string]::IsNullOrEmpty($haveGoMinor) -or ($haveGoMinor -lt $wantGoMinor)) {
|
|
Remove-Item -Force -Recurse -LiteralPath $toolchain -ErrorAction Continue
|
|
Remove-Item -Force -LiteralPath "$toolchain.extracted" -ErrorAction Continue
|
|
}
|
|
}
|
|
|
|
$wantVer = & git rev-parse HEAD
|
|
$gocrossOk = $false
|
|
$gocrossPath = '.\gocross.exe'
|
|
if (Get-Command -Name $gocrossPath -CommandType Application -ErrorAction SilentlyContinue) {
|
|
$gotVer = & $gocrossPath gocross-version 2> $null
|
|
if ($gotVer -eq $wantVer) {
|
|
$gocrossOk = $true
|
|
}
|
|
}
|
|
|
|
if (-not $gocrossOk) {
|
|
$goBuildEnv = Copy-Environment
|
|
$goBuildEnv['CGO_ENABLED'] = '0'
|
|
# Start-Process's -Environment arg applies diffs, so instead of removing
|
|
# these variables from $goBuildEnv, we must set them to $null to indicate
|
|
# that they should be cleared.
|
|
$goBuildEnv['GOOS'] = $null
|
|
$goBuildEnv['GOARCH'] = $null
|
|
$goBuildEnv['GO111MODULE'] = $null
|
|
$goBuildEnv['GOROOT'] = $null
|
|
|
|
$procExe = Join-Path $toolchain 'bin' 'go.exe' -Resolve
|
|
$proc = Start-Process -FilePath $procExe -WorkingDirectory $repoRoot -Environment $goBuildEnv -ArgumentList 'build', '-o', $gocrossPath, "-ldflags=-X=tailscale.com/version.gitCommitStamp=$wantVer", 'tailscale.com/tool/gocross' -NoNewWindow -PassThru
|
|
$proc.WaitForExit()
|
|
if ($proc.ExitCode -ne 0) {
|
|
throw 'error building gocross'
|
|
}
|
|
}
|
|
|
|
} # bootstrapScriptBlock
|
|
|
|
Start-ChildScope -ScriptBlock $bootstrapScriptBlock
|
|
|
|
$repoRoot = Get-RepoRoot
|
|
|
|
$execEnv = Copy-Environment
|
|
# Start-Process's -Environment arg applies diffs, so instead of removing
|
|
# these variables from $execEnv, we must set them to $null to indicate
|
|
# that they should be cleared.
|
|
$execEnv['GOROOT'] = $null
|
|
|
|
$argList = Copy-ScriptArgs
|
|
|
|
if ($Env:TS_USE_GOCROSS -ne '1') {
|
|
$revFile = Join-Path $repoRoot 'go.toolchain.rev' -Resolve
|
|
switch -Wildcard -File $revFile {
|
|
"/*" { $toolchain = $_ }
|
|
default {
|
|
$rev = $_
|
|
$tsgo = Join-Path $Env:USERPROFILE '.cache' 'tsgo'
|
|
$toolchain = Join-Path $tsgo $rev -Resolve
|
|
}
|
|
}
|
|
|
|
$procExe = Join-Path $toolchain 'bin' 'go.exe' -Resolve
|
|
$proc = Start-Process -FilePath $procExe -WorkingDirectory $repoRoot -Environment $execEnv -ArgumentList $argList -NoNewWindow -PassThru
|
|
$proc.WaitForExit()
|
|
exit $proc.ExitCode
|
|
}
|
|
|
|
$procExe = Join-Path $repoRoot 'gocross.exe' -Resolve
|
|
$proc = Start-Process -FilePath $procExe -WorkingDirectory $repoRoot -Environment $execEnv -ArgumentList $argList -NoNewWindow -PassThru
|
|
$proc.WaitForExit()
|
|
exit $proc.ExitCode
|