fix(desktop): preserve Windows process exit evidence

Signed-off-by: Haozhe Wu <haozhe_wu@connect.hku.hk>
This commit is contained in:
Haozhe Wu committed 2026-08-15 04:52:36 +08:00
1 parent bc846ebaef
commit 8a0e90b840
5 files changed
+108 -32

No files matched your search

+5
View File
@@ -66,6 +66,11 @@ jobs:
working-directory: desktop/electron
run: npm run smoke:credentials
- name: Verify bounded native process adapter
working-directory: desktop/electron
shell: powershell
run: .\scripts\test-process-utils.ps1
- name: Assemble minimal Python runtime
working-directory: desktop/electron
shell: powershell
+1
View File
@@ -38,6 +38,7 @@ cd ..\desktop\electron
npm ci
npm run prepare:electron
npm run smoke:credentials
powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/test-process-utils.ps1
npm run runtime:win -- -Clean
npm run smoke:lifecycle
npm run installer:win:review
+2 -32
View File
@@ -25,6 +25,8 @@ $gtkInstallerUrl = "https://github.com/tschoonj/GTK-for-Windows-Runtime-Environm
$gtkInstallerSha256 = 'd05e1488ca0e6ffaabb579bbeb82113c099152ca4260ebc63084b0dd174d4558'
$gtkExtractRoot = Join-Path $gtkCacheRoot 'archive-runtime-v1'
. (Join-Path $PSScriptRoot 'process-utils.ps1')
function Get-VerifiedDownload {
param(
[Parameter(Mandatory)][string]$Uri,
@@ -92,38 +94,6 @@ function Resolve-SevenZip {
throw '7z.exe is required to extract the checksum-pinned GTK archive without executing its legacy installer.'
}
function Invoke-BoundedProcess {
param(
[Parameter(Mandatory)][string]$FilePath,
[Parameter(Mandatory)][string]$Arguments,
[Parameter(Mandatory)][int]$TimeoutSeconds,
[Parameter(Mandatory)][string]$Label
)
$process = $null
try {
$process = Start-Process `
-FilePath $FilePath `
-ArgumentList $Arguments `
-NoNewWindow `
-PassThru
if (-not $process.WaitForExit($TimeoutSeconds * 1000)) {
$taskkill = Join-Path $env:SystemRoot 'System32\taskkill.exe'
& $taskkill /PID $process.Id /T /F | Out-Null
$process.WaitForExit(10000) | Out-Null
throw "$Label timed out after $TimeoutSeconds seconds."
}
if ($process.ExitCode -ne 0) {
throw "$Label failed (exit code $($process.ExitCode))."
}
}
finally {
if ($process) {
$process.Dispose()
}
}
}
if (-not $BuildPython) {
$repoVenvPython = Join-Path $repoRoot '.venv\Scripts\python.exe'
if (Test-Path -LiteralPath $repoVenvPython) {
@@ -0,0 +1,44 @@
function Invoke-BoundedProcess {
[CmdletBinding()]
param(
[Parameter(Mandatory)][string]$FilePath,
[Parameter(Mandatory)][string]$Arguments,
[Parameter(Mandatory)][ValidateRange(1, 2147483)][int]$TimeoutSeconds,
[Parameter(Mandatory)][string]$Label
)
# Windows PowerShell 5.1 can return a Process object with a null ExitCode
# for Start-Process -PassThru -NoNewWindow (PowerShell/PowerShell#20400).
# Use the .NET process API directly so timeout and exit-code evidence both
# come from the same live process handle on every supported Windows image.
$startInfo = [Diagnostics.ProcessStartInfo]::new()
$startInfo.FileName = $FilePath
$startInfo.Arguments = $Arguments
$startInfo.UseShellExecute = $false
$startInfo.CreateNoWindow = $true
$process = [Diagnostics.Process]::new()
$process.StartInfo = $startInfo
try {
if (-not $process.Start()) {
throw "$Label failed to start."
}
if (-not $process.WaitForExit($TimeoutSeconds * 1000)) {
$taskkill = Join-Path $env:SystemRoot 'System32\taskkill.exe'
& $taskkill /PID $process.Id /T /F | Out-Null
$process.WaitForExit(10000) | Out-Null
throw "$Label timed out after $TimeoutSeconds seconds."
}
# The parameterless wait flushes final process state before ExitCode is
# read. It returns immediately because the bounded wait already ended.
$process.WaitForExit()
$exitCode = [int]$process.ExitCode
if ($exitCode -ne 0) {
throw "$Label failed (exit code $exitCode)."
}
}
finally {
$process.Dispose()
}
}
@@ -0,0 +1,56 @@
$ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'process-utils.ps1')
$commandPrompt = Join-Path $env:SystemRoot 'System32\cmd.exe'
Invoke-BoundedProcess `
-FilePath $commandPrompt `
-Arguments '/d /s /c "exit 0"' `
-TimeoutSeconds 5 `
-Label 'zero-exit probe'
$nonZeroObserved = $false
try {
Invoke-BoundedProcess `
-FilePath $commandPrompt `
-Arguments '/d /s /c "exit 7"' `
-TimeoutSeconds 5 `
-Label 'non-zero probe'
}
catch {
if ($_.Exception.Message -ne 'non-zero probe failed (exit code 7).') {
throw
}
$nonZeroObserved = $true
}
if (-not $nonZeroObserved) {
throw 'The bounded process adapter accepted a non-zero exit code.'
}
$timeoutObserved = $false
$stopwatch = [Diagnostics.Stopwatch]::StartNew()
try {
Invoke-BoundedProcess `
-FilePath $commandPrompt `
-Arguments '/d /s /c "ping.exe -n 6 127.0.0.1 >NUL"' `
-TimeoutSeconds 1 `
-Label 'timeout probe'
}
catch {
if ($_.Exception.Message -ne 'timeout probe timed out after 1 seconds.') {
throw
}
$timeoutObserved = $true
}
finally {
$stopwatch.Stop()
}
if (-not $timeoutObserved) {
throw 'The bounded process adapter accepted a process that exceeded its timeout.'
}
if ($stopwatch.Elapsed.TotalSeconds -ge 12) {
throw "The timeout probe took too long to terminate: $($stopwatch.Elapsed.TotalSeconds) seconds."
}
Write-Host 'Bounded native process exit-code and timeout handling: OK'