Codex CLI 接入 CI/CD 流水线:GitHub Actions 自动化测试生成与代码审查实战

Codex CLI 接入 CI/CD 流水线:GitHub Actions 自动化测试生成与代码审查实战

Codex CLI 的真正生产力爆发点不是单人使用,而是集成到 CI/CD 流水线后的团队级自动化。本文演示四个经过验证的生产工作流:PR 自动代码审查、新代码自动生成测试、依赖升级后的回归分析,以及在服务器上触发大规模代码迁移任务。


一、CI 环境准备

1. 安全存储 API Key

在 GitHub 仓库设置中添加 Secret:

  1. 进入仓库 → Settings → Secrets and variables → Actions
  2. 点击「New repository secret」
  3. Name:OPENAI_API_KEY,Value:你的 API Key

对于组织级共用,推荐在 Organization Secrets 中统一管理,避免每个仓库单独配置。

2. 为 CI 创建专用 API Key

不要在 CI 中复用个人 API Key:

  • 在 OpenAI Platform 创建独立的 Project API Key,专用于 CI
  • 设置用量限制(Usage Limits),防止失控消费
  • CI Key 只应有 model inference 权限,不应有 admin 权限

3. 在 AGENTS.md 中声明 CI 专用规则

# 在项目 AGENTS.md 中添加 CI 专用约束

## CI 模式规则(当 CI=true 环境变量存在时)
- 只输出具体的代码变更和命令,不输出解释性文字
- 测试生成必须遵循现有测试文件的命名约定
- 不允许修改除测试文件以外的任何文件
- 生成的测试必须能通过 pytest 零配置运行

二、工作流一:PR 自动代码审查

每当 PR 被创建或更新时,Codex 自动分析差异并以评论形式输出审查报告。

创建 .github/workflows/codex-review.yml

name: Codex PR Code Review

on:
  pull_request:
    types: [opened, synchronize]
    paths:
      - 'app/**/*.py'      # 只在后端代码变更时触发
      - 'tests/**/*.py'

jobs:
  code-review:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write   # 需要写 PR 评论的权限

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0     # 获取完整历史,用于生成正确的 diff

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'

      - name: Install Codex CLI
        run: npm install -g @openai/codex

      - name: Get PR diff
        id: diff
        run: |
          git diff origin/${{ github.base_ref }}...HEAD \
            -- 'app/**/*.py' 'tests/**/*.py' > /tmp/pr.diff
          echo "diff_size=$(wc -l < /tmp/pr.diff)" >> $GITHUB_OUTPUT

      - name: Skip if diff is too large
        if: steps.diff.outputs.diff_size > 1000
        run: |
          echo "PR diff too large (>1000 lines), skipping Codex review to control cost."
          exit 0

      - name: Run Codex code review
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          CI: "true"
        run: |
          cat /tmp/pr.diff | codex \
            --approval-mode suggest \
            --sandbox read-only \
            --output-format json \
            "审查以下 Git diff,重点关注:
             1. 潜在的 bug 和逻辑错误
             2. 安全漏洞(SQL注入、XSS、不安全的密钥处理)
             3. 性能问题(N+1查询、缺失索引、不必要的全表扫描)
             4. 代码规范违反(参考 AGENTS.md)
             5. 缺失的错误处理
             输出格式:按问题严重程度分级(Critical/High/Medium/Low),
             每个问题注明文件名和行号,给出具体修复建议。
             如果没有发现问题,输出:LGTM" \
            > /tmp/review.txt

      - name: Post review comment
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const review = fs.readFileSync('/tmp/review.txt', 'utf8');
            
            if (review.trim() === 'LGTM') {
              await github.rest.pulls.createReview({
                owner: context.repo.owner,
                repo: context.repo.repo,
                pull_number: context.issue.number,
                event: 'APPROVE',
                body: '✅ Codex 审查通过,未发现问题。',
              });
            } else {
              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                body: `## 🤖 Codex 代码审查报告\n\n${review}\n\n---\n*由 Codex CLI 自动生成,仅供参考,最终决策以人工审查为准。*`,
              });
            }

三、工作流二:新代码自动生成单元测试

检测新增的函数,自动为没有对应测试的函数生成测试文件。

创建 .github/workflows/codex-generate-tests.yml

name: Codex Generate Missing Tests

on:
  push:
    branches: [main, develop]
    paths:
      - 'app/services/**/*.py'   # 只监控 Service 层(必须有测试的层)

