Skip to content

fix: preserve signal exit codes in CLI child process handling - #6039

Merged
fengmk2 merged 13 commits into
nextfrom
fix/signal-exit-code
Aug 7, 2026
Merged

fix: preserve signal exit codes in CLI child process handling#6039
fengmk2 merged 13 commits into
nextfrom
fix/signal-exit-code

Conversation

@fengmk2

@fengmk2 fengmk2 commented Aug 3, 2026

Copy link
Copy Markdown
Member

A child process killed by a signal reports code=null on its exit event. Three CLIs mishandled that:

  • egg-scripts start in foreground mode treated a signal-killed server as success (if (!code) return), and this.exit() threw an ExitError inside the event callback where nothing catches it, so any child failure exited 1 with a stack trace. The foreground branch now awaits the child inside run() and exits with the child's code (128 + signal number for signal deaths) through oclif's normal exit path, so catch/finally lifecycle still applies.
  • egg-bin forkNode() rejected with "exit with code null" and the CLI always exited 1. ForkError now extends oclif's CLIError carrying the child's real exit code, rejects with "was killed by signal X" for signal deaths, and sets skipOclifErrorHandling on fork failures so the long command line prints verbatim instead of being word-wrapped by oclif's pretty-printer.
  • create-egg's custom command path ran process.exit(status ?? 0), masking a signal death as success. The path is latent (no template defines customCommand yet); it now uses a non-exported toExitCode() helper, same fix as the upstream create-vite pattern needs.

The 128 + signal number mapping is the same verbatim toExitCode() helper in all three packages; a shared @eggjs/utils export was considered and skipped to avoid widening a published API for a 4-line convention (create-egg deliberately has no workspace deps).

Each fix is covered by a reproducing test written first: a mocked-spawn unit test driving the start command's public surface, an egg-bin dev e2e with a fixture framework that SIGTERMs itself (expects "was killed by signal SIGTERM" and exit code 143, skipped on Windows), and unit tests for toExitCode. Full egg-bin dev+test suites pass (41 tests, built first as the test-egg-bin CI job does); egg-scripts stop.test.ts and show help failures on macOS reproduce identically on next without this change.

Behavior note: egg-bin now exits with the forked child's actual exit code instead of flattening every failure to 1.

Summary by CodeRabbit

  • Bug Fixes

    • Improved CLI handling when child processes fail or are terminated by signals.
    • Preserved nonzero exit codes and converted signal termination into standard shell exit codes.
    • Improved error reporting when development servers or generated applications stop unexpectedly.
  • Documentation

    • Updated local CI guidance for running CLI tests.
  • Tests

    • Added coverage for exit codes, signal termination, and process-start failures.
    • Improved test reliability for slower application startup and platform-specific CI environments.

API note: ForkError (exported from @eggjs/bin/baseCommand) now extends oclif's CLIError; its numeric child status moved from code to exitCode (CLIError reserves code as a string display slot) and the constructor takes number instead of number | null.

Copilot AI lite review requested due to automatic review settings August 3, 2026 15:12
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The CLI tools preserve child-process exit codes and convert signal termination to shell-style codes. egg-bin propagates these codes through ForkError. Tests, fixtures, CI workflows, ignore rules, and local build guidance were updated.

Changes

Child-process signal exit codes

