fix: explicitly exit process on successful command completion#8032
fix: explicitly exit process on successful command completion#8032tobq wants to merge 1 commit intonetlify:mainfrom
Conversation
On success, onEnd() relied on the Node.js event loop draining naturally to exit the process. This fails when stdio pipes are closed by the parent (e.g. CI runners, editor integrations, or tools that spawn the CLI with piped output and then close the connection). On Windows in particular, the orphaned process never exits and continuously leaks OS handles, eventually exhausting the kernel nonpaged pool and freezing the system. The fix: - Add exit() call in onEnd() for the success path (error path already had exit(1)) - Await onEnd() in bin/run.js so telemetry completes before exit Fixes netlify#8031
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe pull request modifies the process lifecycle handling to ensure proper termination and resource cleanup. In Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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 Tip CodeRabbit can enforce grammar and style rules using `languagetool`.Configure the |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/commands/base-command.ts (1)
436-441:⚠️ Potential issue | 🟡 MinorUse
elsebranch to prevent exit code fallthrough whenprocess.exitis mocked.The
onEndmethod at line 414 can fall through to the unconditionalexit()at line 441 whenprocess.exitis mocked to return (as in tests). When an error occurs andexit(1)is called at line 438, a mocked exit returns control flow, allowing line 441 to executeexit()with code0, masking the error status.🔧 Proposed fix
if (error_ !== undefined) { logError(error_ instanceof Error ? error_ : format(error_)) exit(1) } - - exit() + else { + exit() + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/base-command.ts` around lines 436 - 441, The onEnd method currently calls exit(1) when error_ is set but then unconditionally calls exit() afterwards, which can mask failure if process.exit is mocked; update the control flow in onEnd (referencing error_, logError(...) and exit(...)) so that the unconditional exit() is only called in the else branch when error_ is undefined—i.e., after logging the error and calling exit(1) do not fall through to the success exit path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/commands/base-command.ts`:
- Around line 436-441: The onEnd method currently calls exit(1) when error_ is
set but then unconditionally calls exit() afterwards, which can mask failure if
process.exit is mocked; update the control flow in onEnd (referencing error_,
logError(...) and exit(...)) so that the unconditional exit() is only called in
the else branch when error_ is undefined—i.e., after logging the error and
calling exit(1) do not fall through to the success exit path.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4af0590c-9fcd-4fc7-838c-a5728cf05d00
📒 Files selected for processing (2)
bin/run.jssrc/commands/base-command.ts
Summary
Fixes #8031
On success,
onEnd()relied on the Node.js event loop draining naturally to exit the process. This fails when stdio pipes are closed by the parent process (e.g. CI runners, editor integrations, or tools that spawn the CLI with piped stdio and then close the connection).On Windows specifically, the orphaned process never exits and continuously leaks OS handles, eventually accumulating 400,000+ handles per process. This exhausts the Windows kernel nonpaged pool (~3.4 GB vs normal ~500 MB), causing full system freezes including mouse/keyboard input.
Root cause
onEnd()callsexit(1)on error but does nothing on success — it just returns, expecting the event loop to drain. However:keepAlive: true, keeping sockets in the poolChanges
src/commands/base-command.ts: Addexit()call at the end ofonEnd()for the success path (the error path already hadexit(1))bin/run.js:awaittheonEnd()call in both success and error paths so telemetry tracking completes before exitReproduction
The issue can be reproduced by spawning the CLI with piped stdio, then closing the pipes:
Before fix: Process stays alive indefinitely after pipe closure, accumulating handles
After fix: Process exits cleanly via
process.exit(0)before pipes are even abandoned