You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
388 lines
18 KiB
388 lines
18 KiB
|
|
#launch example |
|
#powershell -Command "Invoke-WebRequest -Uri 'https://www.gitea1.intx.com.au/jn/punkshell/raw/branch/master/bin/punk-getzig.cmd' -OutFile 'punk-getzig.cmd'; Start-Process 'cmd.exe' -ArgumentList @('/c', 'punk-getzig.cmd') -NoNewWindow -Wait" |
|
#powershell -Command "Invoke-WebRequest -Uri 'https://www.gitea1.intx.com.au/jn/punkshell/raw/branch/master/bin/punk-getzig.cmd' -OutFile 'punk-getzig.cmd'; Start-Process 'punk-getzig.cmd' -NoNewWindow -Wait" |
|
|
|
#Join-Path using verbose method to support powershell 5? |
|
#$outbase = Join-Path -Path $PSScriptRoot -ChildPath "../.." |
|
$outbase = $PSScriptRoot |
|
$outbase = Resolve-Path -Path $outbase |
|
Write-host "Base folder: $outbase" |
|
$toolsfolder = Join-Path -Path $outbase -ChildPath "tools" |
|
if (-not(Test-Path -Path $toolsfolder -PathType Container)) { |
|
#create folder - (can include missing intermediaries) |
|
New-Item -Path $toolsfolder -ItemType Directory |
|
} |
|
$zigfolder = Join-Path $toolsfolder -ChildPath "zig" |
|
$zigexe = Join-Path $zigfolder -ChildPath "zig.exe" |
|
#$releasearchive = "zig-x86_64-windows-0.15.1.zip" ;#zip on windows, tarball on every other platform |
|
#$releasearchive = "zig-x86_64-windows-0.16.0-dev.254+6dd0270a1.zip" |
|
#$releasearchive = "zig-x86_64-windows-0.16.0-dev.2193+fc517bd01.zip" |
|
$releasearchive = "zig-x86_64-windows-0.16.0.zip" |
|
#single-source the pinned version from the archive name (used for ziglang.org release URLs) |
|
if ($releasearchive -match 'zig-x86_64-windows-(.+)\.zip$') { |
|
$releaseversion = $Matches[1] |
|
} else { |
|
Write-Error "cannot derive zig version from releasearchive name '$releasearchive'" |
|
exit 1 |
|
} |
|
Write-Output "powershell version: $($PSVersionTable.PSVersion)" |
|
|
|
if (Get-Command $zigexe -ErrorAction SilentlyContinue) { |
|
$relative_zigfolder = Resolve-Path -Path $zigfolder -RelativeBasePath $outbase -Relative |
|
Write-Host "zig.exe is installed in $relative_zigfolder" |
|
$zigv = & $zigexe version 2>&1 |
|
$stdout = $zigv | Where-Object {$_ -is [string]} |
|
$stderr = $zigv | Where-Object {$_ -is [System.Management.Automation.ErrorRecord]} |
|
if ($stderr) { |
|
Write-Host "Unexpected output from ${zigexe}: $stderr" |
|
Write-Host "Consider deleting $zigexe and re-downloading" |
|
} else { |
|
Write-Host "$zigexe version is: $stdout" |
|
} |
|
exit |
|
} |
|
|
|
if (Get-Command "minisign" -ErrorAction SilentlyContinue) { |
|
Write-Host "minisign is available" |
|
} else { |
|
Write-Host "minisign is missing. Will attempt to install using winget." |
|
#Find-Module/Install-Module: older mechanism, available in powershell |
|
#Find-PSResource/Install-PSResource: only available in newer pwsh etc? |
|
$wgclient = Get-Module -ListAvailable -Name Microsoft.WinGet.Client |
|
if (${wgclient}.Length -eq 0) { |
|
Write-Host "Microsoft.WinGet.Client module not installed.. will try to install." |
|
Install-PackageProvider -Scope CurrentUser -Name NuGet -Force |
|
$psgallery_existing_policy = (Get-PSRepository -Name PSGallery).InstallationPolicy |
|
if ($psgallery_existing_policy -eq "Untrusted") { |
|
#Applies to all versions of PowerShell for the user, and is persistent for current user. |
|
#This has risks in that a powershell session started after this call, and before we reset it, will treat PSGallery as trusted |
|
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted |
|
} |
|
Install-Module -Scope CurrentUser -Name Microsoft.Winget.Client -Force -Repository PSGallery |
|
Repair-WinGetPackageManager |
|
import-module -name Microsoft.Winget.client |
|
|
|
if ($psgallery_existing_policy -eq "Untrusted") { |
|
Set-PSRepository -Name PSGallery -InstallationPolicy Untrusted |
|
} |
|
} else { |
|
Write-Host "Microsoft.WinGet.Client is available" |
|
} |
|
$wingetversion = (Find-WinGetPackage jedisct1.minisign).Version |
|
if ($wingetversion) { |
|
Write-Host "Installing minisign version: ${wingetversion}" |
|
Install-WinGetPackage -Id "jedisct1.minisign" |
|
} else { |
|
Write-Host "Failed to find minisign using winget" |
|
exit |
|
} |
|
#refreshing the current session's path should make the new command available (required for powershell 5 at least) |
|
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") |
|
if (Get-Command "minisign" -ErrorAction SilentlyContinue) { |
|
Write-Host "minisign is now available" |
|
} else { |
|
Write-Host "minisign is still not available" |
|
#if we automatically relaunch - we could get stuck in a loop - ask user. |
|
$response = read-host -Prompt "Relaunching process may make minizip available. Relaunch? Y|N" |
|
#if ($PSVersionTable.PSEdition -eq "Desktop") { |
|
#powershell.exe |
|
#} else { |
|
#pwsh.exe |
|
#} |
|
if ($response -ieq "y") { |
|
Start-Process 'punk-getzig.cmd' -NoNewWindow -Wait |
|
} |
|
exit |
|
} |
|
} |
|
|
|
#extract_zip to be called only after sig verified |
|
; |
|
function extractZip { |
|
[CmdletBinding()] |
|
param( |
|
[Parameter(Mandatory=$true, Position = 0)][string] $releasearchive, |
|
[Parameter(Mandatory=$true, Position = 1)] [string] $toolsfolder |
|
) |
|
Add-Type -Assembly "System.IO.Compression.Filesystem" |
|
$outfile = Join-Path $toolsfolder -ChildPath $releasearchive |
|
|
|
$zip_rootname = [System.IO.Path]::GetFileNameWithoutExtension($releasearchive) |
|
if (-not ($zip_rootname.Contains("zig"))) { |
|
write-host "sanity check on zip_rootname '$zip_rootname' failed - aborting" |
|
exit 1 |
|
} |
|
$zip_extraction_folder = Join-Path -Path $toolsfolder -ChildPath $zip_rootname |
|
if (Test-Path -Path $zip_extraction_folder -PathType Container) { |
|
write-host "Existing folder found at $zip_extraction_folder - removing folder prior to extraction" |
|
Remove-Item -Path $zip_extraction_folder -Recurse -Force -ErrorAction SilentlyContinue |
|
} |
|
#Expand-Archive -path $outfile -DestinationPath $toolsfolder -Force #This is *insanely* (many minutes vs seconds) slow on powershell 5 at least - we get a progress meter, but it's too high a price to pay. |
|
# ------------------------------------- |
|
[System.IO.Compression.Zipfile]::ExtractToDirectory($outfile, $toolsfolder) |
|
Write-Host " - archive extracted." |
|
if (-not (Test-Path -Path $zip_extraction_folder -PathType Container)) { |
|
write-host "Failed to verify extraction as folder at $zip_extraction_folder" |
|
exit 1 |
|
} |
|
|
|
|
|
$zigfolder = Join-Path $toolsfolder -ChildPath "zig" |
|
$zigexe = Join-Path $zigfolder -ChildPath "zig.exe" |
|
|
|
$retries = 10 |
|
$exe_missing = $true #missing until tested as a found leaf |
|
$sleep_time = 2 |
|
while (($retries -gt 0) -and $exe_missing) { |
|
$retries -= 1 |
|
#We *intermittently* get permission errors when trying to rename the resulting folder (possibly also if we try to delete the zip file immediately) |
|
#There seems to be some sort of lock held after ExtractToDirectory (possibly AV related) |
|
#https://stackoverflow.com/questions/74582293/file-lock-issues-on-zip-file-after-io-compression-zipfileextracttodirectory |
|
|
|
#In some cases just the GC was enough.. but in other cases even sleep 3 seconds + GC didn't work |
|
#sleep of 5 seems more reliable - but the size of the required delay may depend on what is running on the system, |
|
# and the size of the extracted folder etc. |
|
#Rather than use a large sleep to cover all cases - we use a limited retry loop with a somewhat shorter sleep e.g 2 |
|
Write-Host "waiting $sleep_time seconds and also running GC to allow locks to clear. Max remaining retries: $retries" |
|
start-sleep -Seconds $sleep_time |
|
|
|
[GC]::Collect() |
|
[GC]::WaitForPendingFinalizers() |
|
# ------------------------------------- |
|
#Remove-Item -Path "$outfile" |
|
#write-host "zip_rootname: $zip_rootname" |
|
write-host "Attempting to move $zip_extraction_folder to ${zigfolder}" |
|
Rename-Item -Path $(Join-Path -Path $toolsfolder -ChildPath $zip_rootname) -NewName $zigfolder -ErrorAction SilentlyContinue |
|
if (-not $?) { |
|
write-host "Renaming extracted folder into place at $zigfolder failed" |
|
write-host " - error message: $($error[0].Exception.Message)" |
|
} |
|
if (Test-Path -Path $zigexe -PathType leaf) { |
|
$exe_missing = $false |
|
} |
|
} |
|
|
|
if (Test-Path -Path $zigexe -PathType leaf) { |
|
Write-Host "Zig installed in ${zigfolder}" |
|
return $true |
|
} else { |
|
Write-Host "Failed to install as $zigexe" |
|
return $false |
|
} |
|
} |
|
|
|
|
|
$zigpubkey = "RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U" |
|
|
|
$outfile = Join-Path $toolsfolder -ChildPath $releasearchive |
|
$sigfile = "${outfile}.minisig" |
|
$download_required = $true #default assumption |
|
if (Test-Path -Path $outfile -PathType Leaf) { |
|
if (Test-Path -Path $sigfile) { |
|
write-host "Found existing $outfile and ${outfile}.minisig - validating signature..." |
|
$sigresult = minisign -V -P $zigpubkey -m $outfile 2>&1 |
|
$stdout = $sigresult | Where-Object {$_ -is [string]} |
|
$stderr = $sigresult | Where-Object {$_ -is [System.Management.Automation.ErrorRecord]} |
|
$is_valid = $false |
|
if ($stderr) { |
|
write-host "Signature validation failed with message: $stderr" |
|
} else { |
|
if (($stdout | Out-String).Contains("signature verified")) { |
|
write-host $stdout |
|
$is_valid = $true |
|
} else { |
|
write-host "Unexpected output from minisign: $stdout" |
|
write-host "Did not contain 'signature verified'" |
|
} |
|
} |
|
if (-not($is_valid)) { |
|
write-Host "Couldn't verify signature of existing $outfile with $outfile.minisig" |
|
Remove-Item -Path $outfile -ErrorAction SilentlyContinue |
|
Remove-Item -Path "${outfile}.minisig" -ErrorAction SilentlyContinue |
|
} else { |
|
$download_required = $false |
|
} |
|
} |
|
} |
|
if (-not $download_required) { |
|
write-host "Existing zip and zip.minisig ok - download not required" |
|
Write-Host "Signature OK - extracting existing archive ..." |
|
$null = extractZip $releasearchive $toolsfolder |
|
|
|
$zigexe = Join-Path $zigfolder -ChildPath "zig.exe" |
|
$v = Invoke-Expression "$zigexe version" |
|
Write-Host "ZIG VERSION: ${v}" |
|
exit 0 |
|
} |
|
|
|
#index: https://ziglang.org/download/index.json |
|
|
|
$mirrors_url = "https://ziglang.org/download/community-mirrors.txt" |
|
#some mirrors have no index and the base url will produce a 404, some have an html index, some have a modified copy of the ziglang.org/download/index.json |
|
$mirrors_response = $(Invoke-WebRequest -Uri $mirrors_url) |
|
if ($mirrors_response.StatusCode -eq 200) { |
|
#https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master/win32-x86_64/tools/zig-x86_64-windows-0.15.1.zip |
|
$mirror_array = @("https://gitea1.intx.com.au/jn/punkbin/raw/branch/master/win32-x86_64/tools") |
|
$mirror_array += $mirrors_response.Content.TrimEnd("`r`n") -split "`r`n|`n" |
|
#$mirror_array += "https://bogusxxx.org" #test behaviour of a bogus/down entry |
|
$mirror_array += "https://ziglang.org" #main site |
|
|
|
$dict_mirrors = [ordered]@{} |
|
$host_list = @() #same ordering as dict_mirrors |
|
foreach ($mirror in $mirror_array) { |
|
$uri = New-Object System.Uri($mirror) |
|
$hostname = $uri.Host |
|
$dict_mirrors[$hostname] = @{} |
|
$dict_mirrors[$hostname]["uri"] = $mirror |
|
$dict_mirrors[$hostname]["latency"] = 888888 ;#default |
|
$host_list += $hostname |
|
#write-host "Host name: $hostname" |
|
} |
|
#write-host "dict: $($dict_mirrors | out-String)" |
|
write-host "host_list: $host_list" |
|
$automation_name = "punkshell+julian@precisium.com.au_target_by_latency" |
|
|
|
|
|
#test-netconnection progressbar seems not-so-well-behaved (prog bar lingers) |
|
#test-netconnection a function not cmdlet? Seems to need setting at global level |
|
$OriginalProgressPreference = $Global:ProgressPreference |
|
$Global:ProgressPreference = 'SilentlyContinue' |
|
#temporary dev download source |
|
$ihost = "10.30.30.107" |
|
$imirror = "http://10.30.30.107/jn/punkbin/raw/branch/master/win32-x86_64/tools" |
|
$itrace = test-netconnection $ihost -Hops 6 -TraceRoute -ErrorAction Ignore |
|
if ((${itrace}.PingSucceeded) -and (${itrace}.TraceRoute.Count -lt 5)) { |
|
$host_list += $ihost |
|
$dict_mirrors[$ihost] = @{} |
|
$dict_mirrors[$ihost]["latency"] = 1 |
|
$dict_mirrors[$ihost]["uri"] = $imirror |
|
} |
|
$trace = test-netconnection gitea1.intx.com.au -Hops 6 -TraceRoute -ErrorAction Ignore |
|
$Global:ProgressPreference = $OriginalProgressPreference |
|
|
|
if ((${trace}.PingSucceeded) -and (${trace}.TraceRoute.Count -lt 5)) { |
|
$dict_mirrors["gitea1.intx.com.au"]["latency"] = 2 |
|
#short-circuit for hosts very near gitea1.intx.com.au |
|
#don't even test others. This is primarily for hosts at intx.com.au and associated entities - which are more likely to use punkshell than anyone else. |
|
} else { |
|
if ($PSVersionTable.PSEdition -eq "Desktop") { |
|
#powershell.exe |
|
#we seem to need -ErrorAction Ignore for bad/unresponsive host entries - but unlike with pwsh, we don't get any corresponding entry in the test_results, and -Quiet doesn't seem to give us results either REVIEW |
|
$test_results = Test-Connection $host_list -Count 1 -ErrorAction Ignore |
|
for ($i = 0; $i -lt $test_results.Count; $i++) { |
|
$result = $test_results[$i] |
|
if ($result) { |
|
$targethost = $result.Address |
|
if ($result.StatusCode -eq 0) { |
|
$dict_mirrors[$targethost]["latency"] = $result.ResponseTime |
|
} else { |
|
$dict_mirrors[$targethost]["latency"] = 999999 |
|
} |
|
} else { |
|
$targethost = $host_list[$i] |
|
$dict_mirrors[$targethost]["latency"] = 999999 |
|
} |
|
} |
|
} else { |
|
#pwsh.exe |
|
$test_results = Test-Connection -TargetName $host_list -Count 1 -Ipv4 -Detailed -TcpPort 443 -Quiet |
|
for ($i = 0; $i -lt $test_results.Count; $i++) { |
|
$result = $test_results[$i] |
|
if ($result) { |
|
$targethost = $result.Target |
|
if ($result.Status -eq "Success") { |
|
$dict_mirrors[$targethost]["latency"] = $result.Latency |
|
} else { |
|
$dict_mirrors[$targethost]["latency"] = 999999 |
|
} |
|
} else { |
|
$targethost = $host_list[$i] |
|
$dict_mirrors[$targethost]["latency"] = 999999 |
|
} |
|
} |
|
|
|
} |
|
} |
|
|
|
$list_mirror_dicts = @() |
|
#write-host "dict tested: $($dict_mirrors | Out-String)" |
|
foreach ($key in $dict_mirrors.Keys) { |
|
$list_mirror_dicts += $($dict_mirrors[$key]) |
|
} |
|
#need to ensure latency cast to integer (on powershell 5 at least) |
|
$sorted_mirror_dicts = $list_mirror_dicts | Sort-Object -Property { [int]$_.latency } |
|
#Write-Host "Sorted by latency: $($sorted_mirror_dicts | Format-Table -AutoSize | Out-String)" |
|
Write-Host "Sorted by latency: $($sorted_mirror_dicts | Out-String)" |
|
|
|
foreach ($hostinfo in $sorted_mirror_dicts) { |
|
$uristring = $hostinfo.uri |
|
if ($uristring -eq "https://ziglang.org") { |
|
#if it's a release |
|
$full_uristring = "${uristring}/download/${releaseversion}/${releasearchive}?source=${automation_name}" |
|
$sig_uristring = "${uristring}/download/${releaseversion}/${releasearchive}.minisig?source=${automation_name}" |
|
#if it's a pre-release?? /builds/${prereleasearchive} ? |
|
} else { |
|
$full_uristring = "${uristring}/${releasearchive}?source=${automation_name}" |
|
$sig_uristring = "${uristring}/${releasearchive}.minisig?source=${automation_name}" |
|
} |
|
#download the minisig first - because if that fails, we should move on without trying the larger download |
|
Write-Host "Downloading minisig signature from $sig_uristring" |
|
Invoke-WebRequest -Uri $sig_uristring -Outfile "${outfile}.minisig" |
|
if (-not(Test-Path -Path "${outfile}.minisig" -PathType Leaf)) { |
|
write-Host "Failed to download minisig signature from $sig_uristring to ${outfile}.minisig .. aborting download from $uristring" |
|
continue |
|
} |
|
|
|
Write-Host "Downloading zig distribution from $full_uristring" |
|
Write-Host "Download to: $outfile" |
|
Invoke-WebRequest -Uri $full_uristring -OutFile $outfile |
|
|
|
if (-not(Test-Path -Path $outfile -PathType Leaf)) { |
|
write-Host "Failed to download zig package from $full_uristring to ${outfile} .. aborting download from $uristring" |
|
continue |
|
} |
|
|
|
write-host "downloaded $outfile and ${outfile}.minisig - validating signature..." |
|
#validate releasearchive (tarball/zip) |
|
$sigresult = minisign -V -P $zigpubkey -m $outfile 2>&1 |
|
$stdout = $sigresult | Where-Object {$_ -is [string]} |
|
$stderr = $sigresult | Where-Object {$_ -is [System.Management.Automation.ErrorRecord]} |
|
$is_valid = $false |
|
if ($stderr) { |
|
write-host "Signature validation failed with message: $stderr" |
|
} else { |
|
if (($stdout | Out-String).Contains("signature verified")) { |
|
write-host $stdout |
|
$is_valid = $true |
|
} else { |
|
write-host "Unexpected output from minisign: $stdout" |
|
write-host "Did not contain 'signature verified'" |
|
} |
|
} |
|
if (-not($is_valid)) { |
|
write-Host "Couldn't verify signature from download site $uristring" |
|
Remove-Item -Path $outfile -ErrorAction SilentlyContinue |
|
Remove-Item -Path "${outfile}.minisig" -ErrorAction SilentlyContinue |
|
continue |
|
} |
|
|
|
|
|
|
|
# ----------------------------------------------------- |
|
Write-Host "Signature OK - extracting downloaded archive ..." |
|
$null = extractZip $releasearchive $toolsfolder |
|
|
|
$zigexe = Join-Path $zigfolder -ChildPath "zig.exe" |
|
$v = Invoke-Expression "$zigexe version" |
|
Write-Host "ZIG VERSION: ${v}" |
|
|
|
$test_ok = 1 ;#todo |
|
if ($test_ok) { |
|
break |
|
} |
|
} |
|
|
|
} else { |
|
Write-Host "Unable to retrieve list of zig community mirrors from $mirrors_url" |
|
}
|
|
|