From 4b0c1031dba37cd6d3dba402ab91d20b720e48ab Mon Sep 17 00:00:00 2001 From: konsisumer Date: Sat, 15 Aug 2026 02:19:07 +0200 Subject: [PATCH] fix(desktop-update): wait for rebuilt executable before relaunch --- scripts/desktop-update/windows.ps1 | 14 +++++++++++++- ...st_desktop_update_windows_python_handoff.py | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/scripts/desktop-update/windows.ps1 b/scripts/desktop-update/windows.ps1 index 95c69881dc..247ca6cf5b 100644 --- a/scripts/desktop-update/windows.ps1 +++ b/scripts/desktop-update/windows.ps1 @@ -447,7 +447,19 @@ function Start-DesktopRelaunch { # the pid exists, or the fallback spawn returned a live process). The # finally block downgrades the on-screen/on-disk outcome when it didn't # — the sibling truth contract to posix.sh's launch acceptance. - if (-not ($RelaunchExe -and (Test-Path -LiteralPath $RelaunchExe))) { return $false } + if (-not $RelaunchExe) { return $false } + # electron-builder replaces win-unpacked in place. After a successful + # update it can remove the old Hermes.exe before writing the replacement, + # so a one-shot existence check races the rebuild and strands the user. + $relaunchDeadline = (Get-Date).AddSeconds(120) + while (-not (Test-Path -LiteralPath $RelaunchExe)) { + if ((Get-Date) -ge $relaunchDeadline) { + Write-HandoffLog "WARNING: desktop relaunch executable did not reappear within 120s: $RelaunchExe" + return $false + } + Start-Sleep -Milliseconds 500 + if ($script:Ui) { [System.Windows.Forms.Application]::DoEvents() } + } Write-HandoffLog "relaunching desktop: $RelaunchExe" # DO NOT spawn Hermes.exe as our child: Electron/Chromium calls # AttachConsole(ATTACH_PARENT_PROCESS) at boot, so a Desktop launched diff --git a/tests/test_desktop_update_windows_python_handoff.py b/tests/test_desktop_update_windows_python_handoff.py index cb5cf339d8..f4be96e864 100644 --- a/tests/test_desktop_update_windows_python_handoff.py +++ b/tests/test_desktop_update_windows_python_handoff.py @@ -87,3 +87,21 @@ def test_update_no_longer_invokes_the_hermes_exe_shim() -> None: "exact self-lock this fix removes -- route it through $pythonExe " "instead." ) + + +def test_desktop_relaunch_waits_for_an_in_place_rebuild() -> None: + source = _read() + relaunch = re.search( + r"function Start-DesktopRelaunch \{(?P.*?)\n\}\n\nfunction Invoke-HermesStep", + source, + re.DOTALL, + ) + assert relaunch, "Expected Start-DesktopRelaunch in the Windows hand-off script." + + body = relaunch.group("body") + assert "if (-not $RelaunchExe) { return $false }" in body + assert "$relaunchDeadline = (Get-Date).AddSeconds(120)" in body + assert "while (-not (Test-Path -LiteralPath $RelaunchExe))" in body + assert "if ((Get-Date) -ge $relaunchDeadline)" in body + assert "Start-Sleep -Milliseconds 500" in body + assert "[System.Windows.Forms.Application]::DoEvents()" in body