CI/CD 与自动化
概览
把质量门禁自动化,确保任何改动在通过测试、lint、类型检查和构建之前,都不能进入生产环境。CI/CD 是其他所有 skill 的执行保障机制,它能稳定地捕捉人类和 agent 都会漏掉的问题,而且对每一次改动一视同仁。
Shift Left: 尽量在流水线更前面发现问题。一个在 lint 阶段发现的 bug,代价可能只是几分钟;同样的问题到了生产环境,代价可能是几小时。要把检查前移,静态分析早于测试,测试早于 staging,staging 早于 production。
越快越安全: 更小的改动批次和更频繁的发布,并不会增加风险,反而会降低风险。只有 3 个改动的部署,比带着 30 个改动的部署更容易调试。频繁发布也会增强团队对发布流程本身的信心。
何时使用
- 为新项目搭建 CI 流水线
- 增加或修改自动化检查
- 配置部署流水线
- 某项改动应该触发自动验证时
- 调试 CI 失败
质量门禁流水线
每一个改动在合并前都必须通过这些门禁:
Pull Request Opened
│
▼
┌─────────────────┐
│ LINT CHECK │ eslint, prettier
│ ↓ pass │
│ TYPE CHECK │ tsc --noEmit
│ ↓ pass │
│ UNIT TESTS │ jest/vitest
│ ↓ pass │
│ BUILD │ npm run build
│ ↓ pass │
│ INTEGRATION │ API/DB tests
│ ↓ pass │
│ E2E (optional) │ Playwright/Cypress
│ ↓ pass │
│ SECURITY AUDIT │ npm audit
│ ↓ pass │
│ BUNDLE SIZE │ bundlesize check
└─────────────────┘
│
▼
Ready for review
任何门禁都不能跳过。 Lint 失败就修 lint,不要关规则。测试失败就修代码,不要跳过测试。
GitHub Actions 配置
基础 CI 流水线
# .github/workflows/ci.yml
name: CI
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npx tsc --noEmit
- name: Test
run: npm test -- --coverage
- name: Build
run: npm run build
- name: Security audit
run: npm audit --audit-level=high
带数据库集成测试
integration:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: testdb
POSTGRES_USER: ci_user
POSTGRES_PASSWORD: ${{ secrets.CI_DB_PASSWORD }}
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- name: Run migrations
run: npx prisma migrate deploy
env:
DATABASE_URL: postgresql://ci_user:${{ secrets.CI_DB_PASSWORD }}@localhost:5432/testdb
- name: Integration tests
run: npm run test:integration
env:
DATABASE_URL: postgresql://ci_user:${{ secrets.CI_DB_PASSWORD }}@localhost:5432/testdb
注意: 即使是 CI 专用的测试数据库,也要把凭据放在 GitHub Secrets,而不是硬编码进配置。这能帮助团队养成正确习惯,也能避免测试凭据被错误地复制到其他环境。
E2E 测试
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Build
run: npm run build
- name: Run E2E tests
run: npx playwright test
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/
把 CI 失败反馈给 Agent
CI 和 AI agent 结合的关键,是反馈闭环。CI 一旦失败:
CI fails
│
▼
Copy the failure output
│
▼
Feed it to the agent:
"The CI pipeline failed with this error:
[paste specific error]
Fix the issue and verify locally before pushing again."
│
▼
Agent fixes → pushes → CI runs again
关键模式:
Lint failure → Agent runs `npm run lint --fix` and commits
Type error → Agent reads the error location and fixes the type
Test failure → Agent follows debugging-and-error-recovery skill
Build error → Agent checks config and dependencies
部署策略
预览部署
每个 PR 都应该有一个预览部署,供人工验证:
# Deploy preview on PR (Vercel/Netlify/etc.)
deploy-preview:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- name: Deploy preview
run: npx vercel --token=${{ secrets.VERCEL_TOKEN }}
Feature Flags
Feature flag 让“部署”和“发布”解耦。把未完成或高风险功能放在 flag 后面,这样你就可以:
- 代码先上线,不立即启用
- 无需重新部署即可回滚,直接关掉 flag
- 做 canary,先 1%,再 10%,最后 100%
- 做 A/B 测试,比较开关两种行为差异
// Simple feature flag pattern
if (featureFlags.isEnabled('new-checkout-flow', { userId })) {
return renderNewCheckout();
}
return renderLegacyCheckout();
Flag 生命周期: Create → Enable for testing → Canary → Full rollout → Remove the flag and dead code。
永远不清理的 flag 会演变成技术债,所以在创建它的时候就应设定清理日期。
分阶段发布
PR merged to main
│
▼
Staging deployment (auto)
│ Manual verification
▼
Production deployment (manual trigger or auto after staging)
│
▼
Monitor for errors (15-minute window)
│
├── Errors detected → Rollback
└── Clean → Done
回滚计划
每一次部署都必须可逆:
# Manual rollback workflow
name: Rollback
on:
workflow_dispatch:
inputs:
version:
description: 'Version to rollback to'
required: true
jobs:
rollback:
runs-on: ubuntu-latest
steps:
- name: Rollback deployment
run: |
# Deploy the specified previous version
npx vercel rollback ${{ inputs.version }}
环境管理
.env.example → Committed (template for developers)
.env → NOT committed (local development)
.env.test → Committed (test environment, no real secrets)
CI secrets → Stored in GitHub Secrets / vault
Production secrets → Stored in deployment platform / vault
CI 不应该拥有生产 secrets。测试环境和生产环境的凭据必须分开。
CI 之外的自动化
Dependabot / Renovate
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
Build Cop 角色
指定一个 Build Cop,专门对“CI 必须保持绿色”负责。构建一旦坏掉,Build Cop 的职责是修复或回滚,而不是默认归咎于最后一个提交的人。这能避免坏构建一直堆积,而大家都以为别人会修。
PR 检查
- Required reviews: 合并前至少 1 个批准
- Required status checks: CI 必须通过
- Branch protection: 禁止对
main强推 - Auto-merge: 所有检查通过且已批准后自动合并
CI 优化
当流水线耗时超过 10 分钟时,按以下顺序优化:
Slow CI pipeline?
├── Cache dependencies
│ └── Use actions/cache or setup-node cache option for node_modules
├── Run jobs in parallel
│ └── Split lint, typecheck, test, build into separate parallel jobs
├── Only run what changed
│ └── Use path filters to skip unrelated jobs (e.g., skip e2e for docs-only PRs)
├── Use matrix builds
│ └── Shard test suites across multiple runners
├── Optimize the test suite
│ └── Remove slow tests from the critical path, run them on a schedule instead
└── Use larger runners
└── GitHub-hosted larger runners or self-hosted for CPU-heavy builds
示例:缓存与并行
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '22', cache: 'npm' }
- run: npm ci
- run: npm run lint
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '22', cache: 'npm' }
- run: npm ci
- run: npx tsc --noEmit
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '22', cache: 'npm' }
- run: npm ci
- run: npm test -- --coverage
常见自我安慰
| 自我安慰 | 现实 |
|---|---|
| “CI 太慢了” | 应该优化流水线,而不是跳过。5 分钟 CI 往往能省掉几小时调试。 |
| “这次改动很小,跳过 CI 吧” | 小改动一样会炸构建。再说,小改动跑 CI 也很快。 |
| “这个测试 flaky,重跑一下就行” | Flaky test 会掩盖真 bug,还会浪费所有人的时间。修掉它。 |
| “CI 以后再补” | 没有 CI 的项目很容易积累一堆损坏状态。第一天就要配。 |
| “人工测试就够了” | 人工测试不可扩展,也不可重复。能自动化的就自动化。 |
危险信号
- 项目里没有 CI 流水线
- CI 失败被忽略或被静音
- 为了让流水线变绿而在 CI 里禁用测试
- 生产部署前没有 staging 验证
- 没有回滚机制
- secrets 存在代码或 CI 配置里,而不是 secrets manager
- CI 很慢,但没人尝试优化
验证
配置或修改 CI 后,确认:
- 所有质量门禁都存在,例如 lint、types、tests、build、audit
- 每个 PR 和每次 push 到
main都会触发流水线 - 失败会阻止合并,branch protection 已配置
- CI 结果会真正反馈回开发闭环
- Secrets 保存在 secrets manager,而不是代码里
- 部署流程具备回滚机制
- 测试套件相关流水线在 10 分钟内完成