Layer / File(s) Summary
create-egg exit-code normalization
tools/create-egg/src/utils.ts, tools/create-egg/src/index.ts, tools/create-egg/test/exit-code.test.ts
toExitCode preserves numeric exits, maps signals to 128 + the signal number, and falls back to 1. Custom command execution and tests use this behavior.
egg-bin fork error propagation
tools/egg-bin/src/baseCommand.ts, tools/egg-bin/test/..., tools/egg-bin/test/fixtures/..., .gitignore
ForkError carries the oclif exit value. Fork signal handling reports the signal and bypasses pretty-printing. Integration coverage verifies that SIGTERM produces exit code 143.
egg-scripts foreground exit handling
tools/scripts/src/commands/start.ts, tools/scripts/test/...
Foreground child termination is awaited and normalized. Tests cover nonzero exits, signal-derived exit codes, and spawn failures.
Build workflow and test guidance
.github/workflows/ci.yml, AGENTS.md, wiki/log.md, wiki/workflows/local-ci.md, tegg/plugin/eventbus/test/eventbus.test.ts, tegg/plugin/langchain/test/llm.test.ts
CI builds egg-bin and egg-scripts in separate steps. Workspace examples use package-name filters. Local build requirements and Windows app-startup timeouts are documented and updated.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant ChildProcess
  participant ExitCodeMapper
  CLI->>ChildProcess: execute command
  ChildProcess-->>CLI: return exit code or signal
  CLI->>ExitCodeMapper: normalize termination
  ExitCodeMapper-->>CLI: return shell exit code
Loading

Possibly related PRs

  • eggjs/egg#5960: Updates related workspace and local CI documentation.

Suggested labels: chore: gitAction

Suggested reviewers: gxkl, copilot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: preserving signal-based exit codes in CLI child-process handling.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/signal-exit-code

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 3, 2026

Copy link
Copy Markdown

Deploying egg with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2d8dc30
Status: ✅  Deploy successful!
Preview URL: https://cff7f651.egg-cci.pages.dev
Branch Preview URL: https://fix-signal-exit-code.egg-cci.pages.dev

View logs

@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 65.71429% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.08%. Comparing base (2b18475) to head (2d8dc30).

Files with missing lines Patch % Lines
tools/egg-bin/src/baseCommand.ts 45.45% 8 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             next    #6039      +/-   ##
==========================================
- Coverage   83.08%   83.08%   -0.01%     
==========================================
  Files         730      731       +1     
  Lines       22474    22496      +22     
  Branches     4520     4530      +10     
==========================================
+ Hits        18673    18690      +17     
- Misses       3297     3299       +2     
- Partials      504      507       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 3, 2026

Copy link
Copy Markdown

Deploying egg-v3 with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2d8dc30
Status: ✅  Deploy successful!
Preview URL: https://68182d44.egg-v3.pages.dev
Branch Preview URL: https://fix-signal-exit-code.egg-v3.pages.dev

View logs

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes CLI child-process exit handling so signal-killed children (which report code=null) are not misclassified as successful exits, and so callers receive conventional shell exit codes (128 + signal number) and clearer error output.

Changes:

  • Update egg-scripts start foreground mode to map (code, signal) to a real exit code and exit via process.exit() to avoid uncaught ExitError stacks.
  • Update egg-bin fork handling to reject with “was killed by signal X”, propagate the child’s exit code, and print ForkError messages without oclif word-wrapping.
  • Add regression tests/fixtures for signal-killed processes across egg-scripts, egg-bin, and create-egg, plus local CI workflow documentation for the egg-bin build-before-test exception.

Reviewed changes

Copilot reviewed 9 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
wiki/workflows/local-ci.md Documents that egg-bin tests require a prebuilt tools/egg-bin/dist and how to handle stale dist cleanup.
wiki/log.md Adds a dated log entry summarizing the signal-exit fixes and the egg-bin local CI exception.
tools/scripts/test/start-unit.test.ts Adds a unit test covering foreground exit-code mirroring and signal mapping (note: currently leaves a process.exit spy mocked).
tools/scripts/src/commands/start.ts Maps (code, signal) to an exit code and uses process.exit() for non-zero child exits in foreground mode.
tools/egg-bin/test/fixtures/demo-app-kill-self/package.json New fixture app that uses a framework which self-terminates via SIGTERM.
tools/egg-bin/test/fixtures/demo-app-kill-self/node_modules/aliyun-egg-kill-self/package.json Fixture framework package metadata for the self-terminating framework.
tools/egg-bin/test/fixtures/demo-app-kill-self/node_modules/aliyun-egg-kill-self/index.js Fixture framework implementation that logs and kills itself with SIGTERM.
tools/egg-bin/test/commands/dev.test.ts Adds an e2e test asserting SIGTERM produces exit code 143 and an appropriate stderr message (skipped on Windows).
tools/egg-bin/src/baseCommand.ts Enhances ForkError to carry an oclif exit code; improves fork exit handling for signal deaths and prints raw error messages in catch().
tools/create-egg/test/spawn-sync-exit-code.test.ts Adds unit tests for signal-aware spawnSync exit-code mapping behavior.
tools/create-egg/src/index.ts Uses a new spawnSyncExitCode() helper so signal-terminated custom commands don’t exit as success.
.gitignore Un-ignores the new demo-app-kill-self fixture’s node_modules for committed test fixtures.

