App-Ariza.git | resources/templates/ | installer-common-windows.ps1


###############################################################################
# ariza installer runtime (Windows / PowerShell)
#
# Inlined verbatim into install.ps1 and uninstall.ps1 at render time, so a
# generated installer is one self-contained file.
#
# Adapted from the pre-bundle installers' template/lib/common-windows.ps1:
# the registry, the package managers and the shortcut helpers are gone (a
# bundle installs none of that), the user-PATH machinery is kept, because
# an unguarded PATH write is how a user ends up with the same directory in
# it eleven times.
#
# Callers must have set: $AppDisplay, $AppExec, $ArizaRoot.
###############################################################################

$ErrorActionPreference = 'Stop'
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

function Ariza-Log  { param([string]$Message) Write-Host "==> $Message" -ForegroundColor Blue }
function Ariza-Ok   { param([string]$Message) Write-Host "ok  $Message" -ForegroundColor Green }
function Ariza-Warn { param([string]$Message) Write-Host "!!  $Message" -ForegroundColor Yellow }

function Ariza-Err {
    param([string]$Message)
    Write-Host "error $Message" -ForegroundColor Red
    exit 1
}

# ---- downloading -------------------------------------------------------------

function Ariza-Download {
    param([string]$Url, [string]$Dest)
    # -UseBasicParsing keeps this working on a Server Core box with no
    # Internet Explorer engine, which is where Invoke-WebRequest's default
    # parser falls over.
    Invoke-WebRequest -Uri $Url -OutFile $Dest -UseBasicParsing
}

# ---- checksums ---------------------------------------------------------------

function Ariza-Sha256 {
    param([string]$Path)
    # Use the .NET implementation directly. Get-FileHash normally ships in
    # Microsoft.PowerShell.Utility, but minimal/non-standard PowerShell 5.1
    # environments can lack that cmdlet while still providing the runtime
    # APIs the installer needs.
    $stream = [IO.File]::OpenRead($Path)
    try {
        $sha = [Security.Cryptography.SHA256]::Create()
        try {
            $hash = $sha.ComputeHash($stream)
            return ([BitConverter]::ToString($hash)).Replace('-', '').ToLowerInvariant()
        }
        finally {
            $sha.Dispose()
        }
    }
    finally {
        $stream.Dispose()
    }
}

function Ariza-FirstWord {
    param([string]$Path)
    # A `<hex>  <filename>` checksum file, as shasum -c reads.
    $line = (Get-Content -LiteralPath $Path -TotalCount 1)
    if (-not $line) { return '' }
    return ($line -split '\s+')[0].ToLower()
}

function Ariza-VerifySha256 {
    param([string]$Path, [string]$Expected)
    $actual = Ariza-Sha256 $Path
    if ($actual -ne $Expected.ToLower()) {
        Ariza-Err "checksum mismatch for $(Split-Path -Leaf $Path): expected $Expected, got $actual -- not installing"
    }
}

# ---- unpacking ---------------------------------------------------------------

function Ariza-Extract {
    param([string]$Archive, [string]$Into)
    New-Item -ItemType Directory -Path $Into -Force | Out-Null
    if ($Archive.ToLower().EndsWith('.zip')) {
        Expand-Archive -LiteralPath $Archive -DestinationPath $Into -Force
    }
    else {
        # bsdtar has shipped in Windows since 10 1803 and reads .tar.gz,
        # which is what ariza packages every platform's bundle as.
        $tar = Get-Command tar.exe -ErrorAction SilentlyContinue
        if (-not $tar) {
            Ariza-Err "need tar.exe (Windows 10 1803 or later) to unpack $(Split-Path -Leaf $Archive)"
        }
        & $tar.Path -x -z -f $Archive -C $Into
        if ($LASTEXITCODE -ne 0) {
            Ariza-Err "tar could not unpack $(Split-Path -Leaf $Archive) (exit $LASTEXITCODE)"
        }
    }
}

# ---- user PATH ---------------------------------------------------------------
#
# The user environment in the registry (HKCU\Environment), never the
# machine one: nothing here needs administrator rights, and nothing here
# should be visible to other accounts.

function Ariza-PersistPath {
    param([string]$Dir)
    $current = [Environment]::GetEnvironmentVariable('PATH', 'User')
    if (-not $current) { $current = '' }
    $parts = $current -split ';' | Where-Object { $_ -ne '' }
    if ($parts -notcontains $Dir) {
        [Environment]::SetEnvironmentVariable('PATH', ((@($Dir) + $parts) -join ';'), 'User')
        Ariza-Ok "added $Dir to your PATH"
    }
    if (($env:PATH -split ';') -notcontains $Dir) {
        $env:PATH = "$Dir;$env:PATH"
    }
}

function Ariza-UnpersistPath {
    param([string]$Dir)
    $current = [Environment]::GetEnvironmentVariable('PATH', 'User')
    if (-not $current) { return }
    $parts = $current -split ';' | Where-Object { $_ -ne '' }
    if ($parts -notcontains $Dir) { return }
    $kept = $parts | Where-Object { $_ -ne $Dir }
    [Environment]::SetEnvironmentVariable('PATH', ($kept -join ';'), 'User')
    Ariza-Ok "removed $Dir from your PATH"
}

# ---- the `current` junction --------------------------------------------------
#
# A junction rather than a symbolic link: creating a symlink on Windows
# needs either administrator rights or Developer Mode, and an installer
# that demands either for a per-user install is one nobody runs.

function Ariza-RemoveLink {
    param([string]$Path)
    if (-not (Test-Path -LiteralPath $Path)) { return }
    $item = Get-Item -LiteralPath $Path -Force
    if (-not $item.LinkType) {
        Ariza-Err "$Path exists and is not a junction -- move it aside and re-run"
    }
    # .Delete() removes the junction itself; Remove-Item -Recurse would
    # walk through it and delete the target's contents.
    $item.Delete()
}

function Ariza-PointCurrent {
    param([string]$Target)
    $link = Join-Path $ArizaRoot 'current'
    Ariza-RemoveLink $link
    New-Item -ItemType Junction -Path $link -Target $Target | Out-Null
}