diff --git a/bin/getzig.cmd b/bin/getzig.cmd index aa1f80a3..68591b2d 100644 --- a/bin/getzig.cmd +++ b/bin/getzig.cmd @@ -806,7 +806,13 @@ do if not defined param1 set %%~"param1=%2%%~" rename set ""; rename S set; set k {-- "$@" "a}; if {[info exists ::env($k)]} {unset ::env($k)} ;# tidyup and restore Hide :exit_multishell;Hide {<#};Hide '@ #--------------------------------------------------------------------- +puts "info script : [info script]" +#puts "argcount : $::argc" +#puts "argvalues: $::argv" +#puts "argv0 : $::argv0" +# -- --- --- --- --- --- --- --- --- --- --- --- #divert to configured nextshell +set script_as_called [info script] package require platform set plat_full [platform::generic] set plat [lindex [split $plat_full -] 0] @@ -819,6 +825,14 @@ set in_data 0 set nextshellpath "" set nextshelltype "" puts stderr "PLAT: $plat" +switch -glob -- $plat { + "msys" - "mingw*" { + set os "win32" + } + default { + set os $plat + } +} foreach ln [split $scriptdata \n] { if {[string trim $ln] eq ""} {continue} if {!$in_data} { @@ -826,14 +840,14 @@ foreach ln [split $scriptdata \n] { set in_data 1 } } else { - if {[string match "*@SET*nextshellpath?${plat}_*" $ln]} { + if {[string match "*@SET*nextshellpath?${os}_*" $ln]} { set lineparts [split $ln =] set tail [lindex $lineparts 1] set nextshellpath [string trimright $tail {_"}] if {$nextshellpath ne "" && $nextshelltype ne ""} { break } - } elseif {[string match "*@SET*nextshelltype?${plat}_*" $ln]} { + } elseif {[string match "*@SET*nextshelltype?${os}_*" $ln]} { set lineparts [split $ln =] set tail [lindex $lineparts 1] set nextshelltype [string trimright $tail {_"}] @@ -846,7 +860,6 @@ foreach ln [split $scriptdata \n] { } } if {$nextshelltype ne "tcl" && $nextshelltype ne "none"} { - set script_as_called [info script] set script_rootname [file rootname $script_as_called] if {$nextshelltype in "pwsh powershell"} { # experimental @@ -907,20 +920,52 @@ if {$nextshelltype ne "tcl" && $nextshelltype ne "none"} { set scrname $script_as_called set arglist $::argv } - puts stdout "tclsh launching subshell of type: $nextshelltype shellpath: $nextshellpath on script $scrname with args: $arglist" #todo - handle /usr/bin/env #todo - exitcode - if {[llength $nextshellpath] == 1 && [string index $nextshellpath 0] eq {"} && [string index $nextshellpath end] eq {"}} { - set nextshell_words [list $nextshellpath] - } else { - set nextshell_words $nextshellpath + #review - test spaced quoted words in nextshellpath? + # + #if {[llength $nextshellpath] == 1 && [string index $nextshellpath 0] eq {"} && [string index $nextshellpath end] eq {"}} { + # set nextshell_words [list $nextshellpath] + #} else { + # set nextshell_words $nextshellpath + #} + + #perform any msys argument munging on a cmd/cmd.exe based nextshellpath before we convert the first word to an auto_exec path + switch -glob -- $plat { + "msys" - "mingw*" { + set cmdword [lindex $nextshellpath 0] + #we only act on cmd or cmd.exe - not a full path such as c:/WINDOWS/system32/cmd.exe + #the nextshellpath should generally be configured as cmd /c ... or cmd.exe ... but specifying it as a path could allow bypassing this un-munging. + #The un-munging only applies to msys/mingw, so such bypassing should be unnecessary - review + #maint: keep this munging in sync with zsh/bash and perl blocks which must also do msys mangling + if {[regexp {^cmd$|^cmd[.]exe$} $cmdword]} { + #need to deal with msys argument munging + puts stderr "cmd call via msys detected. performing translation of /c to //C" + #for now we only deal with /C or /c - todo - other cmd.exe flags? + #In this context we would usually only be using cmd.exe /c to launch older 'desktop' powershell to avoid spaced-argument problems - so we aren't expecting other flags + set new_nextshellpath [list $cmdword] + #for now - just do what zsh munging does - bash regex/string/array processing is tedious and footgunny for the unfamiliar (me), + #so determine the minimum viable case for code there, then port behaviour to perl/tcl msys munging sections. + foreach w [lrange $nextshellpath 1 end] { + if {[regexp {^/[Cc]$} $w]} { + lappend new_nextshellpath {//C} + } else { + lappend new_nextshellpath $w + } + } + set nextshellpath $new_nextshellpath + } + } } + set ns_firstword [lindex $nextshellpath 0] - if {[string index $ns_firstword 0] eq {"} && [string index $ns_firstword end] eq {"}} { - set ns_firstword [string range $ns_firstword 1 end-1] - } + #review - is this test for extra layer of double quoting on first word really necessary? + #if we are treaing $nextshellpath as a tcl list - the first layer of double quotes will already have disappeared + ##if {[string index $ns_firstword 0] eq {"} && [string index $ns_firstword end] eq {"}} { + ## set ns_firstword [string range $ns_firstword 1 end-1] + ##} - if {[string match {/*/env} $ns_firstword] && $::tcl_platform(platform) ne "windows"} { + if {$::tcl_platform(platform) ne "windows" && [string match {/*/env} $ns_firstword]} { set exec_part $nextshellpath } else { set epath [auto_execok $ns_firstword] @@ -930,6 +975,10 @@ if {$nextshelltype ne "tcl" && $nextshelltype ne "none"} { set exec_part [list {*}$epath {*}[lrange $nextshellpath 1 end]] } } + + + puts stdout "tclsh launching subshell of type: $nextshelltype shellpath: $nextshellpath on script $scrname with args: $arglist" + puts stdout "exec: $exec_part $scrname $arglist" catch {exec {*}$exec_part $scrname {*}$arglist <@stdin >@stdout 2>@stderr} emsg eopts if {[dict exists $eopts -errorcode]} { @@ -973,11 +1022,6 @@ namespace eval ::punk::multishell { } } # -- --- --- --- --- --- --- --- --- --- --- --- --- ---begin Tcl Payload -#puts "script : [info script]" -#puts "argcount : $::argc" -#puts "argvalues: $::argv" -#puts "argv0 : $::argv0" -# -- --- --- --- --- --- --- --- --- --- --- --- # puts stderr "No tcl code for this script. Try another program such as zsh or bash or perl" @@ -1006,7 +1050,7 @@ echo "var0: $0 @: $@" # use oldschool backticks and sed (posix - lowest common denominator) \ # ps_shellname=`ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()' | sed -E 's/^.*\/|^-//'` \ # some ps impls will return arguments - so last field not always appropriate \ -# some ps impls don't have -o (e.g gnu cygwin) so ps_shellname may remain empty and emit an error \ +# some ps impls don't have -o (e.g cygwin) so ps_shellname may remain empty and emit an error \ ps_shellname=`ps -o pid,comm -p $$ | awk '$1 != "PID" {print $2}'` # \ echo "shell from ps: $ps_shellname" @@ -1061,17 +1105,14 @@ if false==false # else { then : # - # zsh/bash \ shift && set -- "${@:1:$((${#@}-1))}" # ## ### ### ### ### ### ### ### ### ### ### ### ### ### -# -- sh/bash script section -# -- leave as is if all that is required is launching the Tcl payload" -# -- -# -- Note that sh/bash script isn't called when running a .bat/.cmd from cmd.exe on windows by default -# -- adjust the %nextshell% value above -# -- if sh/bash scripting needs to run on windows too. +# -- zsh/bash script section # -- +# -- review - for zsh do we want to use: setopt KSH_ARRAYS ? +# -- arrays in bash 0-based vs 1-based in zsh +# -- stick to the @:i:len syntax which is same for both # ## ### ### ### ### ### ### ### ### ### ### ### ### ### plat=$(uname -s) #platform/system @@ -1094,7 +1135,6 @@ elif [[ "$plat" == "MINGW64"* ]]; then elif [[ "$plat" == "CYGWIN_NT"* ]]; then os="win32" elif [[ "$plat" == "MSYS_NT"* ]]; then - echo MSYS #review.. #Need to consider the difference between when msys2 was launched (which strips some paths and sets up the environment) @@ -1141,6 +1181,8 @@ elif [[ "$ps_shellname" == "zsh" ]]; then else #fallback - doesn't seem to work in zsh - untested in early bash IFS=$'\n' arr_oslines=($shellconfiglines) + IFS=$' \t\n' + # review fi nextshellpath="" nextshelltype="" @@ -1182,23 +1224,33 @@ if [[ "$nextshelltype" != "bash" && "$nextshelltype" != "none" ]]; then fi if [[ "$plat" == "MSYS_NT"* ]]; then - #we need to deal with MSYS argument mangling + #we need to deal with MSYS argument munging cmdpattern="^cmd.exe |^cmd " #do not double quote cmdpattern - or it will be treated as literal string if [[ "$nextshellpath" =~ $cmdpattern ]]; then #for now - tell the user what's going on - echo "cmd call via msys detected. performing translation of /c to //c and escaping backslashes in script path" + echo "cmd call via msys detected. performing translation of /c to //c and escaping backslashes in script path" >&2 #flags to cmd.exe such as /c are interpreted by msys as looking like a unix path #review - for nextshellpath targets specified in the block for win32 - we don't expect unix paths (?) #what about other flags? - can we just double up all forward slashes? - nextshellpath="${nextshellpath// \/c / \/\/c }" - echo "new nextshellpath: ${nextshellpath}" + #maint: keep this munging in sync with the tcl block and perl block which must also do msys munging + nextshellpath="${nextshellpath// \/[cC] / \/\/c }" + # echo "new nextshellpath: ${nextshellpath}" + #review - #don't double quote this script=${script//\\/\\\\} fi echo "calling ${nextshellpath} $script $@" - # eval is required here - but why? - eval ${nextshellpath} "$script" "$@" + + #load into array + cmd_array=($nextshellpath) + cmd_array+=("$script") #add script, which may contain spaces as a single entry ? + cmd_array+=( "$@" ) #add each element of args to array as a separate entry (equiv ? "${arr[@]}") + # printf "%s\n" "${cmd_array[@]}" + "${cmd_array[@]}" + + # this works to make nextshellpath run - but joins $@ members incorrectly + #eval ${nextshellpath} "$script" "$@" else #e.g /usr/bin/env tclsh "$0" "$@" ${nextshellpath} "$script" "$@" @@ -1308,8 +1360,33 @@ if 0 { # -- unbalanced braces { } here *even in comments* will cause problems if there was no Tcl exit or return above # -- custom script should generally go below the begin_powershell_payload line # ## ### ### ### ### ### ### ### ### ### ### ### ### ### -function GetScriptName { $myInvocation.ScriptName } -$scriptname = GetScriptName +#$MyInvocation.ScriptName should probably be considered deprecated +# https://stackoverflow.com/questions/78511229/how-can-i-choose-between-myinvocation-scriptname-and-myinvocation-pscommandpat +$runningscriptname = $PSCommandPath +if (-not $MyInvocation.PSCommandPath) { + $callingscriptname = '' +} else { + $callingscriptname = $MyInvocation.PSCommandPath +} +#The problem with psmodulepath +#https://github.com/PowerShell/PowerShell/issues/18108 +# psmodulepath is shared by powershell and pwsh despite not all ps modules being compatible. +# It is futzed with by powershell/pwsh based on detecting the child process type. +# a psmodulepath that has been futzed with by pwsh will not work for a child powershell 5 process that isn't launched directly +#This is inherently unfriendly to situations where an intervening process may be something else such as cmd.exe,tcl,perl etc +# nevertheless, powershell/pwsh maintainers seem to have taken the MS-centric view of the world that such situations don't exist :/ +# +#symptoms of these shenannigans not working include things like Get-FileHash failing in powershell desktop +# +#We don't know if the original console was pwsh/powershell or cmd.exe, and we need to potentially divert to powershell 5 (desktop) +#via tcl or perl etc - or cmd.exe +if ($PSVersionTable.PSVersion.Major -le 5) { + # For Windows PowerShell, we want to remove any PowerShell 7 paths from PSModulePath + #snipped from https://github.com/PowerShell/DSC/pull/777/commits/af9b99a4d38e0cf1e54c4bbd89cbb6a8a8598c4e + #Presumably users are supposed to know not to have custom paths for powershell desktop containing a 'powershell' subfolder?? + $env:PSModulePath = ($env:PSModulePath -split ';' | Where-Object { $_ -notlike '*\powershell\*' }) -join ';' +} + function GetDynamicParamDictionary { [CmdletBinding()] param( @@ -1384,11 +1461,11 @@ function GetDynamicParamDictionary { #} #psmain @args #"Timestamp : {0,10:yyyy-MM-dd HH:mm:ss}" -f $(Get-Date) | write-host -"Script Name : {0}" -f $scriptname | write-host +#"Running Script Name : {0}" -f $runningscriptname | write-host "Powershell Version: {0}" -f $PSVersionTable.PSVersion.Major | write-host -"powershell args : {0}" -f ($args -join ", ") | write-host +#"powershell args : {0}" -f ($args -join ", ") | write-host # -- --- --- --- -$thisfileContent = Get-Content $scriptname -Raw +$thisfileContent = Get-Content $runningscriptname -Raw $startTag = ": <>" $endTag = ": <>" $pattern = "(?s)`n$startTag[^`n]*`n(.*?)`n$endTag" @@ -1487,7 +1564,7 @@ if ($match.Success) { } if (-not (("pwsh", "powershell", "") -contains $nextshell_type)) { #nextshell diversion exists for this platform - write-host "os: $os pwsh/powershell launching subshell of type: $nextshell_type shellpath: $nextshell_path on script $scriptname" + write-host "os: $os pwsh/powershell launching subshell of type: $nextshell_type shellpath: $nextshell_path on script $runningscriptname" # $arguments = @($($MyInvocation.MyCommand.Path)) # $arguments += $args @@ -1495,7 +1572,7 @@ if ($match.Success) { # $process = (Start-Process -FilePath $nextshell_path -ArgumentList $arguments -NoNewWindow -Wait) # Exit $process.ExitCode - & $nextshell_path $scriptname $args + & $nextshell_path $runningscriptname $args exit $LASTEXITCODE } } @@ -1503,275 +1580,380 @@ if ($match.Success) { # -- --- --- --- --- --- --- --- --- --- --- --- --- ---begin powershell Payload # - -#launch example -#powershell -Command "Invoke-WebRequest -Uri 'https://www.gitea1.intx.com.au/jn/punkshell/raw/branch/master/bin/getzig.cmd' -OutFile 'getzig.cmd'; Start-Process 'cmd.exe' -ArgumentList @('/c', 'getzig.cmd') -NoNewWindow -Wait" -#powershell -Command "Invoke-WebRequest -Uri 'https://www.gitea1.intx.com.au/jn/punkshell/raw/branch/master/bin/getzig.cmd' -OutFile 'getzig.cmd'; Start-Process '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 "zig.exe" -$releasearchive = "zig-x86_64-windows-0.15.1.zip" ;#zip on windows, tarball on every other platform -Write-Output "powershell version: $($PSVersionTable.PSVersion)" - -if (Get-Command $zigexe -ErrorAction SilentlyContinue) { - Write-Host "zig.exe is installed in tools/zig" - $zigv = tools/zig/zig.exe 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 tools/zig/zig.exe: $stderr" - Write-Host "Consider deleting tools/zig and re-downloading" - } else { - Write-Host "tools/zig/zig.exe 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 -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 'getzig.cmd' -NoNewWindow -Wait - } - exit - } -} - -$zigpubkey = "RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U" -$mirrors_url = "https://ziglang.org/download/community-mirrors.txt" -$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") { - $full_uristring = "${uristring}/download/0.15.1/${releasearchive}?source=${automation_name}" - $sig_uristring = "${uristring}/download/0.15.1/${releasearchive}.minisig?source=${automation_name}" - } else { - $full_uristring = "${uristring}/${releasearchive}?source=${automation_name}" - $sig_uristring = "${uristring}/${releasearchive}.minisig?source=${automation_name}" - } - Write-Host "Downloading zig from $full_uristring" - #$uriobj = [uri]$full_uristring - #$lastSegment = $uriobj.Segments[-1] - #$outfile = Join-Path $toolsfolder -ChildPath $lastSegment - $outfile = Join-Path $toolsfolder -ChildPath $releasearchive - 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 "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 from $sig_uristring to ${outfile}.minisig .. 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 archive ..." - #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. - # ------------------------------------- - Add-Type -Assembly "System.IO.Compression.Filesystem" - [System.IO.Compression.Zipfile]::ExtractToDirectory($outfile, $toolsfolder) - Write-Host " - archive extracted." - #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 - [GC]::Collect() - [GC]::WaitForPendingFinalizers() - # ------------------------------------- - - #Remove-Item -Path "$outfile" - $zip_rootname = [System.IO.Path]::GetFileNameWithoutExtension($releasearchive) - #Rename-Item -Path - #write-host "zip_rootname: $zip_rootname" - write-host "moving $(Join-Path -Path $toolsfolder -ChildPath $zip_rootname) to ${zigfolder}" - Rename-Item -Path $(Join-Path -Path $toolsfolder -ChildPath $zip_rootname) -NewName $zigfolder - Write-Host "Zig installed in ${zigfolder}" - $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" -} + +#launch example +#powershell -Command "Invoke-WebRequest -Uri 'https://www.gitea1.intx.com.au/jn/punkshell/raw/branch/master/bin/getzig.cmd' -OutFile 'getzig.cmd'; Start-Process 'cmd.exe' -ArgumentList @('/c', 'getzig.cmd') -NoNewWindow -Wait" +#powershell -Command "Invoke-WebRequest -Uri 'https://www.gitea1.intx.com.au/jn/punkshell/raw/branch/master/bin/getzig.cmd' -OutFile 'getzig.cmd'; Start-Process '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" +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 '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 +} + +$mirrors_url = "https://ziglang.org/download/community-mirrors.txt" +$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") { + $full_uristring = "${uristring}/download/0.15.1/${releasearchive}?source=${automation_name}" + $sig_uristring = "${uristring}/download/0.15.1/${releasearchive}.minisig?source=${automation_name}" + } 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" +} # diff --git a/src/scriptapps/getzig.bash b/src/scriptapps/bin/getzig.bash similarity index 100% rename from src/scriptapps/getzig.bash rename to src/scriptapps/bin/getzig.bash diff --git a/src/scriptapps/getzig.ps1 b/src/scriptapps/bin/getzig.ps1 similarity index 66% rename from src/scriptapps/getzig.ps1 rename to src/scriptapps/bin/getzig.ps1 index 9d667d58..7c185901 100644 --- a/src/scriptapps/getzig.ps1 +++ b/src/scriptapps/bin/getzig.ps1 @@ -1,269 +1,374 @@ - -#launch example -#powershell -Command "Invoke-WebRequest -Uri 'https://www.gitea1.intx.com.au/jn/punkshell/raw/branch/master/bin/getzig.cmd' -OutFile 'getzig.cmd'; Start-Process 'cmd.exe' -ArgumentList @('/c', 'getzig.cmd') -NoNewWindow -Wait" -#powershell -Command "Invoke-WebRequest -Uri 'https://www.gitea1.intx.com.au/jn/punkshell/raw/branch/master/bin/getzig.cmd' -OutFile 'getzig.cmd'; Start-Process '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 "zig.exe" -$releasearchive = "zig-x86_64-windows-0.15.1.zip" ;#zip on windows, tarball on every other platform -Write-Output "powershell version: $($PSVersionTable.PSVersion)" - -if (Get-Command $zigexe -ErrorAction SilentlyContinue) { - Write-Host "zig.exe is installed in tools/zig" - $zigv = tools/zig/zig.exe 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 tools/zig/zig.exe: $stderr" - Write-Host "Consider deleting tools/zig and re-downloading" - } else { - Write-Host "tools/zig/zig.exe 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 -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 'getzig.cmd' -NoNewWindow -Wait - } - exit - } -} - -$zigpubkey = "RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U" -$mirrors_url = "https://ziglang.org/download/community-mirrors.txt" -$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") { - $full_uristring = "${uristring}/download/0.15.1/${releasearchive}?source=${automation_name}" - $sig_uristring = "${uristring}/download/0.15.1/${releasearchive}.minisig?source=${automation_name}" - } else { - $full_uristring = "${uristring}/${releasearchive}?source=${automation_name}" - $sig_uristring = "${uristring}/${releasearchive}.minisig?source=${automation_name}" - } - Write-Host "Downloading zig from $full_uristring" - #$uriobj = [uri]$full_uristring - #$lastSegment = $uriobj.Segments[-1] - #$outfile = Join-Path $toolsfolder -ChildPath $lastSegment - $outfile = Join-Path $toolsfolder -ChildPath $releasearchive - 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 "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 from $sig_uristring to ${outfile}.minisig .. 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 archive ..." - #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. - # ------------------------------------- - Add-Type -Assembly "System.IO.Compression.Filesystem" - [System.IO.Compression.Zipfile]::ExtractToDirectory($outfile, $toolsfolder) - Write-Host " - archive extracted." - #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 - [GC]::Collect() - [GC]::WaitForPendingFinalizers() - # ------------------------------------- - - #Remove-Item -Path "$outfile" - $zip_rootname = [System.IO.Path]::GetFileNameWithoutExtension($releasearchive) - #Rename-Item -Path - #write-host "zip_rootname: $zip_rootname" - write-host "moving $(Join-Path -Path $toolsfolder -ChildPath $zip_rootname) to ${zigfolder}" - Rename-Item -Path $(Join-Path -Path $toolsfolder -ChildPath $zip_rootname) -NewName $zigfolder - Write-Host "Zig installed in ${zigfolder}" - $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" -} + +#launch example +#powershell -Command "Invoke-WebRequest -Uri 'https://www.gitea1.intx.com.au/jn/punkshell/raw/branch/master/bin/getzig.cmd' -OutFile 'getzig.cmd'; Start-Process 'cmd.exe' -ArgumentList @('/c', 'getzig.cmd') -NoNewWindow -Wait" +#powershell -Command "Invoke-WebRequest -Uri 'https://www.gitea1.intx.com.au/jn/punkshell/raw/branch/master/bin/getzig.cmd' -OutFile 'getzig.cmd'; Start-Process '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" +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 '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 +} + +$mirrors_url = "https://ziglang.org/download/community-mirrors.txt" +$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") { + $full_uristring = "${uristring}/download/0.15.1/${releasearchive}?source=${automation_name}" + $sig_uristring = "${uristring}/download/0.15.1/${releasearchive}.minisig?source=${automation_name}" + } 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" +} diff --git a/src/scriptapps/getzig_original.polyglot b/src/scriptapps/bin/getzig_original.polyglot similarity index 97% rename from src/scriptapps/getzig_original.polyglot rename to src/scriptapps/bin/getzig_original.polyglot index 41af9fed..f9426738 100644 --- a/src/scriptapps/getzig_original.polyglot +++ b/src/scriptapps/bin/getzig_original.polyglot @@ -1,18 +1,18 @@ - -#!/bin/sh -echo `# <#` -mkdir -p ./zig -wget https://ziglang.org/download/0.10.1/zig-linux-x86_64-0.10.1.tar.xz -O ./zig/zig-linux-x86_64-0.10.1.tar.xz -tar -xf ./zig/zig-linux-x86_64-0.10.1.tar.xz -C ./zig --strip-components=1 -rm ./zig/zig-linux-x86_64-0.10.1.tar.xz -echo "Zig installed." -./zig/zig version -exit -#> > $null - -Invoke-WebRequest -Uri "https://ziglang.org/download/0.10.1/zig-windows-x86_64-0.10.1.zip" -OutFile ".\zig-windows-x86_64-0.10.1.zip" -Expand-Archive -Path ".\zig-windows-x86_64-0.10.1.zip" -DestinationPath ".\" -Force -Remove-Item -Path " .\zig-windows-x86_64-0.10.1.zip" -Rename-Item -Path ".\zig-windows-x86_64-0.10.1" -NewName ".\zig" -Write-Host "Zig installed." -./zig/zig.exe version + +#!/bin/sh +echo `# <#` +mkdir -p ./zig +wget https://ziglang.org/download/0.10.1/zig-linux-x86_64-0.10.1.tar.xz -O ./zig/zig-linux-x86_64-0.10.1.tar.xz +tar -xf ./zig/zig-linux-x86_64-0.10.1.tar.xz -C ./zig --strip-components=1 +rm ./zig/zig-linux-x86_64-0.10.1.tar.xz +echo "Zig installed." +./zig/zig version +exit +#> > $null + +Invoke-WebRequest -Uri "https://ziglang.org/download/0.10.1/zig-windows-x86_64-0.10.1.zip" -OutFile ".\zig-windows-x86_64-0.10.1.zip" +Expand-Archive -Path ".\zig-windows-x86_64-0.10.1.zip" -DestinationPath ".\" -Force +Remove-Item -Path " .\zig-windows-x86_64-0.10.1.zip" +Rename-Item -Path ".\zig-windows-x86_64-0.10.1" -NewName ".\zig" +Write-Host "Zig installed." +./zig/zig.exe version diff --git a/src/scriptapps/getzig_wrap.toml b/src/scriptapps/bin/getzig_wrap.toml similarity index 96% rename from src/scriptapps/getzig_wrap.toml rename to src/scriptapps/bin/getzig_wrap.toml index 3cfdb55e..1606dc1f 100644 --- a/src/scriptapps/getzig_wrap.toml +++ b/src/scriptapps/bin/getzig_wrap.toml @@ -1,21 +1,21 @@ - - -[application] - template="punk.multishell.cmd" - as_admin=false - - scripts=[ - "getzig.ps1", - "getzig.bash" - ] - - default_outputfile="getzig.cmd" - default_nextshellpath="/usr/bin/env bash" - default_nextshelltype="bash" - - #valid nextshelltype entries are: tcl perl pwsh powershell bash. - #nextshellpath entries must be 64 characters or less. - - win32.nextshellpath="pwsh -nop -nol -ExecutionPolicy bypass -c" - win32.nextshelltype="pwsh" - win32.outputfile="getzig.cmd" + + +[application] + template="punk.multishell.cmd" + as_admin=false + + scripts=[ + "getzig.ps1", + "getzig.bash" + ] + + default_outputfile="getzig.cmd" + default_nextshellpath="/usr/bin/env bash" + default_nextshelltype="bash" + + #valid nextshelltype entries are: tcl perl pwsh powershell bash. + #nextshellpath entries must be 64 characters or less. + + win32.nextshellpath="pwsh -nop -nol -ExecutionPolicy bypass -c" + win32.nextshelltype="pwsh" + win32.outputfile="getzig.cmd" diff --git a/src/scriptapps/getpunk.ps1 b/src/scriptapps/getpunk.ps1 index 8393ffff..b0d7a3ec 100644 --- a/src/scriptapps/getpunk.ps1 +++ b/src/scriptapps/getpunk.ps1 @@ -1,176 +1,176 @@ - -#powershell -Command "Invoke-WebRequest -Uri 'https://www.gitea1.intx.com.au/jn/punkshell/raw/branch/master/getpunk.cmd' -OutFile 'getpunk.cmd'; Start-Process 'getpunk.cmd' -NoNewWindow -Wait" - -#todo - support either fossil or git - -#check if git available -#if not, check/install winget, winget git - -$git_upstream = "https://gitea1.intx.com.au/jn/punkshell.git" -$launchdir = Get-Location #store original CWD -$scriptfolder = Resolve-Path (Split-Path -Path $PSCommandPath -Parent) -$punkfolder = "" -$scriptroot = "$([System.IO.Path]::GetFileNameWithoutExtension($PSCommandPath))" -$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") - -if (-not (Get-Command "git" -ErrorAction SilentlyContinue)) { - Write-Host "The git command doesn't seem to be available. 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 -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" - } - $gitversion = (Find-WinGetPackage Git.Git).Version - if ($gitversion) { - Write-Host "Installing git version: ${gitversion}" - Install-WinGetPackage -Id "Git.Git" - } else { - Write-Host "Failed to find git 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 "git" -ErrorAction SilentlyContinue) { - Write-Host "git is now available" - } else { - Write-Host "git is still not available" - Write-HOst "Please install Git or relaunch your terminal and check it is available on the path." - exit - } -} - -if (($launchdir.Path) -ne ($scriptfolder.Path)) { - Write-Host "The current directory does not seem to be the folder in which the getpunk script is located." - $answer = Read-Host "Do you want to use the current directory '$($launchdir.Path) as the location for punkshell? Y|N`n Y to use launchdir '$($launchdir.Path)'`n 'N' to use script folder '$($scriptfolder.Path)`n Any other value to abort: " - if ($answer -match "y") { - $punkfolder = $launchdir - } elseif ($answer -match "n") { - $punkfolder = $scriptfolder - } else { - exit 1 - } - -} else { - $punkfolder = $scriptfolder -} -$punkfoldercontents = Get-ChildItem -Path $punkfolder -Force #include possibly hidden items such as .git folder -$contentcount = ( $punkfoldercontents | Measure-Object).Count -$effectively_empty = 0 -if ($contentcount -eq 0) { - $effectively_empty = 1 -} elseif ($punkfolder -eq $scriptfolder -and $contentcount -lt 10) { - #treat as empty if we have only a few files matching script root name - $scriptlike = get-childitem -Path $punkfolder | Where-Object {$_.name -like "${scriptroot}.*"} - if ($scriptlike.Count -eq $contentcount) { - $effectively_empty = 1 - } -} -if (-not($effectively_empty)) { - if (-not(Test-Path -Path (Join-Path -Path $punkfolder -ChildPath ".git") -PathType Container)) { - Write-Host "The folder $punkfolder contains other items, and it does not appear to be a git project root." - Write-Host "Please place this script in an empty folder which is to be the punkshell base folder." - exit - } else { - $repo_origin = git remote get-url origin - if ($repo_origin -ne $git_upstream) { - Write-Host "The current repository origin '$repo_origin' is not the expected upstream '${git_upstream}'" - $answer = Read-Host "Continue anyway? (Y|N)" - if (-not($answer -match "y")) { - exit 1 - } - } - } -} else { - #punkfolder is empty, or has just the current script -} -Set-Location -Path $punkfolder - - -if (-not(Test-Path -Path (Join-Path -Path $punkfolder -ChildPath ".git") -PathType Container)) { - git init - git remote add origin $git_upstream -} -git fetch origin -if (($launchdir.Path) -eq ($scriptfolder.Path)) { - if (Test-Path -Path "${scriptroot}.cmd") { - #rename-item won't allow overwriting existing target file - Move-Item -Path "${scriptroot}.cmd" -Destination "${scriptroot}.cmd.lastrun" -Force - } -} -git pull $git_upstream master -git checkout "${scriptroot}.cmd" -git branch --set-upstream-to=origin/master master - -Set-Location $launchdir #restore original CWD -#see also: https://github.com/jdhitsolutions/WTToolbox - -# Define the necessary Win32 API functions and constants -Add-Type -TypeDefinition @" -using System; -using System.Runtime.InteropServices; - -public class WinAPI -{ - // Console Input/Output Handles - public const int STD_OUTPUT_HANDLE = -11; - public const uint ENABLE_QUICK_EDIT_MODE = 0x0040; - public const uint ENABLE_EXTENDED_FLAGS = 0x0080; - public const uint ENABLE_MOUSE_INPUT = 0x0010; - public const uint ENABLE_WINDOW_INPUT = 0x0008; - public const uint ENABLE_INSERT_MODE = 0x0020; - public const uint ENABLE_LINE_INPUT = 0x0002; - public const uint ENABLE_ECHO_INPUT = 0x0004; - public const uint ENABLE_PROCESSED_INPUT = 0x0001; - - // Console Modes - public const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004; - public const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008; - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern IntPtr GetStdHandle(int nStdHandle); - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); -} -"@ - -# Get the handle to the console output buffer -$stdoutHandle = [WinAPI]::GetStdHandle([WinAPI]::STD_OUTPUT_HANDLE) - -# Get the current console mode -[uint32]$currentMode = 0 -if (![WinAPI]::GetConsoleMode($stdoutHandle, [ref]$currentMode)) { - Write-Error "Failed to get console mode. Error code: $($LAST_ERROR)" - return -} - -# Enable virtual terminal processing -$newMode = $currentMode -bor [WinAPI]::ENABLE_VIRTUAL_TERMINAL_PROCESSING - -# Set the new console mode -if (-not [WinAPI]::SetConsoleMode($stdoutHandle, $newMode)) { - Write-Error "Failed to set console mode. Error code: $($LAST_ERROR)" - return -} - -Write-Host "Virtual terminal processing enabled successfully." - -write-host "`e[92m getpunk done `e[m" + +#powershell -Command "Invoke-WebRequest -Uri 'https://www.gitea1.intx.com.au/jn/punkshell/raw/branch/master/getpunk.cmd' -OutFile 'getpunk.cmd'; Start-Process 'getpunk.cmd' -NoNewWindow -Wait" + +#todo - support either fossil or git + +#check if git available +#if not, check/install winget, winget git + +$git_upstream = "https://gitea1.intx.com.au/jn/punkshell.git" +$launchdir = Get-Location #store original CWD +$scriptfolder = Resolve-Path (Split-Path -Path $PSCommandPath -Parent) +$punkfolder = "" +$scriptroot = "$([System.IO.Path]::GetFileNameWithoutExtension($PSCommandPath))" +$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + +if (-not (Get-Command "git" -ErrorAction SilentlyContinue)) { + Write-Host "The git command doesn't seem to be available. 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 -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" + } + $gitversion = (Find-WinGetPackage Git.Git).Version + if ($gitversion) { + Write-Host "Installing git version: ${gitversion}" + Install-WinGetPackage -Id "Git.Git" + } else { + Write-Host "Failed to find git 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 "git" -ErrorAction SilentlyContinue) { + Write-Host "git is now available" + } else { + Write-Host "git is still not available" + Write-HOst "Please install Git or relaunch your terminal and check it is available on the path." + exit + } +} + +if (($launchdir.Path) -ne ($scriptfolder.Path)) { + Write-Host "The current directory does not seem to be the folder in which the getpunk script is located." + $answer = Read-Host "Do you want to use the current directory '$($launchdir.Path) as the location for punkshell? Y|N`n Y to use launchdir '$($launchdir.Path)'`n 'N' to use script folder '$($scriptfolder.Path)`n Any other value to abort: " + if ($answer -match "y") { + $punkfolder = $launchdir + } elseif ($answer -match "n") { + $punkfolder = $scriptfolder + } else { + exit 1 + } + +} else { + $punkfolder = $scriptfolder +} +$punkfoldercontents = Get-ChildItem -Path $punkfolder -Force #include possibly hidden items such as .git folder +$contentcount = ( $punkfoldercontents | Measure-Object).Count +$effectively_empty = 0 +if ($contentcount -eq 0) { + $effectively_empty = 1 +} elseif ($punkfolder -eq $scriptfolder -and $contentcount -lt 10) { + #treat as empty if we have only a few files matching script root name + $scriptlike = get-childitem -Path $punkfolder | Where-Object {$_.name -like "${scriptroot}.*"} + if ($scriptlike.Count -eq $contentcount) { + $effectively_empty = 1 + } +} +if (-not($effectively_empty)) { + if (-not(Test-Path -Path (Join-Path -Path $punkfolder -ChildPath ".git") -PathType Container)) { + Write-Host "The folder $punkfolder contains other items, and it does not appear to be a git project root." + Write-Host "Please place this script in an empty folder which is to be the punkshell base folder." + exit + } else { + $repo_origin = git remote get-url origin + if ($repo_origin -ne $git_upstream) { + Write-Host "The current repository origin '$repo_origin' is not the expected upstream '${git_upstream}'" + $answer = Read-Host "Continue anyway? (Y|N)" + if (-not($answer -match "y")) { + exit 1 + } + } + } +} else { + #punkfolder is empty, or has just the current script +} +Set-Location -Path $punkfolder + + +if (-not(Test-Path -Path (Join-Path -Path $punkfolder -ChildPath ".git") -PathType Container)) { + git init + git remote add origin $git_upstream +} +git fetch origin +if (($launchdir.Path) -eq ($scriptfolder.Path)) { + if (Test-Path -Path "${scriptroot}.cmd") { + #Rename-Item won't allow overwriting existing target file + Move-Item -Path "${scriptroot}.cmd" -Destination "${scriptroot}.cmd.lastrun" -Force + } +} +git pull $git_upstream master +git checkout "${scriptroot}.cmd" +git branch --set-upstream-to=origin/master master + +Set-Location $launchdir #restore original CWD +#see also: https://github.com/jdhitsolutions/WTToolbox + +# Define the necessary Win32 API functions and constants +Add-Type -TypeDefinition @" +using System; +using System.Runtime.InteropServices; + +public class WinAPI +{ + // Console Input/Output Handles + public const int STD_OUTPUT_HANDLE = -11; + public const uint ENABLE_QUICK_EDIT_MODE = 0x0040; + public const uint ENABLE_EXTENDED_FLAGS = 0x0080; + public const uint ENABLE_MOUSE_INPUT = 0x0010; + public const uint ENABLE_WINDOW_INPUT = 0x0008; + public const uint ENABLE_INSERT_MODE = 0x0020; + public const uint ENABLE_LINE_INPUT = 0x0002; + public const uint ENABLE_ECHO_INPUT = 0x0004; + public const uint ENABLE_PROCESSED_INPUT = 0x0001; + + // Console Modes + public const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004; + public const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008; + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr GetStdHandle(int nStdHandle); + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); +} +"@ + +# Get the handle to the console output buffer +$stdoutHandle = [WinAPI]::GetStdHandle([WinAPI]::STD_OUTPUT_HANDLE) + +# Get the current console mode +[uint32]$currentMode = 0 +if (![WinAPI]::GetConsoleMode($stdoutHandle, [ref]$currentMode)) { + Write-Error "Failed to get console mode. Error code: $($LAST_ERROR)" + return +} + +# Enable virtual terminal processing +$newMode = $currentMode -bor [WinAPI]::ENABLE_VIRTUAL_TERMINAL_PROCESSING + +# Set the new console mode +if (-not [WinAPI]::SetConsoleMode($stdoutHandle, $newMode)) { + Write-Error "Failed to set console mode. Error code: $($LAST_ERROR)" + return +} + +Write-Host "Virtual terminal processing enabled successfully." + +write-host "`e[92m getpunk done `e[m" diff --git a/src/scriptapps/getpunk_wrap.toml b/src/scriptapps/getpunk_wrap.toml index 2cf7048f..eec21eb9 100644 --- a/src/scriptapps/getpunk_wrap.toml +++ b/src/scriptapps/getpunk_wrap.toml @@ -1,23 +1,23 @@ - -[application] - template="punk.multishell.cmd" - as_admin=false - - scripts=[ - "getpunk.ps1", - "getpunk.bash" - ] - - default_outputfile="getpunk.cmd" - default_nextshellpath="/usr/bin/env bash" - default_nextshelltype="bash" - - #valid nextshelltype entries are: tcl perl powershell bash. - #nextshellpath entries must be 128 characters or less. - - #win32.nextshellpath="pwsh -nop -nol -ExecutionPolicy bypass -File" - #win32.nextshelltype="pwsh" - - win32.nextshellpath="cmd.exe /c powershell -nop -nol -ExecutionPolicy ByPass -File" - win32.nextshelltype="powershell" - win32.outputfile="getpunk.cmd" + +[application] + template="punk.multishell.cmd" + as_admin=false + + scripts=[ + "getpunk.ps1", + "getpunk.bash" + ] + + default_outputfile="getpunk.cmd" + default_nextshellpath="/usr/bin/env bash" + default_nextshelltype="bash" + + #valid nextshelltype entries are: tcl perl powershell bash. + #nextshellpath entries must be 128 characters or less. + + #win32.nextshellpath="pwsh -nop -nol -ExecutionPolicy bypass -File" + #win32.nextshelltype="pwsh" + + win32.nextshellpath="cmd.exe /c powershell -nop -nol -ExecutionPolicy ByPass -File" + win32.nextshelltype="powershell" + win32.outputfile="getpunk.cmd" diff --git a/src/scriptapps/runtime.ps1 b/src/scriptapps/runtime.ps1 index e147e9f4..10422337 100644 --- a/src/scriptapps/runtime.ps1 +++ b/src/scriptapps/runtime.ps1 @@ -1,416 +1,416 @@ - - -function GetDynamicParamDictionary { - [CmdletBinding()] - param( - [Parameter(ValueFromPipeline=$true, Mandatory=$true)] - [string] $CommandName - ) - - begin { - # Get a list of params that should be ignored (they're common to all advanced functions) - $CommonParameterNames = [System.Runtime.Serialization.FormatterServices]::GetUninitializedObject([type] [System.Management.Automation.Internal.CommonParameters]) | - Get-Member -MemberType Properties | - Select-Object -ExpandProperty Name - } - - process { - # Create the dictionary that this scriptblock will return: - $DynParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary - - # Convert to object array and get rid of Common params: - (Get-Command $CommandName | Select-Object -exp Parameters).GetEnumerator() | - Where-Object { $CommonParameterNames -notcontains $_.Key } | - ForEach-Object { - $DynamicParameter = New-Object System.Management.Automation.RuntimeDefinedParameter ( - $_.Key, - $_.Value.ParameterType, - $_.Value.Attributes - ) - $DynParamDictionary.Add($_.Key, $DynamicParameter) - } - - # Return the dynamic parameters - return $DynParamDictionary - } -} -function ParameterDefinitions { - param( - [Parameter(ValueFromRemainingArguments=$true,Position = 1)][string[]] $opts - ) -} - -function psmain { - [CmdletBinding()] - #Empty param block (extra params can be added) - param( - [Parameter(Mandatory=$false, Position = 0)][string] $action = "" - ) - dynamicparam { - if ($action -eq 'list') { - $parameterAttribute = [System.Management.Automation.ParameterAttribute]@{ - ParameterSetName = "listruntime" - Mandatory = $false - } - $attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() - $attributeCollection.Add($parameterAttribute) - $dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new( - 'remote', [switch], $attributeCollection - ) - $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() - $paramDictionary.Add('remote', $dynParam1) - return $paramDictionary - } elseif ($action -eq 'fetch') { - #GetDynamicParamDictionary ParameterDefinitions - $parameterAttribute = [System.Management.Automation.ParameterAttribute]@{ - ParameterSetName = "fetchruntime" - Mandatory = $false - Position = 1 - } - $attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() - $attributeCollection.Add($parameterAttribute) - - $dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new( - 'runtime', [string], $attributeCollection - ) - - $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() - $paramDictionary.Add('runtime', $dynParam1) - return $paramDictionary - } elseif ($action -eq 'run') { - #GetDynamicParamDictionary ParameterDefinitions - $parameterAttribute = [System.Management.Automation.ParameterAttribute]@{ - ParameterSetName = "runargs" - Mandatory = $false - ValueFromRemainingArguments = $true - } - $attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() - $attributeCollection.Add($parameterAttribute) - - $dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new( - 'opts', [string[]], $attributeCollection - ) - - $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() - $paramDictionary.Add('opts', $dynParam1) - return $paramDictionary - } else { - #accept all args when action is unrecognised - so we can go to help anyway - $parameterAttribute = [System.Management.Automation.ParameterAttribute]@{ - ParameterSetName = "invalidaction" - Mandatory = $false - ValueFromRemainingArguments = $true - } - $attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() - $attributeCollection.Add($parameterAttribute) - - $dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new( - 'opts', [string[]], $attributeCollection - ) - - $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() - $paramDictionary.Add('opts', $dynParam1) - return $paramDictionary - } - } - process { - #Called once - we get a single item being our PSBoundParameters dictionary - #write-host "Bound Parameters:$($PSBoundParameters.Keys)" - switch ($PSBoundParameters.keys) { - 'action' { - write-host "got action " $PSBoundParameters.action - Set-Variable -Name $_ -Value $PSBoundParameters."$_" - $known_actions = @("fetch", "list", "run") - if (-not($known_actions -contains $action)) { - write-host "fetch '$action' not understood. Known_actions: $known_actions" - exit 1 - } - } - 'opts' { - # write-warning "Unused parameters: $($PSBoundParameters.$_)" - } - Default { - # write-warning "Unhandled parameter -> [$($_)]" - } - } - #foreach ($boundparam in $PSBoundParameters.Keys) { - # write-host "k: $boundparam" - #} - } - end { - # PSBoundParameters - #write-host "action:'$action'" - $outbase = $PSScriptRoot - $outbase = Resolve-Path -Path $outbase - #expected script location is the bin folder of a punk project - $rtfolder = Join-Path -Path $outbase -ChildPath "runtime" - #Binary artifact server url. (git is not ideal for this - but will do for now - todo - use artifact system within gitea?) - $artifacturl = "https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master" - switch ($action) { - 'fetch' { - $arch = "win32-x86_64" - $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" - $archurl = "$artifacturl/$arch" - $sha1url = "$archurl/sha1sums.txt" - $runtime = "tclsh902z.exe" - foreach ($boundparam in $PSBoundParameters.Keys) { - write-host "fetchopt: $boundparam $($PSBoundParameters[$boundparam])" - } - if ( $PSBoundParameters["runtime"].Length ) { - $runtime = $PSBoundParameters["runtime"] - } - $fileurl = "$archurl/$runtime" - - $output = join-path -Path $archfolder -ChildPath $runtime - $sha1local = join-path -Path $archfolder -ChildPath "sha1sums.txt" - - $container = split-path -Path $output -Parent - new-item -Path $container -ItemType Directory -force #create with intermediary folders if not already present - - try { - Write-Host "Fetching $sha1url" - Invoke-WebRequest -Uri $sha1url -OutFile $sha1local -ErrorAction Stop - Write-Host "sha1 saved at $sha1local" - } catch { - Write-Host "An error occurred while downloading ${sha1url}: $($_.Exception.Message)" - if ($_.Exception.Response) { - Write-Host "HTTP Status code: $($_.Exception.Response.StatusCode)" - } - } - if (Test-Path -Path $sha1local -PathType Leaf) { - $sha1Content = Get-Content -Path $sha1local - $stored_sha1 = "" - foreach ($line in $sha1Content) { - #all sha1sums have * (binary indicator) - review - $match = [regex]::Match($line,"(.*) [*]${runtime}$") - if ($match.Success) { - $stored_sha1 = $match.Groups[1].Value - Write-host "stored hash from sha1sums.txt: $storedhash" - break - } - } - if ($stored_sha1 -eq "") { - Write-Host "Unable to locate hash for $runtime in $sha1local - Aborting" - Write-Host "Please download and verify manually" - return - } - - $need_download = $false - if (Test-Path -Path $output -PathType Leaf) { - Write-Host "Runtime already found at $output" - Write-Host "Checking sha1 checksum of local file versus sha1 of server file" - $file_sha1 = Get-FileHash -Path "$output" -Algorithm SHA1 - if (${file_sha1}.Hash -ne $stored_sha1) { - Write-Host "$runtime on server has different sha1 hash - Download required" - $need_download = $true - } - } else { - Write-Host "$runtime not found locally - Download required" - $need_download = $true - } - - if ($need_download) { - Write-Host "Downloading from $fileurl ..." - try { - Invoke-WebRequest -Uri $fileurl -OutFile "${output}.tmp" -ErrorAction Stop - Write-Host "Runtime saved at $output.tmp" - } - catch { - Write-Host "An error occurred while downloading $fileurl $($_.Exception.Message)" - if ($_.Exception.Response) { - Write-Host "HTTP Status code: $($_.Exception.Response.StatusCode)" - } - return - } - Write-Host "comparing sha1 checksum of downloaded file with data in sha1sums.txt" - Start-Sleep -Seconds 1 #REVIEW - give at least some time for windows to do its thing? (av filters?) - $newfile_sha1 = Get-FileHash -Path "${output}.tmp" -Algorithm SHA1 - if (${newfile_sha1}.Hash -eq $stored_sha1) { - Write-Host "sha1 checksum ok" - Move-Item -Path "${output}.tmp" -Destination "${output}" -Force - Write-Host "Runtime is available at ${output}" - } else { - Write-Host "WARNING! sha1 of downloaded file at $output.tmp does not match stored sha1 from sha1sums.txt" - return - } - } else { - Write-Host "Local copy of runtime at $output seems to match sha1 checksum of file on server." - Write-Host "No download required" - } - } else { - Write-Host "Unable to consult local copy of sha1sums.txt at $sha1local" - if (Test-Path -Path $output -PathType Leaf) { - Write-Host "A runtime is available at $output - but we failed to retrieve the list of sha1sums from the server" - Write-Host "Unable to check for updated version at this time." - } else { - Write-Host "Please retry - or manually download a runtime from $archurl and verify checksums" - } - } - } - 'run' { - #select first (or configured default) runtime and launch, passing arguments - $arch = "win32-x86_64" - $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" - if (-not(Test-Path -Path $archfolder -PathType Container)) { - write-host "No runtimes seem to be installed for $arch`nPlease use 'runtime.cmd fetch' to install" - } else { - $dircontents = (get-childItem -Path $archfolder -File | Sort-Object Name) - if ($dircontents.Count -gt 0) { - #write-host "run.." - write-host "num params: $($PSBoundParameters.opts.count)" - - #todo - use 'active' runtime - need to lookup (PSToml?) - #when no 'active' runtime for this os-arch - use last item (sorted in dictionary order) - $active = $dircontents[-1].FullName - write-host "using: $active" - if ($PSBoundParameters.opts.Length -gt 0) { - $optsType = $PSBoundParameters.opts.GetType() #method can only be called if .opts is not null - write-host "type of opts: $($optsType.FullName)" - foreach ($boundparam in $PSBoundParameters.opts) { - write-host $boundparam - } - Write-Host "opts: $($PSBoundParameters.opts)" - Write-Host "args: $args" - Write-HOst "argscount: $($args.Count)" - $arglist = @() - foreach ($o in $PSBoundParameters.opts) { - $oquoted = $o -replace '"', "`\`"" - #$oquoted = $oquoted -replace "'", "`'" - if ($oquoted -match "\s") { - $oquoted = "`"$oquoted`"" - } - $arglist += @($oquoted) - } - $arglist = $arglist.TrimEnd(' ') - write-host "arglist: $arglist" - #$arglist = $PSBoundParameters.opts - Start-Process -FilePath $active -ArgumentList $arglist -NoNewWindow -Wait - } else { - #powershell 5.1 and earlier can't accept an empty -ArgumentList value :/ !! - #$arglist = @() - #Start-Process -FilePath $active -ArgumentList $arglist -NoNewWindow -Wait - #Start-Process -FilePath $active -ArgumentList "" -NoNewWindow -Wait - Start-Process -FilePath $active -NoNewWindow -Wait - } - } else { - write-host "No files found in $archfolder" - write-host "No runtimes seem to be installed for $arch`nPlease use 'runtime.cmd fetch' to install." - } - } - } - 'list' { - #todo - option to list for other os-arch - $arch = 'win32-x86_64' - $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" - $sha1local = join-path -Path $archfolder -ChildPath "sha1sums.txt" - $archurl = "$artifacturl/$arch" - $sha1url = "$archurl/sha1sums.txt" - if ( $PSBoundParameters.ContainsKey('remote') ) { - if (-not (test-path -Path $archfolder -Type Container)) { - new-item -Path $container -ItemType Directory -force #create with intermediary folders if not already present - } - write-host "Checking for available remote runtimes for" - Write-Host "Fetching $sha1url" - Invoke-WebRequest -Uri $sha1url -OutFile $sha1local -ErrorAction Stop - Write-Host "sha1 saved at $sha1local" - $sha1Content = Get-Content -Path $sha1local - $remotedict = @{} - foreach ($line in $sha1Content) { - #all sha1sums have * (binary indicator) - review - $match = [regex]::Match($line,"(.*) [*](.*)$") - if ($match.Success) { - $server_sha1 = $match.Groups[1].Value - $server_rt = $match.Groups[2].Value - $remotedict[$server_rt] = $server_sha1 - } - } - - $localdict = @{} - if (test-path -Path $archfolder -Type Container) { - $dircontents = (get-childItem -Path $archfolder -File | Where-object {-not ($(".txt",".tm") -contains $_.Extension) }) - foreach ($f in $dircontents) { - $local_sha1 = Get-FileHash -Path $(${f}.FullName) -Algorithm SHA1 - $localdict[$f.Name] = ${local_sha1}.Hash - } - } - - Write-host "-----------------------------------------------------------------------" - Write-host "Runtimes for $arch" - Write-host "Local $archfolder" - Write-host "Remote $archurl" - Write-host "-----------------------------------------------------------------------" - Write-host "Local Remote" - Write-host "-----------------------------------------------------------------------" - # 12345678910234567892023456789302345 - $G = "`e[32m" #Green - $Y = "`e[33m" #Yellow - $R = "`e[31m" #Red - $RST = "`e[m" - foreach ($key in $localdict.Keys) { - $local_sha1 = $($localdict[$key]) - if ($remotedict.ContainsKey($key)) { - if ($local_sha1 -eq $remotedict[$key]) { - $rhs = "Same version" - $C = $G - } else { - $rhs = "UPDATE AVAILABLE" - $C = $Y - } - } else { - $C = $R - $rhs = "(not listed on server)" - } - #ansi problems from cmd.exe not in windows terminal - review - $C = "" - $RST = "" - $lhs = "$key".PadRight(35, ' ') - write-host -nonewline "${C}${lhs}${RST}" - write-host $rhs - } - $lhs_missing = "-".PadRight(35, ' ') - foreach ($key in $remotedict.Keys) { - if (-not ($localdict.ContainsKey($key))) { - write-host -nonewline $lhs_missing - write-host $key - } - } - Write-host "-----------------------------------------------------------------------" - - } else { - if (test-path -Path $archfolder -Type Container) { - Write-host "-----------------------------------------------------------------------" - Write-Host "Local runtimes for $arch" - $dircontents = (get-childItem -Path $archfolder -File | Where-object {-not ($(".txt",".tm") -contains $_.Extension) }) - write-host "$(${dircontents}.count) files in $archfolder" - Write-host "-----------------------------------------------------------------------" - foreach ($f in $dircontents) { - write-host $f.Name - } - Write-host "-----------------------------------------------------------------------" - Write-host "Use: 'list -remote' to compare local runtimes with those available on the artifact server" - } else { - write-host "No runtimes seem to be installed for $arch in $archfolder`nPlease use 'runtime.cmd fetch' to install." - write-host "Use 'runtime.cmd list -remote' to see available runtimes for $arch" - } - } - } - default { - $actions = @("fetch", "list", "run") - write-host "Available actions: $actions" - write-host "received" - foreach ($boundparam in $PSBoundParameters.opts) { - write-host $boundparam - } - } - } - - return $PSBoundParameters - } -} -#write-host (psmain @args) -#$returnvalue = psmain @args -#Write-Host "Function Returned $returnvalue" -ForegroundColor Cyan -#return $returnvalue -psmain @args | out-null -exit 0 - + + +function GetDynamicParamDictionary { + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline=$true, Mandatory=$true)] + [string] $CommandName + ) + + begin { + # Get a list of params that should be ignored (they're common to all advanced functions) + $CommonParameterNames = [System.Runtime.Serialization.FormatterServices]::GetUninitializedObject([type] [System.Management.Automation.Internal.CommonParameters]) | + Get-Member -MemberType Properties | + Select-Object -ExpandProperty Name + } + + process { + # Create the dictionary that this scriptblock will return: + $DynParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary + + # Convert to object array and get rid of Common params: + (Get-Command $CommandName | Select-Object -exp Parameters).GetEnumerator() | + Where-Object { $CommonParameterNames -notcontains $_.Key } | + ForEach-Object { + $DynamicParameter = New-Object System.Management.Automation.RuntimeDefinedParameter ( + $_.Key, + $_.Value.ParameterType, + $_.Value.Attributes + ) + $DynParamDictionary.Add($_.Key, $DynamicParameter) + } + + # Return the dynamic parameters + return $DynParamDictionary + } +} +function ParameterDefinitions { + param( + [Parameter(ValueFromRemainingArguments=$true,Position = 1)][string[]] $opts + ) +} + +function psmain { + [CmdletBinding()] + #Empty param block (extra params can be added) + param( + [Parameter(Mandatory=$false, Position = 0)][string] $action = "" + ) + dynamicparam { + if ($action -eq 'list') { + $parameterAttribute = [System.Management.Automation.ParameterAttribute]@{ + ParameterSetName = "listruntime" + Mandatory = $false + } + $attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() + $attributeCollection.Add($parameterAttribute) + $dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new( + 'remote', [switch], $attributeCollection + ) + $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() + $paramDictionary.Add('remote', $dynParam1) + return $paramDictionary + } elseif ($action -eq 'fetch') { + #GetDynamicParamDictionary ParameterDefinitions + $parameterAttribute = [System.Management.Automation.ParameterAttribute]@{ + ParameterSetName = "fetchruntime" + Mandatory = $false + Position = 1 + } + $attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() + $attributeCollection.Add($parameterAttribute) + + $dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new( + 'runtime', [string], $attributeCollection + ) + + $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() + $paramDictionary.Add('runtime', $dynParam1) + return $paramDictionary + } elseif ($action -eq 'run') { + #GetDynamicParamDictionary ParameterDefinitions + $parameterAttribute = [System.Management.Automation.ParameterAttribute]@{ + ParameterSetName = "runargs" + Mandatory = $false + ValueFromRemainingArguments = $true + } + $attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() + $attributeCollection.Add($parameterAttribute) + + $dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new( + 'opts', [string[]], $attributeCollection + ) + + $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() + $paramDictionary.Add('opts', $dynParam1) + return $paramDictionary + } else { + #accept all args when action is unrecognised - so we can go to help anyway + $parameterAttribute = [System.Management.Automation.ParameterAttribute]@{ + ParameterSetName = "invalidaction" + Mandatory = $false + ValueFromRemainingArguments = $true + } + $attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new() + $attributeCollection.Add($parameterAttribute) + + $dynParam1 = [System.Management.Automation.RuntimeDefinedParameter]::new( + 'opts', [string[]], $attributeCollection + ) + + $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new() + $paramDictionary.Add('opts', $dynParam1) + return $paramDictionary + } + } + process { + #Called once - we get a single item being our PSBoundParameters dictionary + #write-host "Bound Parameters:$($PSBoundParameters.Keys)" + switch ($PSBoundParameters.keys) { + 'action' { + write-host "got action " $PSBoundParameters.action + Set-Variable -Name $_ -Value $PSBoundParameters."$_" + $known_actions = @("fetch", "list", "run") + if (-not($known_actions -contains $action)) { + write-host "fetch '$action' not understood. Known_actions: $known_actions" + exit 1 + } + } + 'opts' { + # write-warning "Unused parameters: $($PSBoundParameters.$_)" + } + Default { + # write-warning "Unhandled parameter -> [$($_)]" + } + } + #foreach ($boundparam in $PSBoundParameters.Keys) { + # write-host "k: $boundparam" + #} + } + end { + # PSBoundParameters + #write-host "action:'$action'" + $outbase = $PSScriptRoot + $outbase = Resolve-Path -Path $outbase + #expected script location is the bin folder of a punk project + $rtfolder = Join-Path -Path $outbase -ChildPath "runtime" + #Binary artifact server url. (git is not ideal for this - but will do for now - todo - use artifact system within gitea?) + $artifacturl = "https://www.gitea1.intx.com.au/jn/punkbin/raw/branch/master" + switch ($action) { + 'fetch' { + $arch = "win32-x86_64" + $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" + $archurl = "$artifacturl/$arch" + $sha1url = "$archurl/sha1sums.txt" + $runtime = "tclsh902z.exe" + foreach ($boundparam in $PSBoundParameters.Keys) { + write-host "fetchopt: $boundparam $($PSBoundParameters[$boundparam])" + } + if ( $PSBoundParameters["runtime"].Length ) { + $runtime = $PSBoundParameters["runtime"] + } + $fileurl = "$archurl/$runtime" + + $output = join-path -Path $archfolder -ChildPath $runtime + $sha1local = join-path -Path $archfolder -ChildPath "sha1sums.txt" + + $container = split-path -Path $output -Parent + new-item -Path $container -ItemType Directory -force #create with intermediary folders if not already present + + try { + Write-Host "Fetching $sha1url" + Invoke-WebRequest -Uri $sha1url -OutFile $sha1local -ErrorAction Stop + Write-Host "sha1 saved at $sha1local" + } catch { + Write-Host "An error occurred while downloading ${sha1url}: $($_.Exception.Message)" + if ($_.Exception.Response) { + Write-Host "HTTP Status code: $($_.Exception.Response.StatusCode)" + } + } + if (Test-Path -Path $sha1local -PathType Leaf) { + $sha1Content = Get-Content -Path $sha1local + $stored_sha1 = "" + foreach ($line in $sha1Content) { + #all sha1sums have * (binary indicator) - review + $match = [regex]::Match($line,"(.*) [*]${runtime}$") + if ($match.Success) { + $stored_sha1 = $match.Groups[1].Value + Write-host "stored hash from sha1sums.txt: $storedhash" + break + } + } + if ($stored_sha1 -eq "") { + Write-Host "Unable to locate hash for $runtime in $sha1local - Aborting" + Write-Host "Please download and verify manually" + return + } + + $need_download = $false + if (Test-Path -Path $output -PathType Leaf) { + Write-Host "Runtime already found at $output" + Write-Host "Checking sha1 checksum of local file versus sha1 of server file" + $file_sha1 = Get-FileHash -Path "$output" -Algorithm SHA1 + if (${file_sha1}.Hash -ne $stored_sha1) { + Write-Host "$runtime on server has different sha1 hash - Download required" + $need_download = $true + } + } else { + Write-Host "$runtime not found locally - Download required" + $need_download = $true + } + + if ($need_download) { + Write-Host "Downloading from $fileurl ..." + try { + Invoke-WebRequest -Uri $fileurl -OutFile "${output}.tmp" -ErrorAction Stop + Write-Host "Runtime saved at $output.tmp" + } + catch { + Write-Host "An error occurred while downloading $fileurl $($_.Exception.Message)" + if ($_.Exception.Response) { + Write-Host "HTTP Status code: $($_.Exception.Response.StatusCode)" + } + return + } + Write-Host "comparing sha1 checksum of downloaded file with data in sha1sums.txt" + Start-Sleep -Seconds 1 #REVIEW - give at least some time for windows to do its thing? (av filters?) + $newfile_sha1 = Get-FileHash -Path "${output}.tmp" -Algorithm SHA1 + if (${newfile_sha1}.Hash -eq $stored_sha1) { + Write-Host "sha1 checksum ok" + Move-Item -Path "${output}.tmp" -Destination "${output}" -Force + Write-Host "Runtime is available at ${output}" + } else { + Write-Host "WARNING! sha1 of downloaded file at $output.tmp does not match stored sha1 from sha1sums.txt" + return + } + } else { + Write-Host "Local copy of runtime at $output seems to match sha1 checksum of file on server." + Write-Host "No download required" + } + } else { + Write-Host "Unable to consult local copy of sha1sums.txt at $sha1local" + if (Test-Path -Path $output -PathType Leaf) { + Write-Host "A runtime is available at $output - but we failed to retrieve the list of sha1sums from the server" + Write-Host "Unable to check for updated version at this time." + } else { + Write-Host "Please retry - or manually download a runtime from $archurl and verify checksums" + } + } + } + 'run' { + #select first (or configured default) runtime and launch, passing arguments + $arch = "win32-x86_64" + $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" + if (-not(Test-Path -Path $archfolder -PathType Container)) { + write-host "No runtimes seem to be installed for $arch`nPlease use 'runtime.cmd fetch' to install" + } else { + $dircontents = (get-childItem -Path $archfolder -File | Sort-Object Name) + if ($dircontents.Count -gt 0) { + #write-host "run.." + write-host "num params: $($PSBoundParameters.opts.count)" + + #todo - use 'active' runtime - need to lookup (PSToml?) + #when no 'active' runtime for this os-arch - use last item (sorted in dictionary order) + $active = $dircontents[-1].FullName + write-host "using: $active" + if ($PSBoundParameters.opts.Length -gt 0) { + $optsType = $PSBoundParameters.opts.GetType() #method can only be called if .opts is not null + write-host "type of opts: $($optsType.FullName)" + foreach ($boundparam in $PSBoundParameters.opts) { + write-host $boundparam + } + Write-Host "opts: $($PSBoundParameters.opts)" + Write-Host "args: $args" + Write-HOst "argscount: $($args.Count)" + $arglist = @() + foreach ($o in $PSBoundParameters.opts) { + $oquoted = $o -replace '"', "`\`"" + #$oquoted = $oquoted -replace "'", "`'" + if ($oquoted -match "\s") { + $oquoted = "`"$oquoted`"" + } + $arglist += @($oquoted) + } + $arglist = $arglist.TrimEnd(' ') + write-host "arglist: $arglist" + #$arglist = $PSBoundParameters.opts + Start-Process -FilePath $active -ArgumentList $arglist -NoNewWindow -Wait + } else { + #powershell 5.1 and earlier can't accept an empty -ArgumentList value :/ !! + #$arglist = @() + #Start-Process -FilePath $active -ArgumentList $arglist -NoNewWindow -Wait + #Start-Process -FilePath $active -ArgumentList "" -NoNewWindow -Wait + Start-Process -FilePath $active -NoNewWindow -Wait + } + } else { + write-host "No files found in $archfolder" + write-host "No runtimes seem to be installed for $arch`nPlease use 'runtime.cmd fetch' to install." + } + } + } + 'list' { + #todo - option to list for other os-arch + $arch = 'win32-x86_64' + $archfolder = Join-Path -Path $rtfolder -ChildPath "$arch" + $sha1local = join-path -Path $archfolder -ChildPath "sha1sums.txt" + $archurl = "$artifacturl/$arch" + $sha1url = "$archurl/sha1sums.txt" + if ( $PSBoundParameters.ContainsKey('remote') ) { + if (-not (test-path -Path $archfolder -Type Container)) { + new-item -Path $container -ItemType Directory -force #create with intermediary folders if not already present + } + write-host "Checking for available remote runtimes for" + Write-Host "Fetching $sha1url" + Invoke-WebRequest -Uri $sha1url -OutFile $sha1local -ErrorAction Stop + Write-Host "sha1 saved at $sha1local" + $sha1Content = Get-Content -Path $sha1local + $remotedict = @{} + foreach ($line in $sha1Content) { + #all sha1sums have * (binary indicator) - review + $match = [regex]::Match($line,"(.*) [*](.*)$") + if ($match.Success) { + $server_sha1 = $match.Groups[1].Value + $server_rt = $match.Groups[2].Value + $remotedict[$server_rt] = $server_sha1 + } + } + + $localdict = @{} + if (test-path -Path $archfolder -Type Container) { + $dircontents = (get-childItem -Path $archfolder -File | Where-object {-not ($(".txt",".tm") -contains $_.Extension) }) + foreach ($f in $dircontents) { + $local_sha1 = Get-FileHash -Path $(${f}.FullName) -Algorithm SHA1 + $localdict[$f.Name] = ${local_sha1}.Hash + } + } + + Write-host "-----------------------------------------------------------------------" + Write-host "Runtimes for $arch" + Write-host "Local $archfolder" + Write-host "Remote $archurl" + Write-host "-----------------------------------------------------------------------" + Write-host "Local Remote" + Write-host "-----------------------------------------------------------------------" + # 12345678910234567892023456789302345 + $G = "`e[32m" #Green + $Y = "`e[33m" #Yellow + $R = "`e[31m" #Red + $RST = "`e[m" + foreach ($key in $localdict.Keys) { + $local_sha1 = $($localdict[$key]) + if ($remotedict.ContainsKey($key)) { + if ($local_sha1 -eq $remotedict[$key]) { + $rhs = "Same version" + $C = $G + } else { + $rhs = "UPDATE AVAILABLE" + $C = $Y + } + } else { + $C = $R + $rhs = "(not listed on server)" + } + #ansi problems from cmd.exe not in windows terminal - review + $C = "" + $RST = "" + $lhs = "$key".PadRight(35, ' ') + write-host -nonewline "${C}${lhs}${RST}" + write-host $rhs + } + $lhs_missing = "-".PadRight(35, ' ') + foreach ($key in $remotedict.Keys) { + if (-not ($localdict.ContainsKey($key))) { + write-host -nonewline $lhs_missing + write-host $key + } + } + Write-host "-----------------------------------------------------------------------" + + } else { + if (test-path -Path $archfolder -Type Container) { + Write-host "-----------------------------------------------------------------------" + Write-Host "Local runtimes for $arch" + $dircontents = (get-childItem -Path $archfolder -File | Where-object {-not ($(".txt",".tm") -contains $_.Extension) }) + write-host "$(${dircontents}.count) files in $archfolder" + Write-host "-----------------------------------------------------------------------" + foreach ($f in $dircontents) { + write-host $f.Name + } + Write-host "-----------------------------------------------------------------------" + Write-host "Use: 'list -remote' to compare local runtimes with those available on the artifact server" + } else { + write-host "No runtimes seem to be installed for $arch in $archfolder`nPlease use 'runtime.cmd fetch' to install." + write-host "Use 'runtime.cmd list -remote' to see available runtimes for $arch" + } + } + } + default { + $actions = @("fetch", "list", "run") + write-host "Available actions: $actions" + write-host "received" + foreach ($boundparam in $PSBoundParameters.opts) { + write-host $boundparam + } + } + } + + return $PSBoundParameters + } +} +#write-host (psmain @args) +#$returnvalue = psmain @args +#Write-Host "Function Returned $returnvalue" -ForegroundColor Cyan +#return $returnvalue +psmain @args | out-null +exit 0 + diff --git a/src/scriptapps/runtime_wrap.toml b/src/scriptapps/runtime_wrap.toml index 115a88f1..30c1667d 100644 --- a/src/scriptapps/runtime_wrap.toml +++ b/src/scriptapps/runtime_wrap.toml @@ -1,27 +1,27 @@ - -[application] - template="punk.multishell.cmd" - as_admin=false - - scripts=[ - "runtime.ps1", - "runtime.bash" - ] - - default_outputfile="runtime.cmd" - default_nextshellpath="/usr/bin/env bash" - default_nextshelltype="bash" - - #valid nextshelltype entries are: tcl perl powershell bash. - #nextshellpath entries must be 64 characters or less. - - #don't use -c for launching - or in old powershell, arguments such as "a b" will become 2 arguments a b - #do use -File (even though pwsh doesn't require it) - #win32.nextshellpath="pwsh -nop -nol -ExecutionPolicy bypass -File" - #win32.nextshelltype="pwsh" - - #powershell needs cmd.exe to preserve spaced args - win32.nextshellpath="cmd.exe /c powershell -nop -nol -ExecutionPolicy bypass -File" - win32.nextshelltype="powershell" - - win32.outputfile="runtime.cmd" + +[application] + template="punk.multishell.cmd" + as_admin=false + + scripts=[ + "runtime.ps1", + "runtime.bash" + ] + + default_outputfile="runtime.cmd" + default_nextshellpath="/usr/bin/env bash" + default_nextshelltype="bash" + + #valid nextshelltype entries are: tcl perl powershell bash. + #nextshellpath entries must be 64 characters or less. + + #don't use -c for launching - or in old powershell, arguments such as "a b" will become 2 arguments a b + #do use -File (even though pwsh doesn't require it) + #win32.nextshellpath="pwsh -nop -nol -ExecutionPolicy bypass -File" + #win32.nextshelltype="pwsh" + + #powershell needs cmd.exe to preserve spaced args + win32.nextshellpath="cmd.exe /c powershell -nop -nol -ExecutionPolicy bypass -File" + win32.nextshelltype="powershell" + + win32.outputfile="runtime.cmd"