Back

Node.js 项目的实用 CI 配置

Node.js 项目的实用 CI 配置

每个 Node.js 项目都会达到这样一个阶段:手动测试变得不再可靠。有人在推送代码前忘记运行代码检查工具。测试在本地通过,但在队友的机器上却失败了。依赖项更新导致生产环境崩溃,因为没有人发现不兼容问题。

一个结构良好的 CI 流水线可以自动捕获这些问题。本文将解释一个可靠的 Node.js CI 基础配置应该是什么样子(使用 GitHub Actions),为什么需要每个组件,以及如何思考这些组成部分,使你的流水线经久耐用。

核心要点

  • 在 CI 流水线中使用 npm ci 而不是 npm install,以基于锁文件实现确定性、可重现的构建
  • 构建流水线以快速失败:安装依赖 → 代码检查和类型检查 → 运行测试
  • 使用版本矩阵测试你实际支持的 Node.js 版本,重点关注活跃 LTS 版本
  • 在测试之前运行静态分析,以快速发现错误并生成更清晰的失败消息
  • 通过避免过度设计的缓存和过多的版本固定,保持流水线简单且易于维护

JavaScript CI 流水线的基础功能

一个实用的 Node.js 项目 CI 流水线需要处理三个关注点:确保依赖项一致性、验证代码质量,以及在相关的 Node 版本上运行测试。

流水线应该快速失败且失败可见。如果出现问题,开发人员需要立即知道并理解原因。

核心阶段

一个可靠的 GitHub Actions Node CI 工作流遵循以下顺序:

安装依赖代码检查和类型检查运行测试

每个阶段都是下一个阶段的门控。如果代码甚至无法正确解析,就没有必要运行完整的测试套件。

依赖项安装:为什么 npm ci 很重要

最重要的 npm CI 最佳实践是在流水线中使用 npm ci 而不是 npm install

npm ci 做了两件对 CI 很重要的事情:

  1. 它精确安装锁文件中的内容——不进行版本解析,没有意外
  2. 它首先删除 node_modules,确保从干净状态开始

这种确定性行为意味着你的 CI 环境与锁文件指定的内容匹配。当构建失败时,你知道失败不是由依赖项漂移引起的。

你的锁文件(npm 的 package-lock.json、pnpm 的 pnpm-lock.yaml、Yarn 的 yarn.lock)必须提交到代码仓库。没有它,npm ci 将无法工作,你也会失去可重现性。

使用 Corepack 管理包管理器

如果你的团队使用 pnpm 或 Yarn,Corepack 可以处理包管理器的版本控制。在安装依赖项之前,在工作流中启用它。这确保每个人(包括 CI)都使用 package.json 中指定的相同包管理器版本。

版本矩阵:跨 Node 版本测试

版本矩阵允许你同时在多个 Node.js 版本上运行流水线。对于大多数项目,针对活跃 LTS 版本进行测试就足够了。兼容性要求更广泛的项目可能会添加当前的维护 LTS 版本。

矩阵方法可以及早发现兼容性问题。在较新 Node 版本中有效的语法特性可能在用户依赖的旧版本中不存在。

保持矩阵最小化。针对每个可能的版本进行测试会增加 CI 时间,但收益不成比例。专注于你的项目实际支持的版本。

在测试之前进行代码检查和类型检查

在测试套件之前运行静态分析。ESLint 捕获代码质量问题。TypeScript(如果你使用它)捕获类型错误。两者的运行速度都比大多数测试套件快。

这种顺序很重要,原因有二:

  1. 更快的反馈:语法错误在几秒钟内就会显现,而不是几分钟
  2. 更清晰的失败:代码检查错误比由相同底层问题引起的隐晦测试失败更容易诊断

配置这些工具在出现错误时使构建失败。不会导致构建失败的警告会被忽略。

测试执行和失败可见性

你的测试阶段应该产生清晰的输出。当测试失败时,开发人员需要快速识别问题——理想情况下无需深入折叠的日志部分。

大多数测试运行器支持 CI 友好的输出格式。Jest、Vitest 和 Node 的内置测试运行器都能检测 CI 环境并相应调整其输出。

考虑以下实践:

  • 仅在实际使用覆盖率数据时才运行带覆盖率的测试
  • 如果运行器支持且测试是独立的,则并行化测试文件
  • 在开发分支上快速失败,在主分支上运行完整套件

缓存:关于预期的说明

依赖项缓存可以减少安装时间,但收益各不相同。依赖项较少的小型项目可能改善很小。大型 monorepo 可能每次运行节省几分钟。

不要过度设计缓存。actions/setup-node 中的内置缓存可以处理常见情况。如果安装速度很慢,在添加复杂性之前先进行测量。

保持流水线的可维护性

需要持续更新的 CI 流水线会成为负担。避免将 action 版本固定到特定补丁——使用接收兼容更新的主版本标签。在可能的情况下,通过发布线而不是确切版本来引用 Node 版本。

目标是一个可靠运行且无需频繁维护的 JavaScript CI 流水线。当你确实需要更新它时,更改应该是有意为之的,而不是被动应对的。

结论

一个可靠的 Node.js CI 配置不需要复杂的配置。使用 npm ci 确定性地安装依赖项,在测试之前运行静态分析,并针对你支持的 Node 版本进行测试。使失败可见,并保持流水线足够简单以便维护。

从这个基线开始。仅在有特定问题需要解决时才增加复杂性。

常见问题

npm ci 精确安装锁文件指定的内容而不解析版本,并且首先删除 node_modules 以获得干净状态。npm install 可能会更新锁文件并解析不同的版本。对于 CI,npm ci 提供与提交的锁文件完全匹配的确定性构建。

测试你的项目实际支持的版本。对于大多数项目,活跃 LTS 版本就足够了。如果需要更广泛的兼容性,可以添加维护 LTS 版本。避免测试每个可能的版本,因为它会增加 CI 时间而收益不成比例。

代码检查比大多数测试套件运行得更快,可以在几秒钟内捕获语法和代码质量问题。首先运行它可以提供更快的反馈并产生更清晰的失败消息。代码检查错误比由相同底层问题引起的隐晦测试失败更容易诊断。

这取决于你的项目规模。依赖项较少的小型项目从缓存中获得的改善很小。大型 monorepo 每次运行可以节省几分钟。从 actions/setup-node 中的内置缓存开始,在添加复杂性之前测量实际的安装时间。

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay