From 1bd6e89ff58fad3defe9ee16609551a48edd5987 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 9 Sep 2026 09:24:51 +0200 Subject: [PATCH] Kill only this run's browser on the remote host Both shutdown paths ran Get-Process chrome,msedge | Stop-Process -Force, which is every browser on the machine. The remote host is somebody's desktop: a test run must not close the windows they are working in. It only stayed harmless because the test account cannot touch another user's processes. The launcher now writes its browser pid to %TEMP%\gm-browser-.pid and kills that pid; the job object still takes the renderer children. Measured against the Windows desktop: five msedge processes belonging to the logged-in user before the run, five after, and our chrome gone. 24 smoke checks ALL PASSED. --- tools/lib/browser.mjs | 6 ++++-- tools/remote/gm-browser.ps1 | 12 +++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/tools/lib/browser.mjs b/tools/lib/browser.mjs index 17b06ec6..db908676 100644 --- a/tools/lib/browser.mjs +++ b/tools/lib/browser.mjs @@ -103,16 +103,18 @@ export function launchBrowser({ port, width, height }) { /** * The launcher notices a finished run on its own, but only after its idle window – a browser * lingering a minute on someone else's desktop is rude, so end it now and let that window be - * the net for runs that die without reaching here. + * the net for runs that die without reaching here. Only this run's pid: the host is somebody's + * desktop, and killing by name would close the browser they are reading this in. */ stop: () => { + const pidFile = `$env:TEMP\\gm-browser-${port}.pid`; try { execFileSync( 'ssh', [ ...sshArgs(key), remote, - 'powershell -NoProfile -Command "Get-Process chrome,msedge -ErrorAction SilentlyContinue | Stop-Process -Force"', + `powershell -NoProfile -Command "if (Test-Path ${pidFile}) { Stop-Process -Id (Get-Content ${pidFile}) -Force -ErrorAction SilentlyContinue }"`, ], { stdio: 'pipe', timeout: 20_000 }, ); diff --git a/tools/remote/gm-browser.ps1 b/tools/remote/gm-browser.ps1 index e2a05851..7c30d46b 100644 --- a/tools/remote/gm-browser.ps1 +++ b/tools/remote/gm-browser.ps1 @@ -73,6 +73,10 @@ $flags = @( $job = [GmJob]::Create() $proc = Start-Process -FilePath $exe -ArgumentList $flags -PassThru -NoNewWindow [GmJob]::Assign($job, $proc.Handle) +# The pid file is how the caller ends this run's browser without touching the browser windows the +# person at this desk has open. +$pidFile = Join-Path $env:TEMP "gm-browser-$Port.pid" +Set-Content -Path $pidFile -Value $proc.Id [Console]::Error.WriteLine("gm-browser: $exe pid=$($proc.Id) port=$Port job=on") # Either the browser dies on its own, or nobody is driving it any more. Both must end this script. @@ -107,8 +111,10 @@ while (-not $proc.HasExited) { if ($proc.HasExited) { [Console]::Error.WriteLine("gm-browser: $reason $($proc.ExitCode)") } else { - # taskkill and tasklist filters are denied to this account; Stop-Process is not. The job object - # would do it anyway when this process ends - this just makes the kill immediate. - Get-Process chrome, msedge -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue + # By pid, never by name: this host is somebody's desktop and their own Chrome must survive a test + # run. taskkill is denied to this account, Stop-Process is not, and the job object takes the + # renderer children when this process ends - this just makes the kill immediate. + Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue [Console]::Error.WriteLine("gm-browser: $reason, killed the browser") } +Remove-Item $pidFile -Force -ErrorAction SilentlyContinue