Close the dashboard debug browser when the Aspire session ends - #19296
Adam Ratzman (adamint) merged 4 commits into
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 37d5f71d-3c66-4bae-909d-5f710316b2f2
Review of the dispose fix surfaced two lifecycle holes that the new call site turns into user-visible behavior. The started-session predicate matched only on configuration name and browser type. Concurrent Aspire debug sessions all launch their dashboard with the same name and the same user-configured browser type, so one session could adopt another session's browser, later close it, and leave its own browser orphaned. Match on the parent session id instead. The Aspire session can also be disposed while the browser is still launching, for example when the AppHost exits right after reporting the dashboard URL. The late-arriving session was stored on an already-disposed instance and never stopped, and a failed launch fell back to an external browser after teardown. Both paths now re-check disposal, routing through closeDashboard so the opt-out setting is still honored. Also corrects two doc comments that still claimed VS Code auto-closes the dashboard child session, which is the assumption this fix disproves. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 37d5f71d-3c66-4bae-909d-5f710316b2f2
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 19296Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 19296" |
There was a problem hiding this comment.
Pull request overview
Ensures dashboard debug browsers are cleaned up when Aspire sessions end.
Changes:
- Stops tracked dashboard browser sessions during disposal.
- Associates browser sessions with the correct parent and handles launch/disposal races.
- Adds five focused lifecycle unit tests.
Show a summary per file
| File | Description |
|---|---|
extension/src/debugger/AspireDebugSession.ts |
Implements dashboard browser lifecycle cleanup. |
extension/src/test/aspireDebugSession.test.ts |
Adds regression tests for cleanup, opt-out, ownership, and races. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Balanced
The unit tests cover the extension's own bookkeeping, but the regression in microsoft#19289 lived below that: the extension's debugSessions list was already empty while the launched browser kept running. An E2E test needs to observe VS Code's browser debug sessions directly, which the extension state snapshot does not expose. Track browser debug sessions (pwa-chrome, pwa-msedge, firefox) in the test-only E2E state file bridge, then add a debug-dashboard E2E test that configures the debug browser, starts the AppHost, asserts the dashboard browser session is parented to the Aspire session, stops debugging, and requires the browser session to terminate. Verified against a real Extension Development Host: with the fix the tracked browser sessions are empty after stopping, and with the fix reverted both the root browser session and its page child are still present, so the new assertion reproduces the reported bug. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 37d5f71d-3c66-4bae-909d-5f710316b2f2
Ella Hathaway (ellahathaway)
left a comment
There was a problem hiding this comment.
The explicit dashboard-session cleanup is correctly scoped to its owning Aspire session and handles disposal during browser launch. The focused unit coverage plus the new VS Code E2E regression test cover the reported lifecycle failure and the relevant races.
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
The Linux E2E runner cannot launch Chrome under xvfb. js-debug activates on `onDebugResolve:pwa-chrome` and then never resolves `startDebugging`, so the `openDashboard` await never returns, the AppHost startup handshake never completes, and the stall cascades into the rest of the file. The Windows runner launches the real browser and the test passed there, so the shutdown behavior still has end-to-end coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 37d5f71d-3c66-4bae-909d-5f710316b2f2
|
Follow-up on the E2E test, since the first CI run answered a question I could only guess at locally. Windows passed the new test. Linux could not run it, so it is now gated to On the Linux xvfb runner, js-debug activates on
Two things worth separating out of that:
|
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
bb1d405
into
microsoft:main
Description
When you stop an Aspire debug session in VS Code, the dashboard browser window stays open. You end up collecting orphaned Edge/Chrome windows pointing at dashboards for AppHosts that are long gone.
AspireDebugSessionalready had acloseDashboard()that implements theaspire.closeDashboardOnDebugEndsetting (defaulttrue), but nothing ever called it — it was dead code. VS Code does not stop a child browser debug session when its parent session terminates, so the browser just outlived the session. This calls it fromdispose().Reviewing that one-line call surfaced two more holes in the same path that the call site turns into user-visible behavior:
openExternalafter teardown, popping a window open during shutdown. Both paths re-check disposal now, and the first routes back throughcloseDashboard()so the opt-out setting is still honored.Also fixes two doc comments that still claimed VS Code auto-closes the dashboard child session, since that assumption is what caused this.
Note this is a different path from #18626, which handles the DCP
WithBrowserDebugger()browser lifecycle. This one is the dashboard browser launched byopenDashboard.Fixes #19289
Verification
Five unit tests, all written first and observed failing (
extension/src/test/aspireDebugSession.test.ts).Beyond that, I ran an A/B against a real VS Code Extension Development Host, a real Aspire AppHost, and the real Edge instance js-debug launches. Playwright CLI confirms the dashboard actually rendered before the stop; the Edge pid is then polled for 30s after the stop:
closeDashboardOnDebugEndtruetruefalseThe only difference between rows 1 and 2 is the
closeDashboard()call. Worth notingdebugSessionsis empty in all three runs — the extension always believed the session had ended, which is why this was invisible from extension state and only observable at the process level.There is also a new E2E test,
closes the dashboard debug browser when the AppHost debug session stopsinextension/src/test-e2e/debugDashboard.e2e.test.ts. It configuresaspire.dashboardBrowser: debugChrome, starts the AppHost, asserts the dashboard browser session is parented to the Aspire session, stops debugging, and requires the browser session to terminate.That test needed a new observable. The extension's own
debugSessionswas empty in every repro run, so asserting on extension state cannot catch this regression; the divergence only exists in VS Code's debug sessions, which the state snapshot does not expose. The test-only E2E state file bridge now trackspwa-chrome/pwa-msedge/firefoxsessions asbrowserDebugSessions. Re-running the live A/B against that field: empty after stop with the fix, and with the fix reverted both the rootAspire Dashboardsession and its page child are still present, so the new assertion fails without the fix.One aside for anyone trying to reproduce this with Playwright directly: js-debug launches Edge with
--remote-debugging-pipe, not--remote-debugging-port, so there's no HTTP CDP endpoint and noDevToolsActivePortfile to attach to. You have to track the process instead.Checklist
<remarks />and<code />elements on your triple slash comments?