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 止。