Comment thread tools/scripts/test/start-unit.test.ts Outdated
@fengmk2
fengmk2 requested review from gxkl and killagu August 3, 2026 15:16
@fengmk2 fengmk2 self-assigned this Aug 3, 2026
@socket-security

socket-security Bot commented Aug 3, 2026

Copy link
Copy Markdown

Dependency limit exceeded — report not shown.

This pull request scan exceeded the 10,000-dependency limit applied to this scan, so the results are incomplete and may be inaccurate. To avoid reporting false positives, Socket has not posted a report.

Upgrade your plan to raise the dependency limit and get complete reports, or view the partial scan in the dashboard.

Socket is always free for open source. If this is a non-commercial open source project, contact us to request a free Team account.

Copilot AI review requested due to automatic review settings August 3, 2026 15:40
@fengmk2

fengmk2 commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

@codex review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 13 changed files in this pull request and generated 2 comments.

Comment thread tools/egg-bin/src/baseCommand.ts
Comment thread tools/scripts/test/start-unit.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tools/scripts/src/commands/start.ts`:
- Around line 373-381: Update the child-lifetime Promise around the
`child.once('exit', ...)` listener to also reject when the spawned child emits
`error`, ensuring spawn failures settle instead of hanging and propagate through
oclif’s normal lifecycle handling. Add a regression test covering `start` with a
nonexistent `--node` executable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0ef072d2-fba1-4e69-b81b-51c212d99296

📥 Commits

Reviewing files that changed from the base of the PR and between cbc4349 and 7280c1d.

📒 Files selected for processing (7)
  • tools/create-egg/src/index.ts
  • tools/create-egg/src/utils.ts
  • tools/create-egg/test/exit-code.test.ts
  • tools/egg-bin/src/baseCommand.ts
  • tools/egg-bin/test/commands/dev.test.ts
  • tools/scripts/src/commands/start.ts
  • tools/scripts/test/start-unit.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tools/egg-bin/test/commands/dev.test.ts

Comment thread tools/scripts/src/commands/start.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7280c1d3cf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread wiki/log.md Outdated
Comment thread wiki/workflows/local-ci.md
Comment thread tools/scripts/src/commands/start.ts Outdated
Comment thread tools/egg-bin/src/baseCommand.ts
Copilot AI review requested due to automatic review settings August 3, 2026 15:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 14 changed files in this pull request and generated no new comments.

Suppressed comments (1)

tools/egg-bin/src/baseCommand.ts:440

  • The ForkError message currently reconstructs the command as modulePath + forkArgs.join(' '), which drops process.execPath, execArgv, and the quoting you already computed in fullCommand. That makes the printed command line less accurate and harder to copy/paste when debugging (and it undercuts the goal of printing the long command verbatim). Prefer using fullCommand (or another single canonical string) in the error message for both exit-code and signal cases.
        const command = modulePath + ' ' + forkArgs.join(' ');
        const message =
          code === null ? `${command} was killed by signal ${signal}` : `${command} exit with code ${code}`;
        const err = new ForkError(message, toExitCode(code, signal));

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tools/egg-bin/src/baseCommand.ts (1)

431-445: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Handle fork error events.

forkNode() only subscribes to child.once('exit'), but a spawned child can emit error before or instead of exit. Add a one-shot settlement path for both exit and error, normalize the spawn error to ForkError, and add a regression test that emits error without exit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/egg-bin/src/baseCommand.ts` around lines 431 - 445, Update forkNode()
to settle the promise exactly once from either the child process’s exit or error
event, preventing duplicate resolve/reject handling. Add an error-event handler
that converts the spawn error into a ForkError while preserving the existing
exit-code behavior, and add a regression test covering an error emitted without
a subsequent exit.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tools/scripts/test/start-unit.test.ts`:
- Around line 118-132: Update the foreground spawn failure assertion in
TestStart.run to verify rejection with the exact Error instance passed to the
captured onError callback, rather than matching only its message. Preserve the
existing spawn ENOENT simulation and rejection behavior.

---

Outside diff comments:
In `@tools/egg-bin/src/baseCommand.ts`:
- Around line 431-445: Update forkNode() to settle the promise exactly once from
either the child process’s exit or error event, preventing duplicate
resolve/reject handling. Add an error-event handler that converts the spawn
error into a ForkError while preserving the existing exit-code behavior, and add
a regression test covering an error emitted without a subsequent exit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dc7a192a-8a38-4bc1-a712-ec1c91e3fb1d

📥 Commits

Reviewing files that changed from the base of the PR and between 7280c1d and 538418e.

📒 Files selected for processing (6)
  • tools/egg-bin/src/baseCommand.ts
  • tools/scripts/src/commands/start.ts
  • tools/scripts/test/snapshot-start.test.ts
  • tools/scripts/test/start-unit.test.ts
  • wiki/log.md
  • wiki/workflows/local-ci.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • wiki/log.md
  • wiki/workflows/local-ci.md

Comment thread tools/scripts/test/start-unit.test.ts

@killagu killagu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copilot AI review requested due to automatic review settings August 4, 2026 03:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 14 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings August 6, 2026 10:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 17 changed files in this pull request and generated no new comments.

Suppressed comments (1)

wiki/log.md:314

  • This log entry still recommends the Windows-incompatible path-form workspace filter (-- --workspace ./tools/egg-bin). In this same PR the CI workflow and local-ci guide are updated to use the package-name form (--workspace @eggjs/bin), so the log note should be updated to match (and avoid suggesting a command that fails on Windows).
- note: A child killed by a signal reports `code=null` on its exit event; three CLIs mishandled that (egg-scripts foreground start exited 0, egg-bin forkNode reported "exit with code null" and flattened every child failure to exit 1, create-egg's latent custom-command path ran `process.exit(status ?? 0)`). All three now map signal deaths to the shell convention `128 + signal number`, and egg-bin propagates the child's exit code through `ForkError.oclif.exit`. Durable finding recorded in local-ci.md: egg-bin's coffee tests run the compiled `dist/commands` CLI, so its suite needs `ut run build -- --workspace ./tools/egg-bin` first (the dedicated `test-egg-bin` CI job does exactly this), unlike the rest of the repo which tests unbuilt sources.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@AGENTS.md`:
- Line 29: Update the focused-workflow guidance in AGENTS.md to show that
`@eggjs/bin` must be built before running its coffee tests, placing `ut run build
--workspace `@eggjs/bin`` before `ut run test --workspace `@eggjs/bin`` while
preserving the package-name workspace form.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 52c54151-b54b-43f3-a632-2b54a01da771

