From 47eb8c9f6f04508c6f73f5d0a0a47cf759842479 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Fri, 31 Jul 2026 22:40:26 +0200 Subject: [PATCH] fix(windows): a web-console deploy that half-succeeded stops reporting success MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found by investigating a console that had been serving errors on .173 for hours while every health check said HTTP 200. Nitro's `entry.mjs` imports its sibling chunks by CONTENT HASH, so a `.output` that mixes two builds is not degraded — it is dead: bun answers every page with a `ResolveMessage` JSON body ("Cannot find module ../_/router-.mjs") under a 200 status. Two defects here let exactly that ship and then hid it: * The pre-copy `Remove-Item` used `-ErrorAction SilentlyContinue`, so a removal blocked by a still-running bun was swallowed and `Copy-Item` merged the new build into the old tree. (Reproduced live: an older task-based copy of this script, run against the now supervised-child host, tried `schtasks /end` for a task that no longer exists, never stopped the service, and so could never unlock the files.) The removal is now verified and refuses to copy over a tree it could not clear. * The success probe read only the status code, so it reported a healthy console for a server that serves nothing but an error. It now checks that `/login` actually returns HTML, and says so loudly when the body is a module-resolution error instead. Co-Authored-By: Claude Fable 5 --- scripts/windows/build-web.ps1 | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/scripts/windows/build-web.ps1 b/scripts/windows/build-web.ps1 index 5c460afc..870dff0c 100644 --- a/scripts/windows/build-web.ps1 +++ b/scripts/windows/build-web.ps1 @@ -31,9 +31,20 @@ if (-not (Test-Path $appWeb)) { } Write-Host "swapping $appWeb\.output (stopping the PunktfunkHost service) ..." +$dst = Join-Path $appWeb '.output' & net stop PunktfunkHost | Out-Null try { - Remove-Item (Join-Path $appWeb '.output') -Recurse -Force -ErrorAction SilentlyContinue + # The removal MUST succeed before the copy. A merge of two builds is not a degraded + # install, it is a dead one: Nitro's entry.mjs imports its siblings by content hash, so a + # stale chunks/_ next to a new entry.mjs makes every page 200 with a bun ResolveMessage + # body instead of the app. Observed on .173 2026-07-31 (an older task-based copy of this + # script could not unlock the files under the supervised-child host, its + # -ErrorAction SilentlyContinue swallowed that, and the console served a JSON error for + # hours while the probe below reported success). + Remove-Item $dst -Recurse -Force -ErrorAction SilentlyContinue + if (Test-Path $dst) { + throw "could not remove $dst (files still locked - is another bun/host still running?). Refusing to copy over it: a mixed .output serves errors, not the console." + } Copy-Item (Join-Path $web '.output') -Destination $appWeb -Recurse -Force } finally { @@ -43,14 +54,20 @@ finally { # The console serves HTTPS-only (PUNKTFUNK_UI_SECURE=1, the host's own cert) - probe with curl.exe # (-k for the self-signed cert; Invoke-WebRequest under Windows PowerShell 5.1, which this script # runs under, has no -SkipCertificateCheck), retrying while the service/bun cold-starts. -$code = $null +# +# The BODY is the check, not the status code: a bun that started but cannot resolve its own +# chunks answers 200 with a ResolveMessage JSON, so a code-only probe reports a healthy +# console that serves nothing but an error (exactly how the .173 breakage stayed invisible). +$body = $null for ($i = 0; $i -lt 15; $i++) { Start-Sleep 2 - $code = & curl.exe -sk -o NUL -w '%{http_code}' --max-time 5 'https://127.0.0.1:47992/login' 2>$null - if ($code -eq '200') { break } + $body = & curl.exe -sk --max-time 5 'https://127.0.0.1:47992/login' 2>$null + if ($body -match ' HTTP $code" +if ($body -match '