GitHub Actions Node.js CI 完全指南:Lint → Type Check → 测试 → 构建 → 发布的完整流水线

用 GitHub Actions 搭建 Node.js 项目的完整 CI 流水线:缓存优化、矩阵构建、并行测试、覆盖率上报、Monorepo 支持、Playwright E2E 集成、安全扫描(npm audit/Snyk)、自动发布(npm/changesets)。含完整的生产级配置模板和性能调优策略。

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~40spnpm 项目

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 }} 在命令行

相关阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「saas」更多文章