139 lines
5.7 KiB
PowerShell
139 lines
5.7 KiB
PowerShell
[CmdletBinding()]
|
|
param()
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
function Get-Sha256([string]$Path) {
|
|
return (Get-FileHash -Algorithm SHA256 -LiteralPath $Path).Hash.ToLowerInvariant()
|
|
}
|
|
|
|
function Get-UserPathValue {
|
|
if ($env:THT_USER_PATH_FILE) {
|
|
if (Test-Path -LiteralPath $env:THT_USER_PATH_FILE) {
|
|
return [System.IO.File]::ReadAllText($env:THT_USER_PATH_FILE)
|
|
}
|
|
return ''
|
|
}
|
|
return [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
}
|
|
|
|
function Set-UserPathValue([string]$Value) {
|
|
if ($env:THT_USER_PATH_FILE) {
|
|
[System.IO.File]::WriteAllText($env:THT_USER_PATH_FILE, $Value)
|
|
return
|
|
}
|
|
[Environment]::SetEnvironmentVariable('Path', $Value, 'User')
|
|
}
|
|
|
|
function Add-InstallDirectoryToUserPath([string]$InstallDirectory) {
|
|
$currentPath = Get-UserPathValue
|
|
$entries = @($currentPath -split ';' | Where-Object { $_ -ne '' })
|
|
if ($entries | Where-Object { $_.TrimEnd('\\') -ieq $InstallDirectory.TrimEnd('\\') }) {
|
|
return $false
|
|
}
|
|
$newPath = (@($entries) + $InstallDirectory) -join ';'
|
|
Set-UserPathValue $newPath
|
|
return $true
|
|
}
|
|
|
|
function Replace-InstalledBinary([string]$StagedBinary, [string]$TargetBinary) {
|
|
$backupBinary = $null
|
|
$replacedExistingBinary = $false
|
|
try {
|
|
if (Test-Path -LiteralPath $TargetBinary -PathType Leaf) {
|
|
$backupBinary = Join-Path (Split-Path -Parent $TargetBinary) ('.tht-previous-' + [guid]::NewGuid() + '.exe')
|
|
[System.IO.File]::Replace($StagedBinary, $TargetBinary, $backupBinary, $true)
|
|
$replacedExistingBinary = $true
|
|
} else {
|
|
[System.IO.File]::Move($StagedBinary, $TargetBinary)
|
|
}
|
|
|
|
& $TargetBinary --help | Out-Null
|
|
if ($LASTEXITCODE -ne 0) { throw 'installed tht did not pass its help check' }
|
|
|
|
if ($backupBinary -and (Test-Path -LiteralPath $backupBinary -PathType Leaf)) {
|
|
Remove-Item -LiteralPath $backupBinary -Force -ErrorAction Stop
|
|
}
|
|
} catch {
|
|
$installFailure = $_
|
|
if ($replacedExistingBinary -and $backupBinary -and (Test-Path -LiteralPath $backupBinary -PathType Leaf)) {
|
|
try {
|
|
[System.IO.File]::Replace($backupBinary, $TargetBinary, $null, $true)
|
|
$backupBinary = $null
|
|
} catch {
|
|
throw "tht replacement failed; the previous executable remains at $backupBinary. Original failure: $installFailure. Restore failure: $_"
|
|
}
|
|
}
|
|
throw $installFailure
|
|
} finally {
|
|
Remove-Item -LiteralPath $StagedBinary -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
$repositoryRoot = Split-Path -Parent $PSScriptRoot
|
|
$buildOutput = $null
|
|
$sourceArtifactForInstall = $null
|
|
$installDirectory = if ($env:THT_INSTALL_DIRECTORY) {
|
|
$env:THT_INSTALL_DIRECTORY
|
|
} else {
|
|
Join-Path $env:LOCALAPPDATA 'ThothII\bin'
|
|
}
|
|
$installDirectory = [System.IO.Path]::GetFullPath($installDirectory)
|
|
|
|
if ($env:THT_BUILD_ARTIFACT) {
|
|
$sourceArtifact = $env:THT_BUILD_ARTIFACT
|
|
if (-not (Test-Path -LiteralPath $sourceArtifact -PathType Leaf)) {
|
|
throw 'THT_BUILD_ARTIFACT is not a regular file'
|
|
}
|
|
if (-not $env:THT_BUILD_ARTIFACT_SHA256 -or $env:THT_BUILD_ARTIFACT_SHA256 -notmatch '^[0-9A-Fa-f]{64}$') {
|
|
throw 'THT_BUILD_ARTIFACT_SHA256 must be a 64-character SHA-256 digest'
|
|
}
|
|
if ((Get-Sha256 $sourceArtifact) -ne $env:THT_BUILD_ARTIFACT_SHA256.ToLowerInvariant()) {
|
|
throw 'packaged tht artifact checksum does not match'
|
|
}
|
|
$sourceArtifactForInstall = $sourceArtifact
|
|
} else {
|
|
if ($env:PROCESSOR_ARCHITECTURE -notin @('AMD64', 'x86_64')) {
|
|
throw "unsupported Windows processor architecture: $env:PROCESSOR_ARCHITECTURE (supported: AMD64)"
|
|
}
|
|
$buildOutput = Join-Path ([System.IO.Path]::GetTempPath()) ("tht-build-" + [guid]::NewGuid())
|
|
New-Item -ItemType Directory -Path $buildOutput -Force | Out-Null
|
|
$previousOutput = $env:THT_THT_OUTPUT_DIRECTORY
|
|
$env:THT_THT_OUTPUT_DIRECTORY = $buildOutput
|
|
try {
|
|
& bash (Join-Path $repositoryRoot 'scripts/build-tht.sh') --all
|
|
if ($LASTEXITCODE -ne 0) { throw 'the repository-pinned Docker build failed' }
|
|
} finally {
|
|
$env:THT_THT_OUTPUT_DIRECTORY = $previousOutput
|
|
}
|
|
$sourceArtifactForInstall = Join-Path $buildOutput 'tht-windows-amd64.exe'
|
|
if (-not (Test-Path -LiteralPath $sourceArtifactForInstall -PathType Leaf)) {
|
|
throw 'the repository-pinned Docker build did not produce tht-windows-amd64.exe'
|
|
}
|
|
}
|
|
|
|
if ((Get-Item -LiteralPath $sourceArtifactForInstall).Length -eq 0) { throw 'native artifact is empty' }
|
|
New-Item -ItemType Directory -Path $installDirectory -Force | Out-Null
|
|
$stagedBinary = Join-Path $installDirectory ('.tht-' + [guid]::NewGuid() + '.exe')
|
|
try {
|
|
Copy-Item -LiteralPath $sourceArtifactForInstall -Destination $stagedBinary -Force
|
|
& $stagedBinary --help | Out-Null
|
|
if ($LASTEXITCODE -ne 0) { throw 'native artifact did not pass its help check' }
|
|
Replace-InstalledBinary $stagedBinary (Join-Path $installDirectory 'tht.exe')
|
|
} finally {
|
|
Remove-Item -LiteralPath $stagedBinary -Force -ErrorAction SilentlyContinue
|
|
if ($buildOutput) { Remove-Item -LiteralPath $buildOutput -Recurse -Force -ErrorAction SilentlyContinue }
|
|
}
|
|
|
|
$pathChanged = Add-InstallDirectoryToUserPath $installDirectory
|
|
if ($env:Path -notlike "*$installDirectory*") {
|
|
$env:Path = "$installDirectory;$env:Path"
|
|
}
|
|
& (Join-Path $installDirectory 'tht.exe') --help | Out-Null
|
|
if ($LASTEXITCODE -ne 0) { throw 'installed tht did not pass its help check' }
|
|
Write-Output "Installed tht at $(Join-Path $installDirectory 'tht.exe')"
|
|
if ($pathChanged) {
|
|
Write-Output 'Added ThothII to your user PATH. Open a new terminal before using tht there.'
|
|
}
|