fix(windows-installer): build pf-vdisplay from source in CI; ASCII scripts; upgrade-safe web console
windows-drivers / probe-and-proto (push) Successful in 24s
apple / swift (push) Successful in 1m4s
windows-drivers / driver-build (push) Successful in 1m8s
android / android (push) Successful in 4m4s
ci / rust (push) Successful in 4m39s
ci / web (push) Successful in 50s
ci / docs-site (push) Successful in 53s
apple / screenshots (push) Successful in 5m10s
windows-host / package (push) Failing after 5m35s
deb / build-publish (push) Successful in 2m29s
decky / build-publish (push) Successful in 13s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3s
ci / bench (push) Successful in 4m42s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m57s
docker / deploy-docs (push) Successful in 17s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m46s

The pf-vdisplay virtual-display driver shipped as a checked-in PREBUILT binary
that went stale - two field failures on a fresh install (live-repro'd on a
German-locale Dell laptop):

  * Bug A (every box): a repo-wide rename edited the vendored pf_vdisplay.inf
    but never re-signed pf_vdisplay.cat, so the catalog stopped covering the INF
    -> `pnputil /add-driver` fails SPAPI_E_FILE_HASH_NOT_IN_CATALOG -> driver
    never installs -> every session dies "pf-vdisplay driver interface not
    found".
  * the prebuilt binary also predated IOCTL_SET_RENDER_ADAPTER (added to the
    driver source after the vendor freeze) that the host needs to pin the IDD
    render GPU on hybrid/Optimus boxes.

Fix: build the driver FROM SOURCE every release (build-pf-vdisplay.ps1, wired
into pack-host-installer.ps1) so .dll/.inf/.cat are always in lockstep and
current driver features ship. The runner's clang 22 made the driver's pinned
bindgen 0.71 emit opaque structs (157 layout-assert errors), so bump the
vendored wdk-sys/wdk-build bindgen 0.71 -> 0.72 (+ lock). The build self-signs
the driver per build (installer trusts the bundled .cer); a stable
DRIVER_CERT_PFX_B64 secret can override.

  * Bug B (non-English boxes): the installer runs install-pf-vdisplay.ps1 etc.
    via powershell.exe (5.1), which reads a BOM-less script in the ANSI codepage
    - an em-dash's trailing 0x94 byte becomes a curly quote on German
    Windows-1252 and the script aborts "unterminated string", so the driver
    never installed (the gamepad script survived only because it was already
    ASCII). Scrub every installer-run .ps1/.cmd to ASCII + add a CI gate that
    fails on any non-ASCII so it can't regress.

  * Bug C (upgrades): nothing stopped the OLD web console before re-registering
    its task, so a stale server kept :3000 (the new one restart-looped on
    EADDRINUSE) and served a broken old bundle (500 on /login). Stop + reap it
    (runtime-agnostic, by the :3000 listener owner) in web-setup.ps1 and in the
    .iss before the file copy + on uninstall.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-26 14:33:34 +00:00
parent 8e87e617df
commit bdfab8e0d5
12 changed files with 257 additions and 61 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
@echo off
rem punktfunk web console launcher the action the PunktfunkWeb scheduled task runs at boot.
rem punktfunk web console launcher - the action the PunktfunkWeb scheduled task runs at boot.
rem
rem Lays out next to the installed payload: {app}\web\web-run.cmd, {app}\web\.output\... and
rem {app}\bun\bun.exe (so %~dp0 = {app}\web\). Auto-wires the console the same way the Linux
+18 -1
View File
@@ -14,7 +14,7 @@
3. Opens inbound TCP 3000 (the console port) on all profiles.
4. Waits briefly for the host's mgmt token, then starts the task.
The mgmt bearer token is NOT managed here the host owns %ProgramData%\punktfunk\mgmt-token
The mgmt bearer token is NOT managed here - the host owns %ProgramData%\punktfunk\mgmt-token
(crates/punktfunk-host/src/mgmt_token.rs writes it on `serve`); web-run.cmd sources it.
#>
[CmdletBinding()]
@@ -38,6 +38,22 @@ function New-RandomPassword {
return $s.Substring(0, [Math]::Min(20, $s.Length))
}
function Stop-WebConsole {
# On an upgrade a console is already running. Stop + reap it before re-registering so (a) the new
# task can bind :3000 (else the old server keeps it and the new one restart-loops on EADDRINUSE) and
# (b) the installer can overwrite .output / web-run.cmd / bun.exe (a held file blocks the copy). A
# prior install may have run a DIFFERENT runtime (node vs bun), so kill by the script it serves AND
# by the :3000 owner - the latter is runtime-agnostic and future-proofs the next runtime swap.
Stop-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
Get-CimInstance Win32_Process -Filter "Name='bun.exe' OR Name='node.exe'" -ErrorAction SilentlyContinue |
Where-Object { $_.CommandLine -match 'index\.mjs' } |
ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
Get-NetTCPConnection -LocalPort 3000 -State Listen -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty OwningProcess -Unique |
ForEach-Object { Stop-Process -Id $_ -Force -ErrorAction SilentlyContinue }
Start-Sleep -Seconds 1
}
# --- 1. login password -----------------------------------------------------------------------
$password = $null
if ($PasswordFile -and (Test-Path -LiteralPath $PasswordFile)) {
@@ -60,6 +76,7 @@ if ($password) {
}
# --- 2. PunktfunkWeb scheduled task ----------------------------------------------------------
Stop-WebConsole # reap any running (possibly old-runtime) console before re-registering (upgrade-safe)
$cmd = Join-Path $AppDir 'web\web-run.cmd'
if (-not (Test-Path -LiteralPath $cmd)) { throw "web launcher missing: $cmd" }
$action = New-ScheduledTaskAction -Execute $cmd