148 lines
5.6 KiB
PowerShell
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
|
|
}
|