目录
fengmk2

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

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.

1个月前1702次提交

English | 简体中文

NPM version NPM quality NPM download Node.js Version FOSSA Status

Continuous Integration Test coverage Known Vulnerabilities Open Collective backers and sponsors

Features

  • Built-in Process Management
  • Plugin System
  • Framework Customization
  • Lots of plugins

Quickstart

Follow the commands listed below.

$ corepack enable utoo
$ mkdir showcase && cd showcase
$ ut create egg@beta
$ ut install
$ ut run dev
$ open http://localhost:7001

Node.js >= 22.18.0 required.

Monorepo Structure

This project is structured as a utoo monorepo with the following packages:

  • packages/egg - Main Eggjs framework
  • examples/helloworld-commonjs - CommonJS example application
  • examples/helloworld-typescript - TypeScript example application
  • site - Documentation website

The monorepo uses utoo catalog mode for centralized dependency management, ensuring consistent versions across all packages.

Development Commands

# Install dependencies for all packages
ut install --from pnpm

# Build all packages
ut run build

# Test all packages
ut run test

# Run specific package commands
ut --filter=egg run test
ut --filter=@examples/helloworld-typescript run dev
ut --filter=site run dev

Local External Services

Some DAL, ORM, Redis, and ecosystem benchmark paths need local MySQL and Redis services. Start the repository-aligned Docker services before running those tests on a clean machine:

ut run dev:services:start

This starts MySQL 8 and Redis 7, matching the main CI service versions, and creates the databases used by local DAL/ORM/e2e fixtures: test, apple, banana, test_runtime_datasource, test_runtime_dao, test_dal_plugin, test_dal_standalone, cnpmcore, and cnpmcore_unittest.

Useful commands:

ut run dev:services:status
ut run dev:services:stop
ut run dev:services:reset

The default host ports are 127.0.0.1:3306 for MySQL and 127.0.0.1:6379 for Redis. If either port is already used, the start command stops before changing containers. Keep using the existing service if it is compatible with CI, or stop it and run the command again. You can change Docker host ports with EGG_DEV_SERVICES_MYSQL_PORT and EGG_DEV_SERVICES_REDIS_PORT; however, the full DAL/ORM/Redis local test path still expects the default host ports.

Image overrides are available for compatibility checks:

EGG_DEV_SERVICES_MYSQL_IMAGE=mysql:5.7 ut run dev:services:start
EGG_DEV_SERVICES_REDIS_IMAGE=redis:7 ut run dev:services:start

Run ut run dev:services:reset before switching MySQL image families, for example between MySQL 8 and MySQL 5.7, because MySQL data directories are not downgrade-compatible across major versions.

Current hard-coded service assumptions:

  • Redis plugin fixtures under plugins/redis/test/fixtures/apps/**/config.* use 127.0.0.1:6379; skipped Redis plugin tests become runnable when that port is available.
  • Session Redis fixtures under plugins/session/test/fixtures/redis-session/config/config.default.js use 127.0.0.1:6379.
  • DAL runtime tests in tegg/core/dal-runtime/test/DataSource.test.ts and tegg/core/dal-runtime/test/DAO.test.ts use local MySQL on port 3306.
  • DAL module fixtures in tegg/plugin/dal/test/fixtures/apps/dal-app/modules/dal/module.yml and tegg/standalone/standalone/test/fixtures/dal-*/module.yml use local MySQL on port 3306.
  • ORM fixtures in tegg/plugin/orm/test/fixtures/prepare.js and tegg/plugin/orm/test/fixtures/apps/orm-app/config/config.default.ts use local MySQL on port 3306.

Documentations

Contributors

contributors

How to Contribute

Please let us know how can we help. Do check out issues for bug reports or suggestions first.

To become a contributor, please follow our contributing guide, and review the repository guidelines for day-to-day development tips.

Sponsors and Backers

sponsors backers

License

MIT

FOSSA Status

邀请码
    Gitlink(确实开源)
  • 加入我们
  • 官网邮箱:gitlink@ccf.org.cn
  • QQ群
  • QQ群
  • 公众号
  • 公众号

版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9 京公网安备 11010802047560号