Repository navigation
Remove the dev server's listeners when its start throws - #663
Merged
Merged
Conversation
startServer subscribes to playground:log, :url and :stopped before it invokes playground:start, and removed them only when the server announced it had stopped. A start that throws has no server and announces nothing, so the three listeners stayed, and the site's next server would open the site once more for each of them. They now come off when the invoke throws, the same way runPullRequest does it. startPhpServer's catch also drops the mail subscription now, as stopDevServer does on every other way out. Fixes #604. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
window.api.startServersubscribes toplayground:log,playground:urlandplayground:stoppedbefore it invokesplayground:start, and removes them only when the server announces it has stopped. A start that throws (rather than answering{ ok: false }) has no server, so nothing ever announces a stop and the three listeners stay for the life of the window. The site's next server would then open the site in the browser once more for each of them, and print its log twice (#604).Today a contributor cannot reach that next server: after a thrown start the button refuses every further click until the app restarts. #603 fixes that dead button, and once it lands this leak becomes visible. This PR is the half that makes the retry clean, and does not depend on #603.
What changes
startServerinsrc/preload.jstakes the shaperunPullRequestin the same file already has: onecleanup, called by the stop handler as before, and now also when the invoke throws, before the error is re-thrown.startPhpServer'scatchinuse-dev-server.jsxalso drops the mail subscription, asstopDevServerdoes on every other way out. That subscription is guarded against stacking (use-site-mail.jsx), so this is tidying rather than a second leak: before, one subscription just stayed open while no server ran.Root cause: the only removal path ran on an event that a start which throws before spawning can never produce. The
{ ok: false }failures (a server that exits before reporting an address, the 120 s timeout) were never affected: each has a child whoseclosesendsplayground:stopped.Deliberately not here: the dead button after a thrown start. That is #603, by another author.
How to test this
Platforms: any. Nothing here touches paths, spawning or line endings.
Covered by the unit suite, which runs on both platforms on every PR:
Run from the repository root.
startServer leaves no listener behind when the start throws, and the next server is heard oncefails on trunk (all three listener counts stay at 1, and the failed start's callbacks receive the next server's log line and address) and passes here. A second test pins the existing removal onplayground:stopped, including that another site's stop does not remove this site's listeners.By hand, only once #603 is also applied, since without it there is no second start to watch. Build from a branch with both, or from source:
Starting state: a built site, server stopped. In
src/main.js, make theplayground:starthandler throw once (the issue's step 1:let once = true;above it, andif (once) { once = false; throw new Error('forced failure'); }as its first line), thennpm startfrom the repository root.Failed to start PHP server: … forced failure, and the button goes back to Start development server.What must not have happened: the site opening in two browser tabs at step 2, or each line of the Server log at step 2 appearing twice. Revert the
main.jsedit afterwards.Risks and limitations
playground:start, which needsreadSiteMeta, the store,readSettingsor the spawn itself to throw. The normal start and stop paths are unchanged, and the existing removal onplayground:stoppedis now pinned by a test..jsxhook, which the suite cannot load (TESTING.md, layer 1).Related
Fixes #604. Independent of #603, and either can merge first. They touch the same
catchinuse-dev-server.jsxon neighbouring lines, so the second to merge may need a trivial rebase.Design decisions and alternatives considered
runPullRequestrather than invent a new shape. It is the bridge in the same file that already solved this, and the issue suggested it.tests/unit/preload-listeners.test.cjsalready loads against a fakeipcRendererfor exactly this class of bug (debug.log lines duplicate after each dev-server restart (renderer listener leak) #86, Preload's per-run IPC listener plumbing is untested #149, Open a pull request from the app, without a push credential on disk #167). That is the highest layer that sees it. A journey would also need Fix: Fixes dev server retry after start failure #603 to reach a second start.stopDevServer()in the hook'scatch. That would also resetdevServerActiveRefand fix the dead button, which is Fix: Fixes dev server retry after start failure #603's change, by its author.Review outcome (required — see AGENTS.md)
0 [fix here] · 1 [follow-up].
use-dev-server.jsxstartPhpServercatch:devServerActiveRefstaystrueafter a thrown start, so the Start button is a silent no-op until restart. It predates this change and is exactly what Fix: Fixes dev server retry after start failure #603 fixes, so it is deferred to Fix: Fixes dev server retry after start failure #603 rather than taken over here. The new test's header comment was reworded after the review to say that the leak's effect is only reachable once a retry is possible.Lint and tests:
npm testpasses 2095, 0 fail. ESLint on the changed files is clean. Repo-widenpm run lintfails locally only inside gitignored.claude/worktreescopies, not in tracked files..github/instructions/code-review.instructions.md, step 3. Covered the uncommitted change on basef5b3b81; outcome as above.77246ea. The only difference from the reviewed diff is the test header comment reworded as described above; no code changed. Trunk has since moved tob17ca54(Skip the unit and E2E suites on pull requests that change only Markdown #660, CI workflow paths only), which does not touch these files.Screenshots or recording
Nothing on screen changes.
🤖 Generated with Claude Code