#requires -Version 5.1 [CmdletBinding()] param() $ErrorActionPreference = 'Stop' if ([Environment]::OSVersion.Platform -ne [PlatformID]::Win32NT) { throw 'The Jun OS graphical installer can only run on Windows.' } # when this file is compiled with tools/build-installer-exe.ps1 # the host process is JunSetup.exe, NOT powershell.exe. so # MainModule is useless for "give me a shell to run install.ps1 # in", it would relaunch the installer inside itself. resolve # the real powershell.exe off SystemRoot instead. Sysnative is # the alias a 32 bit process uses to reach the 64 bit System32, # and it only exists for such a process, so try it first and fall # back. $script:PowerShellExe = @( (Join-Path $env:SystemRoot 'Sysnative\WindowsPowerShell\v1.0\powershell.exe'), (Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe') ) | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1 if (-not $script:PowerShellExe) { $script:PowerShellExe = 'powershell.exe' } $script:IsCompiled = $PSCommandPath -and $PSCommandPath.EndsWith('.exe', 'OrdinalIgnoreCase') # tools/build-installer-exe.ps1 rewrites the next line, and only # that line, to base64 of install.ps1. leave the marker comment # and the exact assignment shape alone or the build stops # embedding and says nothing about it. $script:EmbeddedInstaller = '' # JUN_EMBEDDED_INSTALLER # same deal, a release build writes its tag and commit in here. # set = this exe installs exactly that version, no channel pick. $script:PinnedRef = '' # JUN_PINNED_REF $script:PinnedSha = '' # JUN_PINNED_SHA # and JunOS.exe, base64, from the same build. a draft release can't # be downloaded, a fork has none, and without it the shortcut ends # up pointing at powershell.exe. $script:EmbeddedManager = '' # JUN_EMBEDDED_MANAGER # install.ps1 git clones the repo itself, so the exe doesn't have # to carry one. embedding that single script is the whole # difference between "double click" and "download the repo # first". function Resolve-InstallerScript { if (-not $script:EmbeddedInstaller) { $beside = Join-Path $PSScriptRoot 'install.ps1' if (-not (Test-Path -LiteralPath $beside)) { throw 'install.ps1 was not found beside this file. Download or clone the complete Jun repository and try again.' } return $beside } $temp = Join-Path ([IO.Path]::GetTempPath()) ("jun-install-$PID.ps1") $bytes = [Convert]::FromBase64String($script:EmbeddedInstaller) [IO.File]::WriteAllBytes($temp, $bytes) $script:tempInstaller = $temp return $temp } $script:tempInstaller = $null function Resolve-ManagerExe { if (-not $script:EmbeddedManager) { return $null } $temp = Join-Path ([IO.Path]::GetTempPath()) ("jun-manager-$PID.exe") [IO.File]::WriteAllBytes($temp, [Convert]::FromBase64String($script:EmbeddedManager)) $script:tempManager = $temp return $temp } $script:tempManager = $null if ([Threading.Thread]::CurrentThread.GetApartmentState() -ne [Threading.ApartmentState]::STA) { if (-not $PSCommandPath) { throw 'Run installer-gui.ps1 from a file so it can start in STA mode.' } # a compiled build can't relaunch itself here, it would just # spawn another non-STA copy forever. ps2exe -STA is what makes # this branch unreachable, so an exe landing in it means the # build dropped the flag. if ($script:IsCompiled) { throw 'This build was compiled without -STA. Rebuild with tools/build-installer-exe.ps1.' } $restart = [Diagnostics.ProcessStartInfo]::new() $restart.FileName = $script:PowerShellExe $restart.Arguments = "-NoProfile -ExecutionPolicy Bypass -STA -File `"$PSCommandPath`"" $restart.UseShellExecute = $true $restart.WindowStyle = [Diagnostics.ProcessWindowStyle]::Hidden [Diagnostics.Process]::Start($restart) | Out-Null exit } Add-Type -AssemblyName PresentationFramework Add-Type -AssemblyName PresentationCore Add-Type -AssemblyName WindowsBase Add-Type -AssemblyName System.Windows.Forms # process output callbacks have no PowerShell runspace. WPF # drains this queue on its dispatcher thread instead. if (-not ('InstallerProcessOutput' -as [type])) { Add-Type -TypeDefinition @' using System.Collections.Concurrent; using System.Diagnostics; public sealed class InstallerProcessOutput { public readonly ConcurrentQueue Lines = new ConcurrentQueue(); public void Attach(Process process) { process.OutputDataReceived += OnData; process.ErrorDataReceived += OnData; } private void OnData(object sender, DataReceivedEventArgs args) { if (args.Data != null) Lines.Enqueue(args.Data); } } '@ } [xml]$xaml = @' Ω