CI/CD Pipeline 设计:GitHub Actions + Cache + Matrix Build

Choyeon· 2026年9月26日· 3 分钟阅读· 187 阅读· 890 字· 3,868 字符· 更新于 2026年10月1日
CI/CD Pipeline 设计:GitHub Actions + Cache + Matrix Build

CI/CD 是研发效率的脊梁。设计差的流水线 PR 等 40 分钟、随机失败、无法复用;设计好的流水线 5 分钟内给出可信反馈,并发矩阵覆盖多版本多 OS,缓存命中让重复构建秒过。

阶段拆分与路径过滤

经典 5 阶段:(1) changes 路径过滤:未改动的子项目整段跳过(PR 只改文档不跑后端测试);(2) lint 阶段快失败优先(8 分钟内出结果);(3) test 多版本矩阵并发;(4) build 构建产物;(5) 打 tag 才执行 publish 推送镜像/发布包。concurrency 同 ref 自动取消旧运行,节省 Action 分钟。

.github/workflows/ci.yml
name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]
    tags: ['v*.*.*']
  pull_request:
    branches: [main, develop]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  changes:
    runs-on: ubuntu-latest
    outputs:
      backend: ${{ steps.filter.outputs.backend }}
      frontend: ${{ steps.filter.outputs.frontend }}
      infra: ${{ steps.filter.outputs.infra }}
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@v3
        id: filter
        with:
          filters: |
            backend:
              - 'apps/api/**'
              - 'packages/shared/**'
              - 'pyproject.toml'
            frontend:
              - 'apps/web/**'
              - 'packages/ui/**'
              - 'pnpm-lock.yaml'
            infra:
              - 'infra/**'
              - 'Dockerfile*'

  lint:
    needs: changes
    if: needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true'
    runs-on: ubuntu-latest
    timeout-minutes: 8
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with: { version: 9 }
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
          cache-dependency-path: pnpm-lock.yaml
      - name: Install Node deps
        run: pnpm install --frozen-lockfile --prefer-offline
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
          cache: pip
          cache-dependency-path: pyproject.toml
      - name: Install Python deps
        run: pip install -e ".[dev]" --retries 3
      - name: Run ESLint + Ruff
        run: |
          pnpm lint
          ruff check apps/api packages
      - name: Cache turbo setup
        uses: actions/cache@v4
        with:
          path: .turbo
          key: turbo-${{ github.sha }}
          restore-keys: turbo-

  test:
    needs: lint
    runs-on: ubuntu-latest
    timeout-minutes: 20
    strategy:
      fail-fast: false
      matrix:
        python-version: ['3.11', '3.12', '3.13']
        node-version: ['18', '20', '22']
        os: [ubuntu-latest]
        include:
          - os: macos-latest
            python-version: '3.12'
            node-version: '20'
    services:
      postgres:
        image: postgres:16-alpine
        env: { POSTGRES_USER: test, POSTGRES_PASSWORD: test, POSTGRES_DB: test }
        ports: ['5432:5432']
        options: >-
          --health-cmd pg_isready
          --health-interval 10s --health-timeout 5s --health-retries 5
      redis:
        image: redis:7-alpine
        ports: ['6379:6379']
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4; with: { version: 9 }
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: pnpm
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          cache: pip
      - run: pnpm install --frozen-lockfile
      - run: pip install -e ".[test]"
      - name: Run tests with coverage
        run: |
          pnpm test -- --coverage --reporter=github
          pytest apps/api --cov=apps/api --cov-report=xml
        env:
          DATABASE_URL: postgresql://test:test@localhost:5432/test
          REDIS_URL: redis://localhost:6379
      - uses: codecov/codecov-action@v4
        if: matrix.os == 'ubuntu-latest' && matrix.node-version == '20' && matrix.python-version == '3.12'
        with: { fail_ci_if_error: false, token: ${{ secrets.CODECOV_TOKEN }} }

  build-and-push-image:
    needs: test
    if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha,prefix=
      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          platforms: linux/amd64,linux/arm64
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

缓存矩阵构建加速三剑客

(1) 依赖缓存:setup-node + pnpm cache、pip cache 让 90% 构建免装依赖;(2) 任务产物缓存:turbo cache 跨工作流复用构建结果;(3) Docker 层缓存:buildx gha cache 复用层,推送时间从 5 分钟缩到 40 秒。Matrix 跨 OS/版本覆盖 Python 3.11-3.13 × Node 18-22,矩阵 include 注入 MacOS 单测确保 Unix 类兼容性。

优化项 未优化耗时 优化后耗时 节省比例
npm/pip 依赖安装 3min 20s (95%命中) 89%
Turborepo 构建 6min 15s (全命中) 96%
Docker 镜像推送 5min 40s 87%
Matrix 顺序执行 30min 6min (5并发) 80%
路径过滤跳过 20min/每次PR 3min 85%

最佳实践

timeout-minutes 每个 Job 强制上限防挂死;services 用 Postgres/Redis 健康检查等待就绪后再跑;fail-fast:false 保证所有组合跑完不提前中断;最终 semver tag 触发发布流程,main 分支仅 build+test 止。

本文作者

评论 (0)

暂无评论,来抢沙发吧。