mirror of
https://github.com/actions/runner-images.git
synced 2025-12-10 19:16:48 +00:00
330 lines
11 KiB
PowerShell
330 lines
11 KiB
PowerShell
Import-Module "$PSScriptRoot/Common.Helpers.psm1"
|
|
Import-Module "$PSScriptRoot/Xcode.Helpers.psm1"
|
|
|
|
function Install-XcodeVersion {
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string] $Version,
|
|
[Parameter(Mandatory)]
|
|
[string] $LinkTo,
|
|
[Parameter(Mandatory)]
|
|
[string] $Sha256Sum
|
|
)
|
|
|
|
$xcodeDownloadDirectory = "$env:HOME/Library/Caches/XcodeInstall"
|
|
$xcodeTargetPath = Get-XcodeRootPath -Version $LinkTo
|
|
$xcodeXipDirectory = Invoke-DownloadXcodeArchive -DownloadDirectory $xcodeDownloadDirectory -Version $Version
|
|
Expand-XcodeXipArchive -DownloadDirectory $xcodeXipDirectory.FullName -TargetPath $xcodeTargetPath
|
|
Remove-Item -Path $xcodeXipDirectory -Force -Recurse
|
|
}
|
|
|
|
function Invoke-DownloadXcodeArchive {
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string] $DownloadDirectory,
|
|
[Parameter(Mandatory)]
|
|
[string] $Version
|
|
)
|
|
|
|
Write-Host "Downloading Xcode $Version"
|
|
$tempXipDirectory = New-Item -Path $DownloadDirectory -Name "Xcode$Version" -ItemType "Directory"
|
|
$xcodeFileName = 'Xcode-{0}.xip' -f $Version
|
|
$xcodeUri = '{0}{1}?{2}'-f ${env:XCODE_INSTALL_STORAGE_URL}, $xcodeFileName, ${env:XCODE_INSTALL_SAS}
|
|
$xcodeFullPath = Join-Path $tempXipDirectory.FullName $xcodeFileName
|
|
Invoke-DownloadWithRetry -Url $xcodeUri -Path $xcodeFullPath | Out-Null
|
|
|
|
# Validating checksum
|
|
$xcodeSha256 = Get-FileHash -Path $xcodeFullPath -Algorithm SHA256 | Select-Object -ExpandProperty Hash
|
|
if ($xcodeSha256 -ne $Sha256Sum) {
|
|
throw "Xcode $Version checksum mismatch. Expected: $Sha256Sum, Actual: $xcodeSha256"
|
|
}
|
|
|
|
return $tempXipDirectory
|
|
}
|
|
|
|
function Expand-XcodeXipArchive {
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string] $DownloadDirectory,
|
|
[Parameter(Mandatory)]
|
|
[string] $TargetPath
|
|
)
|
|
|
|
$xcodeXipPath = Get-ChildItem -Path $DownloadDirectory -Filter "Xcode-*.xip" | Select-Object -First 1
|
|
|
|
Write-Host "Extracting Xcode from '$xcodeXipPath'"
|
|
Push-Location $DownloadDirectory
|
|
if ([boolean] (Get-Command 'unxip' -ErrorAction 'SilentlyContinue')) {
|
|
Invoke-ValidateCommand "unxip $xcodeXipPath"
|
|
} else {
|
|
Invoke-ValidateCommand "xip -x $xcodeXipPath"
|
|
}
|
|
Pop-Location
|
|
|
|
if (Test-Path "$DownloadDirectory/Xcode-beta.app") {
|
|
Write-Host "Renaming Xcode-beta.app to Xcode.app"
|
|
Rename-Item -Path "$DownloadDirectory/Xcode-beta.app" -NewName "Xcode.app"
|
|
}
|
|
|
|
if (-not (Test-Path "$DownloadDirectory/Xcode.app")) {
|
|
throw "XIP archive '$xcodeXipPath' doesn't contain 'Xcode.app'"
|
|
}
|
|
|
|
Write-Host "Moving '$DownloadDirectory/Xcode.app' to '$TargetPath'"
|
|
Move-Item -Path "$DownloadDirectory/Xcode.app" -Destination $TargetPath
|
|
}
|
|
|
|
function Confirm-XcodeIntegrity {
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string] $Version
|
|
)
|
|
|
|
$XcodeRootPath = Get-XcodeRootPath -Version $Version
|
|
if (Test-XcodeStableRelease -XcodeRootPath $XcodeRootPath) {
|
|
Write-Host "Validating Xcode integrity for '$XcodeRootPath'..."
|
|
Invoke-ValidateCommand "spctl --assess --raw $XcodeRootPath"
|
|
}
|
|
}
|
|
|
|
function Approve-XcodeLicense {
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string] $Version
|
|
)
|
|
|
|
$os = Get-OSVersion
|
|
$XcodeRootPath = Get-XcodeRootPath -Version $Version
|
|
Write-Host "Approving Xcode license for '$XcodeRootPath'..."
|
|
$xcodeBuildPath = Get-XcodeToolPath -XcodeRootPath $XcodeRootPath -ToolName "xcodebuild"
|
|
|
|
if ($os.IsVentura -or $os.IsSonoma) {
|
|
Invoke-ValidateCommand -Command "sudo $xcodeBuildPath -license accept" -Timeout 15
|
|
} else {
|
|
Invoke-ValidateCommand -Command "sudo $xcodeBuildPath -license accept"
|
|
}
|
|
}
|
|
|
|
function Install-XcodeAdditionalComponents {
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string] $Version
|
|
)
|
|
|
|
Write-Host "Installing additional MetalToolchain component for Xcode $Version..."
|
|
$xcodeRootPath = Get-XcodeRootPath -Version $Version
|
|
$xcodeBuildPath = Get-XcodeToolPath -XcodeRootPath $xcodeRootPath -ToolName "xcodebuild"
|
|
Invoke-ValidateCommand "$xcodeBuildPath -downloadComponent MetalToolchain" | Out-Null
|
|
}
|
|
|
|
function Invoke-XcodeRunFirstLaunch {
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string] $Version
|
|
)
|
|
|
|
Write-Host "Running 'runFirstLaunch' for Xcode $Version..."
|
|
$xcodeRootPath = Get-XcodeToolPath -Version $Version -ToolName "xcodebuild"
|
|
Invoke-ValidateCommand "sudo $xcodeRootPath -runFirstLaunch"
|
|
}
|
|
|
|
function Install-XcodeAdditionalSimulatorRuntimes {
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string] $Version,
|
|
[Parameter(Mandatory)]
|
|
[string] $Arch,
|
|
[Parameter(Mandatory)]
|
|
[object] $Runtimes
|
|
)
|
|
|
|
Write-Host "Installing Simulator Runtimes for Xcode $Version ..."
|
|
$xcodebuildPath = Get-XcodeToolPath -Version $Version -ToolName 'xcodebuild'
|
|
$validRuntimes = @("iOS", "watchOS", "tvOS")
|
|
|
|
# visionOS is only available on arm64
|
|
if ($Arch -eq "arm64") {
|
|
$validRuntimes += "visionOS"
|
|
}
|
|
|
|
# Install all runtimes / skip runtimes
|
|
if ($Runtimes -eq "default") {
|
|
Write-Host "Installing all runtimes for Xcode $Version ..."
|
|
Invoke-ValidateCommand "$xcodebuildPath -downloadAllPlatforms" | Out-Null
|
|
return
|
|
} elseif ($Runtimes -eq "none") {
|
|
Write-Host "Skipping runtimes installation for Xcode $Version ..."
|
|
return
|
|
}
|
|
|
|
# Convert $Runtimes to hashtable
|
|
if ($Runtimes -is [System.Object[]]) {
|
|
$convertedRuntimes = @{}
|
|
|
|
foreach ($entry in $Runtimes) {
|
|
if ($entry -is [PSCustomObject]) {
|
|
$entry = $entry | ConvertTo-Json -Compress | ConvertFrom-Json -AsHashtable
|
|
}
|
|
|
|
# Copy all keys and values from the entry to the converted runtimes
|
|
foreach ($key in $entry.Keys) {
|
|
if ($convertedRuntimes.ContainsKey($key)) {
|
|
$convertedRuntimes[$key] += $entry[$key]
|
|
} else {
|
|
$convertedRuntimes[$key] = $entry[$key]
|
|
}
|
|
}
|
|
}
|
|
$Runtimes = $convertedRuntimes
|
|
}
|
|
|
|
# Validate runtimes format
|
|
if ($Runtimes -isnot [System.Collections.Hashtable]) {
|
|
throw "Invalid runtime format for Xcode $(Version): Expected hashtable, got [$($Runtimes.GetType())]"
|
|
}
|
|
|
|
# Install runtimes for specified platforms
|
|
foreach ($platform in $validRuntimes) {
|
|
if (-not $Runtimes.ContainsKey($platform)) {
|
|
Write-Host "No runtimes specified for $platform in the toolset for Xcode $Version, please check the toolset."
|
|
return
|
|
}
|
|
foreach ($platformVersion in $Runtimes[$platform]) {
|
|
switch ($platformVersion) {
|
|
"skip" {
|
|
Write-Host "Skipping $platform runtimes installation for Xcode $Version ..."
|
|
continue
|
|
}
|
|
"default" {
|
|
Write-Host "Installing default $platform runtime for Xcode $Version ..."
|
|
Invoke-ValidateCommand "$xcodebuildPath -downloadPlatform $platform" | Out-Null
|
|
continue
|
|
}
|
|
default {
|
|
# Version might be a semver or a build number
|
|
if (($platformVersion -match "^\d{1,2}\.\d(\.\d)?$") -or ($platformVersion -match "^[a-zA-Z0-9]{6,8}$")) {
|
|
Write-Host "Installing $platform $platformVersion runtime for Xcode $Version ..."
|
|
Invoke-ValidateCommand "$xcodebuildPath -downloadPlatform $platform -buildVersion $platformVersion" | Out-Null
|
|
continue
|
|
}
|
|
throw "$platformVersion is not a valid value for $platform version. Valid values are 'latest' or 'skip' or a semver from 0.0 to 99.9.(9)."
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function Build-XcodeSymlinks {
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string] $Version,
|
|
[string[]] $Symlinks
|
|
)
|
|
|
|
$sourcePath = Get-XcodeRootPath -Version $Version
|
|
$Symlinks | Where-Object { $_ } | ForEach-Object {
|
|
$targetPath = Get-XcodeRootPath -Version $_
|
|
Write-Host "Creating symlink: '$targetPath' -> '$sourcePath'"
|
|
New-Item -Path $targetPath -ItemType SymbolicLink -Value $sourcePath | Out-Null
|
|
}
|
|
}
|
|
|
|
function Initialize-XcodeLaunchServicesDb {
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string] $Version
|
|
)
|
|
|
|
$xcodePath = Get-XcodeRootPath -Version $Version
|
|
$lsregister = '/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister'
|
|
Get-ChildItem -Recurse -Filter "*.app" $xcodePath | Foreach-Object { & $lsregister -f $_.FullName}
|
|
}
|
|
|
|
function Build-ProvisionatorSymlink {
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string] $Version
|
|
)
|
|
|
|
$sourcePath = Get-XcodeRootPath -Version $Version
|
|
$versionInfo = Get-XcodeVersionInfo -XcodeRootPath $sourcePath
|
|
|
|
$targetVersion = [SemVer]::Parse($versionInfo.Version).ToString()
|
|
$targetPath = Get-XcodeRootPath -Version $targetVersion
|
|
if ($sourcePath -ne $targetPath) {
|
|
Write-Host "Creating symlink: '$targetPath' -> '$sourcePath'"
|
|
New-Item -Path $targetPath -ItemType SymbolicLink -Value $sourcePath | Out-Null
|
|
}
|
|
}
|
|
|
|
function Set-XcodeDeveloperDirEnvironmentVariables {
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string[]] $XcodeList
|
|
)
|
|
|
|
$exactVersionsList = $XcodeList | Where-Object { Test-XcodeStableRelease -Version $_ } | ForEach-Object {
|
|
$xcodeRootPath = Get-XcodeRootPath -Version $_
|
|
$xcodeVersionInfo = Get-XcodeVersionInfo -XcodeRootPath $xcodeRootPath
|
|
return @{
|
|
RootPath = $xcodeRootPath
|
|
Version = [SemVer]::Parse($xcodeVersionInfo.Version)
|
|
}
|
|
} | Sort-Object -Property Version -Descending
|
|
|
|
$majorVersions = $exactVersionsList.Version.Major | Select-Object -Unique
|
|
$majorVersions | ForEach-Object {
|
|
$majorVersion = $_
|
|
$latestXcodeVersion = $exactVersionsList | Where-Object { $_.Version.Major -eq $majorVersion } | Select-Object -First 1
|
|
$variableName = "XCODE_${_}_DEVELOPER_DIR"
|
|
$variableValue = "$($latestXcodeVersion.RootPath)/Contents/Developer"
|
|
Write-Host "Set ${variableName}=${variableValue}"
|
|
"export ${variableName}=${variableValue}" | Out-File "$env:HOME/.bashrc" -Append
|
|
}
|
|
}
|
|
|
|
function Invoke-ValidateCommand {
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string] $Command,
|
|
[Uint] $Timeout = 0
|
|
)
|
|
|
|
if ($Timeout -eq 0) {
|
|
$output = Invoke-Expression -Command $Command
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Command '$Command' has finished with exit code $LASTEXITCODE"
|
|
}
|
|
return $output
|
|
} else {
|
|
$job = $command | Start-Job -ScriptBlock {
|
|
$output = Invoke-Expression -Command $input
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw 'Command failed'
|
|
}
|
|
return $output
|
|
}
|
|
$waitObject = $job | Wait-Job -Timeout $Timeout
|
|
|
|
if (-not $waitObject) {
|
|
throw "Command '$Command' has timed out"
|
|
}
|
|
|
|
if ($waitObject.State -eq 'Failed') {
|
|
throw "Command '$Command' has failed"
|
|
}
|
|
Receive-Job -Job $job
|
|
}
|
|
}
|
|
|
|
function Update-DyldCache {
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[string] $Version
|
|
)
|
|
|
|
Write-Host "Updating dyld shared cache for Xcode $Version ..."
|
|
Switch-Xcode -Version $Version
|
|
Invoke-ValidateCommand "xcrun simctl runtime dyld_shared_cache update --all"
|
|
}
|