Node.js 项目的 CI 流水线是工程化的基石。一个完善的 CI 应该在代码合并前自动验证代码质量、运行测试、检查类型安全,并在发布时自动构建和部署。本文提供从简单项目到 Monorepo 的完整 GitHub Actions 配置模板和最佳实践。
一、最小可用 CI(90% 项目的起点)
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run type-check
- run: npm test -- --coverage
- run: npm run build
为什么这个配置能覆盖 90% 需求?
npm ci严格按 lockfile 安装,避免"在我机器上跑得好"问题lint + type-check + test + build四重检查过滤 95%+ 的编码问题cache: 'npm'自动缓存~/.npm,Node 依赖安装提速 3-5 倍
二、完整生产级 CI:分层流水线
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
paths:
- 'src/**'
- 'tests/**'
- 'package*.json'
- '.github/workflows/ci.yml'
pull_request:
branches: [main]
paths:
- 'src/**'
- 'tests/**'
- 'package*.json'
- '.github/workflows/ci.yml'
jobs:
# ═══════════════════════════════════════════════
# 阶段 1:代码质量检查(最快失败策略)
# ═══════════════════════════════════════════════
lint:
name: 🔍 Lint & Format
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run format:check
# ═══════════════════════════════════════════════
# 阶段 2:类型安全与单元测试(矩阵构建)
# ═══════════════════════════════════════════════
test:
name: 🧪 Test (Node ${{ matrix.node }})
needs: lint
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
fail-fast: false # 一个版本失败不影响其他版本
matrix:
node: [18, 20, 22]
os: [ubuntu-latest]
include:
- node: 20
os: windows-latest
- node: 20
os: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'npm'
- run: npm ci
- run: npm run type-check
- run: npm test -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
if: matrix.node == 20 && matrix.os == 'ubuntu-latest'
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info
# ═══════════════════════════════════════════════
# 阶段 3:E2E 测试(Playwright)
# ═══════════════════════════════════════════════
e2e:
name: 🎭 E2E Tests
needs: [lint, test]
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run test:e2e
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/
retention-days: 7
# ═══════════════════════════════════════════════
# 阶段 4:安全扫描
# ═══════════════════════════════════════════════
security:
name: 🔒 Security Scan
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm audit --audit-level moderate
- uses: snyk/actions/node@master
if: github.event_name == 'pull_request'
continue-on-error: true
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
# ═══════════════════════════════════════════════
# 阶段 5:构建验证
# ═══════════════════════════════════════════════
build:
name: 📦 Build Verification
needs: [lint, test]
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 5
三、缓存策略深度优化
3.1 npm 缓存
steps:
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm' # 自动缓存 ~/.npm(推荐)
3.2 Node Modules 缓存(更快但更大)
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Cache node_modules
uses: actions/cache@v4
id: cache
with:
path: node_modules
key: ${{ runner.os }}-node20-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-node20-
- if: steps.cache.outputs.cache-hit != 'true'
run: npm ci
缓存命中 vs miss 对比:
| 策略 | 缓存命中 | 缓存未命中 | 适用 |
|---|---|---|---|
cache: 'npm' | ~30s | ~60s | 通用推荐 |
node_modules | ~5s | ~60s | 大型项目 |
pnpm store | ~10s | ~40s | pnpm 项目 |
3.3 Turborepo 远程缓存
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
run: npx turbo run build test lint --cache-dir=.turbo
四、Monorepo CI(pnpm workspace + Turborepo)
name: Monorepo CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
setup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm turbo run lint type-check test build --filter=[origin/main...HEAD]
五、自动发布流程
5.1 npm 自动发布(Changesets + GitHub Release)
name: Release
on:
push:
branches: [main]
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
registry-url: 'https://registry.npmjs.org'
- run: pnpm install
- name: Create Release PR or Publish
uses: changesets/action@v1
with:
publish: pnpm release
version: pnpm version-packages
commit: 'chore(release): version packages'
title: 'chore(release): version packages'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
5.2 语义化版本自动标签
jobs:
release:
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 获取完整历史用于生成 changelog
- name: Bump version and push tag
uses: anothrNick/github-tag-action@1.67.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WITH_V: true
DEFAULT_BUMP: patch
六、性能调优与故障排查
| 问题 | 原因 | 解决 |
|---|---|---|
npm ci 超时 | 依赖解析慢 | 使用 lockfile + 缓存,或换 pnpm |
| 测试随机失败 | 测试间有状态泄漏 | 并行度设为 1,或隔离数据库 |
| Docker 构建慢 | 没有层缓存 | cache-from/to: type=gha |
| Artifact 过大 | 包含 node_modules | .gitignore 同样忽略上传 |
| Secrets 泄露 | 命令回显 | 用 env: 注入,不用 ${{ secrets }} 在命令行 |
相关阅读
- GitHub Actions 详解 — 核心概念与语法速查
- GitHub Actions Docker CI/CD — 容器化项目的 CI/CD
- GitHub Actions 部署到 Vercel — 前端项目自动部署
- Docker 专题 — Docker 部署与镜像优化
- Node.js 专题 — Node.js 性能优化
- React 测试指南 — Vitest + Playwright 的 CI 集成
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。
「saas」更多文章
自定义域名接入 Cloudflare:CDN 加速与缓存规则配置实战
手把手讲解自定义域名接入 Cloudflare CDN 的完整流程:NS 接管、SSL/TLS 模式选择、橙云代理与灰云 DNS 的区别,并用 2024 年后的新 Cache Rules 引擎实战配置静态资源长缓存、HTML 不缓存、后台 Bypass,让边缘缓存命中率最大化。
Hugo 部署到 Cloudflare Pages 实战:从 Git 推送到全球边缘上线
详解 Cloudflare Pages 部署 Hugo 静态站的完整流程,涵盖框架预设、HUGO_VERSION 环境变量固定、构建命令、自定义域名、分支预览、_headers 缓存安全头配置及常见构建失败排查,一次推送即可全球边缘上线。
Cloudflare Workers 入门实战:在边缘运行你的第一行代码
一份面向初学者的 cloudflare workers 教程,从 V8 Isolate 运行时模型讲起,手把手演示 wrangler 初始化、路由分发、KV 存储、Cache API 缓存响应与环境变量配置,帮你在 30 分钟内部署第一个生产可用的边缘 Serverless 应用。