在应用构建完成后运行 shutdown-check。CLI 的退出码决定 job 是否失败;JSON 和 JUnit 输出会保留诊断码、时间线和捕获到的服务输出。
准备项目
添加 CI 配置之前,先完成以下准备:
- 在本地运行
npx shutdown-check test。 - 提交
shutdown-check.json。 - 把
command指向构建后的入口文件。 - 为这个测试预留一个端口。
- 使用相互隔离的数据库、队列和文件资源。
- 确保测试请求所用路由的行为是确定的。
shutdown-check 会自己启动和停止服务。不要在前面的 CI 步骤或 service container 中,在配置的端口上启动同一个服务。
GitHub Actions
name: Graceful shutdown
on:
push:
branches: [main]
pull_request:
jobs:
shutdown-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- name: Test graceful shutdown
run: npx shutdown-check test --json --junit shutdown-result.xml
- name: Save shutdown report
uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: shutdown-check-report
path: shutdown-result.xml
if-no-files-found: ignore--json 会把完整的结构化结果写进 job 日志。如果你更想看可读的文本时间线,去掉这个参数即可。
上传 artifact 的步骤使用了 if: !cancelled(),所以检查失败后这一步仍会执行。if-no-files-found: ignore 用来处理环境或配置错误:这类错误以退出码 2 结束,此时还没有生成 JUnit 结果。
如果想在 pull request 的检查结果里直接显示测试结果,可以把 shutdown-result.xml 交给一个 JUnit reporter action,用来替代上传,或者和上传同时使用。
GitLab CI
shutdown-check:
image: node:22
script:
- npm ci
- npm run build
- npx shutdown-check test --junit shutdown-result.xml
artifacts:
when: always
reports:
junit: shutdown-result.xml
paths:
- shutdown-result.xmlGitLab 会读取这份报告,并把测试用例显示在 merge request 中。由于设置了 when: always,即使失败,artifact 仍然可以下载。
CI 中的退出码
| 退出码 | 含义 |
|---|---|
| 0 | 通过 |
| 1 | 关闭检查失败 |
| 2 | 环境或配置错误 |
| 退出码 | 在 CI 中的含义 | JUnit 文件 |
|---|---|---|
0 | 关闭行为通过检查 | 指定时写入 |
1 | 测试已运行,但有一项关闭检查失败 | 指定时写入 |
2 | 参数、配置或环境问题导致测试无法运行 | 不写入 |
不要在关闭测试命令后面加 || true。这样会吞掉退出码,可能让破坏部署行为的改动被合并。
为并行 job 分配不同端口
只要 baseUrl 已被其他进程占用,shutdown-check 就会以 SC001 失败。给每个 job 或 matrix 条目分配不同的端口,并通过 env 传给服务。
{
"env": { "PORT": "3510" },
"baseUrl": "http://127.0.0.1:3510"
}如果 matrix 是动态生成的,可以在 job 中生成一份小配置,或者调用 Node API,并用 matrix 的值推导出端口。不要让并发的关闭测试操作同一批数据库记录或队列消息。
设置 CI 超时
job 的超时时间必须留够以下各项的耗时:
- 安装依赖和构建
readiness.timeoutMsworkload.started.timeoutMsshutdown.deadlineMs- 少量的进程和 runner 开销
如果测试运行器的超时先触发,你就拿不到有价值的关闭检查结果。外层超时要大于 shutdown-check 各项超时之和,同时关闭截止时间仍要贴近生产环境的实际情况。
排查失败的 job
- 看输出第一行的 SC 码。
- 找到最后一个成功的时间线事件。
- 查看捕获到的 stderr 和 stdout。
- 打开对应诊断码的页面。
- 在本地用相同的构建命令和环境复现。
如果失败和时序有关,或者发生在服务就绪之前,请参考故障排查。
在现有测试套件中运行
如果 CI 已经在发布 node:test 或 Vitest 的结果,可以在测试文件中调用 checkShutdown()。这样现有的测试运行器就能报告断言结果,你也可以在失败时打印时间线。
完整示例和超时设置建议见使用 node:test 和 Vitest 测试。
支持的 runner
请使用 Linux 或 macOS,并搭配 Node.js 22 或更高版本。shutdown-check 依赖 POSIX 信号和进程组,因此不支持 Windows runner。Linux 容器 runner 只要能启动子进程并绑定本地端口,也同样支持。