📥 Commits

Reviewing files that changed from the base of the PR and between 538418e and f78e413.

📒 Files selected for processing (6)
  • .github/workflows/ci.yml
  • AGENTS.md
  • tegg/plugin/eventbus/test/eventbus.test.ts
  • tools/scripts/test/start-unit.test.ts
  • wiki/log.md
  • wiki/workflows/local-ci.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • tools/scripts/test/start-unit.test.ts
  • wiki/workflows/local-ci.md

Comment thread AGENTS.md Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 63 out of 66 changed files in this pull request and generated no new comments.

Suppressed comments (2)

tools/egg-bin/src/baseCommand.ts:446

  • forkNode() builds an error message from modulePath + forkArgs, which omits the NODE_OPTIONS/execArgv that were actually used (even though fullCommand is already computed). This can make the failure message misleading and undermines the intent to print the full command line verbatim.
    wiki/log.md:314
  • This log entry references the Windows-incompatible path-form build filter (-- --workspace ./tools/egg-bin), but the durable guidance and CI now use the package-name form (--workspace @eggjs/bin). Updating this keeps the historical note consistent with the current documented workflow.

Copilot AI review requested due to automatic review settings August 6, 2026 13:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 64 out of 67 changed files in this pull request and generated no new comments.

Suppressed comments (1)

