Files
covenant-setup/scripts/windows-vm/Start-InteractiveSelfInstall.ps1
T
2026-04-27 18:58:52 -05:00

148 lines
5.6 KiB
PowerShell

[CmdletBinding()]
param(
[Parameter(Mandatory)][string]$InstallerPath,
[Parameter(Mandatory)][string]$ResultPath,
[string]$ScriptRoot = $PSScriptRoot,
[int]$TimeoutSeconds = 600,
[string]$TaskNamePrefix = "CovenantSetupSelfInstall"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
function Quote-TaskArgument {
param([Parameter(Mandatory)][string]$Value)
return '"' + $Value.Replace('"', '""') + '"'
}
$installRoot = Join-Path $env:LOCALAPPDATA "CovenantSetupSelfTest"
$shortcutPath = Join-Path ([Environment]::GetFolderPath("Desktop")) "Covenant Setup Self Test.lnk"
$registryPath = "HKCU:\Software\CovenantSetupSelfTest"
$journalPath = Join-Path $installRoot "journal.json"
$installedExe = Join-Path $installRoot "bin\covenant-setup.exe"
$uninstallExe = Join-Path $installRoot "covenant-setup-uninstall.exe"
$taskName = "{0}-{1}" -f $TaskNamePrefix, ([DateTimeOffset]::UtcNow.ToUnixTimeSeconds())
$interactiveScript = Join-Path $ScriptRoot "Invoke-InteractiveInstaller.ps1"
try {
if (-not (Test-Path -LiteralPath $InstallerPath)) {
throw "Installer not found in guest: $InstallerPath"
}
if (-not (Test-Path -LiteralPath $interactiveScript)) {
throw "Interactive installer script not found in guest: $interactiveScript"
}
$resultDir = Split-Path -Parent $ResultPath
if ($resultDir) {
New-Item -ItemType Directory -Force -Path $resultDir | Out-Null
}
Remove-Item -LiteralPath $ResultPath -Force -ErrorAction SilentlyContinue
Remove-Item -LiteralPath $installRoot -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -LiteralPath $shortcutPath -Force -ErrorAction SilentlyContinue
Remove-Item -LiteralPath $registryPath -Recurse -Force -ErrorAction SilentlyContinue
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1)
$actionArguments = @(
"-NoProfile",
"-ExecutionPolicy", "Bypass",
"-File", (Quote-TaskArgument -Value $interactiveScript),
"-InstallerPath", (Quote-TaskArgument -Value $InstallerPath),
"-ResultPath", (Quote-TaskArgument -Value $ResultPath),
"-TimeoutSeconds", $TimeoutSeconds
) -join " "
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $actionArguments
$principal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType Interactive -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-Principal $principal `
-Force | Out-Null
Start-ScheduledTask -TaskName $taskName
$deadline = (Get-Date).AddSeconds($TimeoutSeconds + 120)
while ((Get-Date) -lt $deadline) {
if (Test-Path -LiteralPath $ResultPath) {
break
}
Start-Sleep -Seconds 2
}
if (-not (Test-Path -LiteralPath $ResultPath)) {
$taskInfo = Get-ScheduledTaskInfo -TaskName $taskName
throw "Timed out waiting for interactive installer task. LastTaskResult=$($taskInfo.LastTaskResult)"
}
$runResult = Get-Content -LiteralPath $ResultPath -Raw | ConvertFrom-Json
if (-not $runResult.success) {
throw "Interactive installer task failed: $($runResult.error)"
}
if (-not (Test-Path -LiteralPath $installedExe)) {
throw "Installed executable missing: $installedExe"
}
if (-not (Test-Path -LiteralPath $journalPath)) {
throw "Journal missing: $journalPath"
}
if (-not (Test-Path -LiteralPath $uninstallExe)) {
throw "Installed uninstaller missing: $uninstallExe"
}
if (-not (Test-Path -LiteralPath $shortcutPath)) {
throw "Desktop shortcut missing: $shortcutPath"
}
if (-not (Test-Path -LiteralPath $registryPath)) {
throw "Registry key missing: $registryPath"
}
$installRootValue = Get-ItemPropertyValue -Path $registryPath -Name "InstallRoot"
if ($installRootValue -ne $installRoot) {
throw "Unexpected registry InstallRoot value: $installRootValue"
}
$journal = Get-Content -LiteralPath $journalPath -Raw | ConvertFrom-Json
$journalActionTypes = @($journal.actions | ForEach-Object { $_.type })
if ($journal.app_name -ne "Covenant Setup Self Test") {
throw "Unexpected journal app name: $($journal.app_name)"
}
if ($journalActionTypes -notcontains "copy_file") {
throw "Journal did not record the packaged payload copy."
}
$verification = [ordered]@{
success = $true
exitCode = [int]$runResult.exitCode
installerPath = $InstallerPath
installRoot = $installRoot
installedExe = $installedExe
journalPath = $journalPath
uninstallExe = $uninstallExe
shortcutPath = $shortcutPath
registryPath = $registryPath
journalActionTypes = $journalActionTypes
taskName = $taskName
startedAt = $runResult.startedAt
finishedAt = $runResult.finishedAt
}
$verification | ConvertTo-Json | Set-Content -LiteralPath $ResultPath -Encoding UTF8
}
catch {
$failure = [ordered]@{
success = $false
error = $_.Exception.Message
taskName = $taskName
installRoot = $installRoot
resultPath = $ResultPath
}
$failure | ConvertTo-Json | Set-Content -LiteralPath $ResultPath -Encoding UTF8
exit 1
}
finally {
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
}