Skip to content

CI/CD 流水线集成

将 AI 辅助开发的项目接入 CI/CD 流水线,能够在代码进入生产环境前自动执行质量门控。这对于 AI 生成代码尤为重要,因为 AI 产出物需要经过多道自动化验证才能建立对其可靠性的信任。

流水线设计原则

针对 AI 开发项目的 CI/CD 流水线应遵循以下原则:

  1. 快速反馈:Lint 和单元测试在 3 分钟内完成,让开发者保持专注
  2. 质量门控不可绕过:任何质量门控失败都阻断合并,无例外
  3. 分层检测:静态分析 → 单元测试 → 集成测试 → 安全扫描 → 部署
  4. 环境隔离:staging 环境验证通过后方可推向 production

完整 GitHub Actions 工作流

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

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

env:
  NODE_VERSION: '20'
  COVERAGE_THRESHOLD: 80

jobs:
  # ── 第一阶段:静态检查(最快,先行) ──────────────────────────
  lint:
    name: 代码质量检查
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: 设置 Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: 安装依赖
        run: npm ci

      - name: ESLint 检查
        run: npm run lint -- --format=@microsoft/eslint-formatter-sarif \
          --output-file eslint-results.sarif
        continue-on-error: true

      - name: 上传 ESLint 结果到 GitHub Security
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: eslint-results.sarif

      - name: TypeScript 类型检查
        run: npm run type-check

      - name: Prettier 格式检查
        run: npx prettier --check "src/**/*.{ts,tsx,json}"

  # ── 第二阶段:测试与覆盖率 ──────────────────────────────────
  test:
    name: 单元测试与覆盖率
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - run: npm ci

      - name: 运行单元测试
        run: npm test -- --coverage --coverageReporters=json-summary

      - name: 覆盖率门控检查
        run: |
          COVERAGE=$(node -e "
            const s = require('./coverage/coverage-summary.json');
            console.log(Math.floor(s.total.lines.pct));
          ")
          echo "当前行覆盖率: ${COVERAGE}%"
          if [ "$COVERAGE" -lt "${{ env.COVERAGE_THRESHOLD }}" ]; then
            echo "覆盖率 ${COVERAGE}% 低于阈值 ${{ env.COVERAGE_THRESHOLD }}%"
            exit 1
          fi

      - name: 上传覆盖率报告
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

  # ── 第三阶段:安全扫描 ──────────────────────────────────────
  security:
    name: 安全扫描
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # 完整历史,用于密钥扫描

      - name: 依赖漏洞扫描
        run: npm audit --audit-level=high

      - name: 密钥泄漏扫描(git 历史)
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Semgrep 代码安全扫描
        uses: semgrep/semgrep-action@v1
        with:
          config: >-
            p/owasp-top-ten
            p/javascript
            p/typescript

  # ── 第四阶段:构建产物 ──────────────────────────────────────
  build:
    name: 构建
    runs-on: ubuntu-latest
    needs: [test, security]
    outputs:
      image-tag: ${{ steps.meta.outputs.tags }}
    steps:
      - uses: actions/checkout@v4

      - name: 设置 Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: 登录容器仓库
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: 提取镜像元数据
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/${{ github.repository }}
          tags: |
            type=sha,prefix=sha-
            type=ref,event=branch

      - name: 构建并推送镜像
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  # ── 第五阶段:部署到 Staging ────────────────────────────────
  deploy-staging:
    name: 部署到预发布环境
    runs-on: ubuntu-latest
    needs: build
    environment:
      name: staging
      url: https://staging.example.com
    if: github.ref == 'refs/heads/develop'
    steps:
      - name: 部署到 Staging
        run: |
          curl -X POST "${{ secrets.DEPLOY_WEBHOOK_STAGING }}" \
            -H "Authorization: Bearer ${{ secrets.DEPLOY_TOKEN }}" \
            -d '{"image": "${{ needs.build.outputs.image-tag }}"}'

      - name: 等待部署就绪
        run: |
          for i in {1..12}; do
            STATUS=$(curl -s https://staging.example.com/health | jq -r '.status')
            if [ "$STATUS" = "ok" ]; then
              echo "Staging 部署成功"
              exit 0
            fi
            echo "等待中... ($i/12)"
            sleep 10
          done
          echo "Staging 部署超时"
          exit 1

      - name: 运行 E2E 冒烟测试
        run: npm run test:e2e:staging

  # ── 第六阶段:部署到 Production ─────────────────────────────
  deploy-production:
    name: 部署到生产环境
    runs-on: ubuntu-latest
    needs: deploy-staging
    environment:
      name: production
      url: https://example.com
    if: github.ref == 'refs/heads/main'
    steps:
      - name: 蓝绿部署
        run: |
          curl -X POST "${{ secrets.DEPLOY_WEBHOOK_PROD }}" \
            -H "Authorization: Bearer ${{ secrets.DEPLOY_TOKEN }}" \
            -d '{
              "image": "${{ needs.build.outputs.image-tag }}",
              "strategy": "blue-green"
            }'

      - name: 生产环境健康检查
        run: |
          sleep 30
          curl --fail https://example.com/health

质量门控配置详解

覆盖率阈值(Jest)

javascript
// jest.config.ts
export default {
  coverageThreshold: {
    global: {
      branches: 75,
      functions: 80,
      lines: 80,
      statements: 80,
    },
    // 核心业务模块要求更高覆盖率
    './src/core/**/*.ts': {
      lines: 90,
    },
  },
};

质量门控汇总

检查项阈值不通过后果
ESLint error0 个阻断 PR 合并
TypeScript 类型错误0 个阻断 PR 合并
单元测试失败0 个阻断 PR 合并
代码行覆盖率≥ 80%阻断 PR 合并
npm audit 高危漏洞0 个阻断 PR 合并
密钥泄漏检测0 条阻断 PR 合并
构建产物大小增量≤ 10%发出警告

部署策略

Staging → Production 流程

feature 分支

    ▼ PR 通过所有质量门控
develop 分支 ──► 自动部署 Staging ──► 人工验收测试

    ▼ Release PR
main 分支 ──► 蓝绿部署 Production ──► 监控 15 分钟 ──► 完成 / 回滚

蓝绿部署说明

蓝绿部署维护两个相同的生产环境(蓝色=当前,绿色=新版本):

  1. 新版本部署到绿色环境,蓝色继续接收流量
  2. 健康检查通过后,负载均衡器将流量切换到绿色
  3. 观察 15 分钟,错误率和响应时间无异常则部署完成
  4. 如发现问题,将流量切回蓝色(秒级回滚)

回滚程序

自动回滚触发条件

yaml
# .github/workflows/rollback-monitor.yml
name: 生产监控与自动回滚

on:
  workflow_dispatch:
    inputs:
      target-sha:
        description: '回滚到的 commit SHA'
        required: true

jobs:
  rollback:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: 执行回滚
        run: |
          echo "回滚到版本: ${{ inputs.target-sha }}"
          curl -X POST "${{ secrets.DEPLOY_WEBHOOK_PROD }}" \
            -H "Authorization: Bearer ${{ secrets.DEPLOY_TOKEN }}" \
            -d '{"image": "ghcr.io/org/app:sha-${{ inputs.target-sha }}"}'

      - name: 通知团队
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "生产环境已回滚到 ${{ inputs.target-sha }},原因:${{ inputs.reason }}"
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

手动回滚命令(紧急情况)

bash
# 查看最近的成功部署镜像
gh run list --workflow=ci.yml --status=success --limit=5

# 触发回滚工作流
gh workflow run rollback-monitor.yml \
  -f target-sha=<上一个稳定版本的SHA>

本地开发与 CI 对齐

确保本地运行与 CI 结果一致,避免"在我机器上没问题":

json
// package.json
{
  "scripts": {
    "lint": "eslint src/",
    "type-check": "tsc --noEmit",
    "test": "jest",
    "test:coverage": "jest --coverage",
    "ci": "npm run lint && npm run type-check && npm run test:coverage",
    "ci:security": "npm audit --audit-level=high"
  }
}

在提交 PR 前,开发者应在本地运行 npm run ci 确认所有检查通过,减少 CI 失败返工成本。