mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-09 16:23:27 +08:00
- 根据当前安装标记精确选择 MSI 或 Portable 更新包 - MSI 更新下载到用户缓存并通过 msiexec 保留原目录安装重启 - 下载与安装前校验包类型并隔离不同安装方式的缓存 - 补充安装模式、脚本执行和更新清理测试
152 lines
5.4 KiB
PowerShell
152 lines
5.4 KiB
PowerShell
$ErrorActionPreference = 'Stop'
|
|
|
|
$Source = $env:GONAVI_UPDATE_SOURCE
|
|
$Target = $env:GONAVI_UPDATE_TARGET
|
|
$StagedDir = $env:GONAVI_UPDATE_STAGED_DIR
|
|
$LogPath = $env:GONAVI_UPDATE_LOG_PATH
|
|
$MSILogPath = $env:GONAVI_UPDATE_MSI_LOG_PATH
|
|
$MSIExecPath = $env:GONAVI_UPDATE_MSIEXEC_PATH
|
|
$HostProcessId = 0
|
|
$HostExited = $false
|
|
$InstallSucceeded = $false
|
|
$LaunchSucceeded = $false
|
|
|
|
function Write-UpdateLog {
|
|
param([string]$Message)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($LogPath)) {
|
|
return
|
|
}
|
|
try {
|
|
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss.fff'
|
|
Add-Content -LiteralPath $LogPath -Value "[$timestamp] $Message" -Encoding UTF8
|
|
} catch {
|
|
# Logging must never hide the original updater error.
|
|
}
|
|
}
|
|
function Quote-NativeArgument {
|
|
param([string]$Value)
|
|
|
|
return '"' + $Value.Replace('"', '\"') + '"'
|
|
}
|
|
|
|
function Remove-UpdateArtifact {
|
|
param([string]$Path)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($Path)) {
|
|
return
|
|
}
|
|
try {
|
|
if (Test-Path -LiteralPath $Path) {
|
|
Remove-Item -LiteralPath $Path -Force -Recurse
|
|
}
|
|
} catch {
|
|
Write-UpdateLog ("cleanup failed for " + $Path + ": " + $_.Exception.Message)
|
|
}
|
|
}
|
|
|
|
try {
|
|
foreach ($requiredPath in @($Source, $Target, $StagedDir, $LogPath, $MSILogPath, $MSIExecPath)) {
|
|
if ([string]::IsNullOrWhiteSpace($requiredPath)) {
|
|
throw 'missing required MSI updater path'
|
|
}
|
|
}
|
|
if (-not [int]::TryParse($env:GONAVI_UPDATE_PID, [ref]$HostProcessId) -or $HostProcessId -le 0) {
|
|
throw 'invalid host process id'
|
|
}
|
|
if (-not (Test-Path -LiteralPath $Source -PathType Leaf)) {
|
|
throw 'MSI package not found'
|
|
}
|
|
if (-not [string]::Equals([IO.Path]::GetExtension($Source), '.msi', [StringComparison]::OrdinalIgnoreCase)) {
|
|
throw 'update package is not an MSI file'
|
|
}
|
|
if (-not (Test-Path -LiteralPath $MSIExecPath -PathType Leaf)) {
|
|
throw 'msiexec.exe not found'
|
|
}
|
|
|
|
$TargetDir = [IO.Path]::GetDirectoryName($Target)
|
|
if ([string]::IsNullOrWhiteSpace($TargetDir) -or -not (Test-Path -LiteralPath $TargetDir -PathType Container)) {
|
|
throw 'target directory does not exist'
|
|
}
|
|
|
|
Write-UpdateLog 'MSI updater started'
|
|
$waitedSeconds = 0
|
|
while (Get-Process -Id $HostProcessId -ErrorAction SilentlyContinue) {
|
|
if ($waitedSeconds -ge 90) {
|
|
throw 'host process still running after 90 seconds'
|
|
}
|
|
Start-Sleep -Seconds 1
|
|
$waitedSeconds++
|
|
}
|
|
$HostExited = $true
|
|
Write-UpdateLog 'host process exited'
|
|
Start-Sleep -Seconds 2
|
|
|
|
$MsiArguments = @(
|
|
'/i',
|
|
(Quote-NativeArgument $Source),
|
|
('INSTALLFOLDER=' + (Quote-NativeArgument $TargetDir)),
|
|
'/passive',
|
|
'/norestart',
|
|
'/L*v',
|
|
(Quote-NativeArgument $MSILogPath)
|
|
)
|
|
Write-UpdateLog ("launching msiexec; verbose log: " + $MSILogPath)
|
|
$InstallerProcess = Start-Process -FilePath $MSIExecPath -Verb RunAs -ArgumentList $MsiArguments -Wait -PassThru -ErrorAction Stop
|
|
$InstallerExitCode = $InstallerProcess.ExitCode
|
|
Write-UpdateLog ("msiexec exit code: " + $InstallerExitCode)
|
|
if ($InstallerExitCode -notin @(0, 1641, 3010)) {
|
|
throw ("msiexec failed with exit code " + $InstallerExitCode)
|
|
}
|
|
$InstallSucceeded = $true
|
|
|
|
if (-not (Test-Path -LiteralPath $Target -PathType Leaf)) {
|
|
throw 'installed application executable not found'
|
|
}
|
|
Write-UpdateLog ("launching installed application: " + $Target)
|
|
$NewProcess = Start-Process -FilePath $Target -WorkingDirectory $TargetDir -PassThru -ErrorAction Stop
|
|
Start-Sleep -Milliseconds 1500
|
|
$NewProcess.Refresh()
|
|
if ($NewProcess.HasExited) {
|
|
throw 'updated application exited immediately after launch'
|
|
}
|
|
$LaunchSucceeded = $true
|
|
|
|
Remove-UpdateArtifact $Source
|
|
Write-UpdateLog 'MSI update finished'
|
|
|
|
$CleanupCommand = 'Start-Sleep -Seconds 2; Remove-Item -LiteralPath $env:GONAVI_UPDATE_STAGED_DIR -Recurse -Force -ErrorAction SilentlyContinue'
|
|
$EncodedCleanupCommand = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($CleanupCommand))
|
|
$CleanupWorkingDirectory = [IO.Path]::GetTempPath()
|
|
try {
|
|
Start-Process -FilePath 'powershell.exe' -WorkingDirectory $CleanupWorkingDirectory -WindowStyle Hidden -ArgumentList @(
|
|
'-NoProfile',
|
|
'-NonInteractive',
|
|
'-ExecutionPolicy',
|
|
'Bypass',
|
|
'-EncodedCommand',
|
|
$EncodedCleanupCommand
|
|
) -ErrorAction Stop | Out-Null
|
|
} catch {
|
|
Write-UpdateLog ("cleanup scheduler failed: " + $_.Exception.Message)
|
|
}
|
|
exit 0
|
|
} catch {
|
|
Write-UpdateLog ("MSI updater failed: " + $_.Exception.Message)
|
|
Write-UpdateLog 'MSI package retained for manual install'
|
|
if ($HostExited -and -not $LaunchSucceeded -and (Test-Path -LiteralPath $Target -PathType Leaf)) {
|
|
try {
|
|
$TargetDir = [IO.Path]::GetDirectoryName($Target)
|
|
Start-Process -FilePath $Target -WorkingDirectory $TargetDir -ErrorAction Stop | Out-Null
|
|
if ($InstallSucceeded) {
|
|
Write-UpdateLog 'installed application relaunched after updater failure'
|
|
} else {
|
|
Write-UpdateLog 'previous application relaunched after MSI failure'
|
|
}
|
|
} catch {
|
|
Write-UpdateLog ("application relaunch failed: " + $_.Exception.Message)
|
|
}
|
|
}
|
|
exit 1
|
|
}
|