wiki/log.md:314

  • The 2026-08-03 log entry says the egg-bin suite needs ut run build -- --workspace ./tools/egg-bin and claims the test-egg-bin CI job does exactly that, but this PR changes CI and local guidance to the package-name form (--workspace @eggjs/bin). This note is now inconsistent/misleading for readers trying to follow the workflow.

Copilot AI review requested due to automatic review settings August 6, 2026 14:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 65 out of 68 changed files in this pull request and generated no new comments.

Suppressed comments (2)

tools/egg-bin/src/baseCommand.ts:449

  • The ForkError message is described as embedding the full fork command line, but it currently only uses modulePath + forkArgs.join(' '), which drops NODE_OPTIONS/execArgv and loses quoting for args with spaces. Use the already-computed fullCommand so the message is accurate and copy/pasteable (and so skipOclifErrorHandling is justified).
    wiki/log.md:314
  • This log entry says egg-bin needs ut run build -- --workspace ./tools/egg-bin and that the test-egg-bin CI job does exactly that, but the very next entry (2026-08-06) notes this path-form filter fails on Windows and CI now uses the package-name form. Update this line to reference --workspace @eggjs/bin so the wiki log is not self-contradictory.

fengmk2 added 12 commits August 7, 2026 16:23
A child process killed by a signal reports code=null on its exit event,
and three CLIs mishandled that case:

- egg-scripts start (foreground) treated a signal-killed server as a
  clean exit, and this.exit() threw an ExitError inside the event
  callback where nothing catches it, so any child failure exited 1.
  It now exits with the child's code, or 128 + signal number, via
  process.exit().
- egg-bin forkNode reported "exit with code null" and flattened every
  child failure to exit 1. It now names the signal, maps it to
  128 + signal number, and propagates the child's exit code through
  ForkError.oclif.exit.
- create-egg's custom command path ran process.exit(status ?? 0),
  masking a signal death as success. It now uses the exported
  spawnSyncExitCode() helper.
Post-review cleanup, no behavior change to the fixed paths:

- ForkError now extends oclif's CLIError, which carries the exit code
  through the supported constructor option instead of a hand-rolled
  oclif.exit field; BaseCommand.catch() keys on the standard
  skipOclifErrorHandling flag rather than instanceof ForkError, so only
  the long fork command line messages opt out of pretty-printing
- egg-scripts foreground start awaits the child inside run(), so the
  exit code flows through oclif's normal exit path and catch/finally
  still run; the unit test drives the public command surface instead of
  spying on process.exit
- the 128 + signal number mapping is the same verbatim toExitCode()
  helper in all three packages; create-egg's copy moved to a leaf
  module so it is not a published export and its test no longer loads
  the whole CLI entry
- reject the foreground start promise when the child fails to spawn,
  with a spawn-error unit test (a spawn failure emits 'error' and may
  never emit 'exit', which would hang run())
- drive a clean child exit in snapshot-start.test.ts mocks so the four
  positive-path cases settle now that foreground start awaits the child