jobs:
  generate-tests:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write

    steps:
      - uses: actions/checkout@v4
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

      - uses: actions/setup-node@v4
        with:
          node-version: '22'

      - run: npm install -g @openai/codex

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Detect new functions without tests
        id: detect
        run: |
          # 获取本次提交新增的 Python 函数(简化版检测)
          git diff HEAD~1 --unified=0 -- 'app/services/**/*.py' \
            | grep '^+.*def ' \
            | grep -v '^+++' \
            | sed 's/^+//' > /tmp/new_functions.txt
          
          echo "Found $(wc -l < /tmp/new_functions.txt) new function(s)" echo "has_new=$([ -s /tmp/new_functions.txt ] && echo true || echo false)" \ >> $GITHUB_OUTPUT

      - name: Generate tests with Codex
        if: steps.detect.outputs.has_new == 'true'
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          CI: "true"
        run: |
          # 获取变更的文件列表
          CHANGED_FILES=$(git diff HEAD~1 --name-only -- 'app/services/**/*.py')
          
          for file in $CHANGED_FILES; do
            module=$(basename "$file" .py)
            test_file="tests/services/test_${module}.py"
            
            echo "Generating tests for: $file → $test_file"
            
            codex \
              --approval-mode auto-edit \
              --sandbox workspace-write \
              "分析 ${file} 中本次新增的函数(参考 /tmp/new_functions.txt),
               为每个新函数生成完整的 pytest 单元测试,写入 ${test_file}。
               
               规则:
               - 如果 ${test_file} 已存在,只在末尾追加新测试,不修改已有测试
               - 每个函数至少生成3个测试:正常路径、边界条件、异常路径
               - 使用 pytest-asyncio 处理异步函数
               - 数据库操作使用 AsyncMock,不连接真实数据库
               - 测试数据使用 factory_boy 工厂类(参考 tests/factories.py 中的现有工厂)
               
               只修改测试文件,不修改被测试的源文件。"
          done

      - name: Run generated tests to verify they pass
        run: |
          pytest tests/services/ -v --tb=short -x 2>&1 | tee /tmp/test_results.txt
          
      - name: Create PR with generated tests
        if: success()
        run: |
          git config user.name "Codex Bot"
          git config user.email "codex-bot@yourcompany.com"
          
          BRANCH="codex/auto-tests-$(date +%Y%m%d-%H%M%S)"
          git checkout -b $BRANCH
          git add tests/
          git commit -m "test: 自动生成 $(date +%Y-%m-%d) 新增函数的单元测试 [skip ci]"
          git push origin $BRANCH
          
          gh pr create \
            --title "🤖 自动生成缺失的单元测试" \
            --body "$(cat /tmp/test_results.txt)" \
            --base main \
            --head $BRANCH \
            --label "automated,tests"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

四、工作流三:依赖升级影响分析

当 Dependabot 提交依赖升级 PR 时,Codex 自动分析升级的破坏性变更并给出影响评估:

name: Codex Dependency Impact Analysis

on:
  pull_request:
    paths:
      - 'requirements*.txt'
      - 'package*.json'
      - 'Pipfile'
      - 'pyproject.toml'

jobs:
  impact-analysis:
    if: github.actor == 'dependabot[bot]'
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4
        with:
          node-version: '22'

      - run: npm install -g @openai/codex

      - name: Analyze dependency changes
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: |
          # 获取依赖变更内容
          git diff origin/main...HEAD -- requirements*.txt package*.json \
            > /tmp/dep_changes.txt
          
          codex \
            --approval-mode suggest \
            --sandbox read-only \
            "分析以下依赖版本变更(/tmp/dep_changes.txt),然后扫描本项目代码库:
             
             1. 列出升级的包名和版本号
             2. 根据 CHANGELOG 或已知破坏性变更,分析是否存在 Breaking Change
             3. 搜索项目代码中使用了这些包的哪些 API
             4. 判断这些 API 在新版本中是否有变化
             5. 给出升级风险评级:Low/Medium/High
             6. 如果风险为 Medium 或 High,列出需要修改的文件和具体位置
             
             最终输出一份简洁的中文影响评估报告。" \
            > /tmp/impact_report.txt

      - name: Comment impact report on PR
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const report = fs.readFileSync('/tmp/impact_report.txt', 'utf8');
            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              body: `## 🔍 Codex 依赖升级影响分析\n\n${report}`,
            });

五、成本控制策略

在 CI 中使用 Codex 会产生 API 费用,以下是控制成本的关键措施:

策略实现方式节省比例
跳过大 diffdiff 超过阈值(如 500 行)时跳过 Codex节省 ~30%
路径过滤只在核心代码路径变更时触发节省 ~50%
限制触发分支只在 main/develop 分支或包含特定标签的 PR 上运行节省 ~40%
设置 API 用量上限在 OpenAI Platform 设置每日/每月用量 Alert防止失控
使用较小模型CI 场景使用 gpt-5-codex-mini(假设有),精度略低但成本更低节省 ~60%
缓存结果相同 diff 的审查结果缓存 24 小时避免重复调用
# 在 workflow 中加入成本控制
- name: Check diff size before running Codex
  id: check-size
  run: |
    LINES=$(git diff origin/${{ github.base_ref }}...HEAD | wc -l)
    echo "diff_lines=$LINES" >> $GITHUB_OUTPUT
    if [ "$LINES" -gt 800 ]; then
      echo "skip=true" >> $GITHUB_OUTPUT
      echo "::warning::Diff too large ($LINES lines), skipping Codex to control API cost."
    fi

- name: Run Codex (conditional)
  if: steps.check-size.outputs.skip != 'true'
  env:
    OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
  run: |
    codex ...

六、在香港VPS上运行长时任务

对于耗时超过 GitHub Actions 6 小时限制的大规模迁移任务(如全量代码迁移、大型重构),推荐在 VPS 上直接运行,配合 tmux 保持会话:

# 在 VPS 上启动持久 tmux 会话
tmux new-session -d -s codex-migration

# 在 tmux 中运行大型迁移任务
tmux send-keys -t codex-migration \
  'cd ~/my-project && codex --approval-mode full-auto \
   --sandbox workspace-write \
   "将项目中所有 datetime.utcnow() 替换为 datetime.now(timezone.utc),
    这是 Python 3.12 的弃用警告修复,需要修改所有涉及文件并更新对应测试"' \
  Enter

# 从任何地方查看任务进度
tmux attach-session -t codex-migration

# 任务完成后查看变更摘要
git diff --stat HEAD

七、总结

将 Codex CLI 集成到 CI/CD 流水线,是将 AI 编程能力从个人工具升级为团队基础设施的关键一步。PR 自动审查、测试自动生成、依赖影响分析三个工作流可以直接落地,为团队节省大量重复性的人工工作。

Telegram