App-Ariza.git | t/golden/ | installer-uninstall.ps1
<#
.SYNOPSIS
Example App uninstaller (Windows).
.DESCRIPTION
Generated by ariza from App::ExampleApp's ariza.toml. Do not edit this
file: edit ariza's resources/templates/uninstall-windows.ps1.j2 and
re-run `ariza installers --app=<this repository>`.
Removes exactly what install.ps1 created: the versions directory, the
`current` junction, and the PATH entry it added. Anything
Example App itself wrote — your data, your configuration — is left
alone, and this says where it is rather than deciding for you.
.EXAMPLE
irm https://raw.githubusercontent.com/example-org/App-ExampleApp/HEAD/uninstall.ps1 | iex
#>
[CmdletBinding()]
param()
$AppDisplay = 'Example App'
$AppExec = 'exampleapp'
$ArizaRoot = Join-Path $env:LOCALAPPDATA $AppDisplay
$ArizaBinDir = Join-Path (Join-Path $ArizaRoot 'current') 'bin'
############################# common runtime ##################################
###############################################################################
# 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
}
###############################################################################
function Ariza-Main {
Ariza-Log "uninstalling $AppDisplay"
# The PATH entry goes first: it names a path under $ArizaRoot, and
# removing the tree before the entry would leave a dangling PATH
# element if anything below failed.
Ariza-UnpersistPath $ArizaBinDir
if (Test-Path -LiteralPath $ArizaRoot) {
# The junction is deleted as a link first; a recursive remove
# would otherwise walk through it and delete the same version
# directory twice, which on some Windows builds fails noisily
# half way. Anything else at that path is left for Remove-Item.
$current = Join-Path $ArizaRoot 'current'
if ((Test-Path -LiteralPath $current) -and
(Get-Item -LiteralPath $current -Force).LinkType) {
Ariza-RemoveLink $current
}
Remove-Item -LiteralPath $ArizaRoot -Recurse -Force
Ariza-Ok "removed $ArizaRoot"
}
else {
Ariza-Log "nothing installed at $ArizaRoot"
}
Write-Host ''
Ariza-Ok "$AppDisplay uninstalled"
Write-Host ' Not touched, because this installer never wrote them:'
Write-Host " $(Join-Path $env:LOCALAPPDATA $AppExec)"
Write-Host " any data or configuration $AppDisplay created for you"
}
Ariza-Main