- restore a public numeric field on ForkError as exitCode (CLIError's
  own code slot is a string rendered by oclif's pretty-printer)
- correct the wiki log and updated_at dates to 2026-08-03 and list the
  egg-bin package.json and tsconfig.json sources in local-ci.md
- the path form `--workspace ./tools/egg-bin` does not match any
  workspace on Windows, so the test-egg-bin Windows job never built
  dist and every coffee test failed with "command dev not found"
  (same failure on next); use the package-name filter form and give
  the build its own step for clear failure attribution
- the eventbus plugin app-boot beforeAll ran under vitest's default
  10s hook timeout (glob projects do not inherit the root config's
  hookTimeout) and flaked on slow Windows runners; raise it to 30s
- assert the exact spawn error instance in the start-unit spawn
  failure test (review feedback)
- @eggjs/scripts has no build script, so the name-form workspace filter
  ran nothing and failed; the job is ubuntu-only, keep the root tsdown
  path filter there
- the langchain plugin app-boot beforeAll hit the same default 10s hook
  timeout on Windows as eventbus; give it the same explicit 30s
vitest glob projects do not inherit the root config's hookTimeout, so
every tegg plugin and standalone suite booted its app under the default
10s and flaked on slow Windows CI runners: eventbus, langchain, then
mcp-client failed the same way on consecutive runs. Apply the timeout
to all 54 async beforeAll hooks instead of chasing them one at a time.
A spawn failure emits 'error' and may never emit 'exit', which would
leave the forkNode promise pending and the child ref in the children
set. Same pattern as the egg-scripts foreground fix.
The development plugin suites boot mm.cluster (a real master, agent and
worker process tree) in beforeAll, which exceeded the 20s hook budget on
a slow Windows CI runner during a rerun. The schedule plugin already
uses a 60s hook budget for the same reason.
The three egg-ready assertions slept a fixed 5s before reading
accumulated stdout, which flaked on a slow macOS CI runner even through
vitest's two retries (agent respawn took longer than the sleep). Poll
for the expected output with a 30s deadline instead; the test also gets
faster on healthy runners because polling returns as soon as the output
arrives.
Copilot AI review requested due to automatic review settings August 7, 2026 08:30
@fengmk2
fengmk2 force-pushed the fix/signal-exit-code branch from b01e5f8 to 51601d1 Compare August 7, 2026 08:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 65 out of 68 changed files in this pull request and generated no new comments.

Suppressed comments (2)

wiki/log.md:410

  • This log entry says egg-bin’s suite needs ut run build -- --workspace ./tools/egg-bin and that the test-egg-bin CI job does exactly that, but the current guidance/CI uses the package-name workspace filter (or working-directory: tools/egg-bin). Update the command here to match so the wiki log doesn’t reintroduce the Windows-mismatch pitfall.
    tools/egg-bin/src/baseCommand.ts:446
  • The fork failure message claims to include the full fork command line, but it currently only prints modulePath + args, omitting NODE_OPTIONS, process.execPath, and execArgv. This makes failures harder to reproduce/debug (especially when flags are injected) and contradicts the nearby comment about embedding the full command line. Use the already-computed fullCommand for the error message (and guard against a missing signal).

- the three positive reload assertions slept a fixed 5s after touching a
  watched file before reading stdout, which flaked on a slow Windows
  runner; poll with a 30s deadline instead (negative assertions keep
  their fixed waits, absence needs a bounded window)
- the Windows MySQL choco install failed transiently in CI; give it the
  same 3-attempt retry loop the Memurai step already uses
Copilot AI review requested due to automatic review settings August 7, 2026 08:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 69 out of 72 changed files in this pull request and generated no new comments.

Suppressed comments (1)

wiki/log.md:410

  • The 2026-08-03 log entry says egg-bin’s suite needs ut run build -- --workspace ./tools/egg-bin first and that the CI job “does exactly this”, but the 2026-08-06 entry + local-ci.md describe the correct approach (dedicated build step and using the package-name workspace filter on Windows). This line is now misleading for readers following the wiki log.

@fengmk2
fengmk2 merged commit d4129fc into next Aug 7, 2026
26 of 28 checks passed
@fengmk2
fengmk2 deleted the fix/signal-exit-code branch August 7, 2026 12:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants