Claude Code CI/CD 集成实战:用 GitHub Actions 打造 7x24 小时自动代码审查与修复流水线
Claude Code CI/CD 集成实战:用 GitHub Actions 打造 7x24 小时自动代码审查与修复流水线
当 PR 积压到第三天,你还相信"代码审查是质量守护者"吗?本文带你用 Claude Code + GitHub Actions 搭建一条能自动审查、自动修复、自动汇报的流水线,让 AI 先把机械性问题挡在门外,让人工聚焦真正重要的架构决策。
一、引言:为什么 CI/CD 里需要 AI 代码审查
代码审查是工程团队公认的质量防线,但在实际执行中,它往往沦为瓶颈:
- 审查者时间稀缺:资深工程师的日历被会议塞满,PR 常常排队等待一两天才有人看。
- 标准飘忽不定:同一个团队的审查尺度因人而异,新人无所适从。
- 机械问题消耗精力:命名规范、空指针风险、未处理的异常、明显的逻辑漏洞……这些本可被规则拦截的问题,却反复占用人工讨论时间。
- 跨时区协作延迟:全球化团队中,"等待审查"可能意味着等一个完整的工作日。
把 AI 代码审查嵌入 CI/CD 流水线,并不是要取代人工,而是重新分工:让 AI 在 PR 提交的几秒内完成第一轮筛查,过滤掉机械性问题、给出修复建议,甚至直接提交修复补丁;让人工审查者把注意力留给架构设计、业务正确性和边界场景。
为什么选 Claude Code?
- 它是命令行原生工具,天然适合无头(headless)的 CI 环境。
- 具备强大的代码理解与多文件推理能力,能结合上下文给出有依据的建议。
- 支持 JSON 结构化输出,便于解析后生成报告、评论 PR。
- 可通过
--allowedTools、--max-turns等参数精细控制行为边界,安全可控。 - 原生提供 GitHub Action 集成,也支持直接调用 CLI,灵活度极高。
本文将采用"直接调用 CLI + 自定义脚本"的方式,因为它对审查逻辑、报告格式和成本控制的掌控力最强,最适合学习与定制。
二、环境准备
2.1 前置条件
| 依赖 | 版本要求 | 说明 |
|------|---------|------|
| GitHub 仓库 | - | 需开启 Actions 功能 |
| Anthropic API Key | - | 在 console.anthropic.com 创建,注意设置用量上限 |
| Node.js | 18+ | Claude Code CLI 依赖 |
| Python | 3.10+ | 审查脚本运行环境(标准库即可,无需额外安装) |
2.2 配置仓库 Secrets
进入 GitHub 仓库 → Settings → Secrets and variables → Actions → New repository secret,添加:
ANTHROPIC_API_KEY:你的 Anthropic API 密钥。GITHUB_TOKEN:无需手动创建,Actions 运行时自动注入,但需要在 workflow 中声明permissions。
2.3 本地验证 Claude Code CLI
在接入 CI 前,先在本地确认 CLI 可用:
# 安装 Claude Code CLI
npm install -g @anthropic-ai/claude-code
# 设置 API Key(本地测试用,CI 中走 Secret)
export ANTHROPIC_API_KEY="sk-ant-xxxxx"
# 冒烟测试:以 JSON 格式输出
claude -p "用一句话解释什么是单调函数" --output-format json --max-turns 1
若终端返回一段包含 result、cost_usd 字段的 JSON,说明环境就绪。
三、GitHub Actions Workflow 配置
下面是一份可直接使用的完整 workflow。它会在 PR 打开或更新时触发,安装依赖、跑审查脚本、把结果作为评论贴回 PR。
# .github/workflows/claude-review.yml
name: Claude Code Review
on:
pull_request:
types: [opened, synchronize, reopened]
# 可选:只审查特定路径,节省成本
# paths:
# - "src/**"
# - "*.py"
workflow_dispatch: # 支持手动触发,便于调试
# 限制同一 PR 的并发运行,避免重复计费
concurrency:
group: claude-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
review:
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read # 读取代码
pull-requests: write # 发表 PR 评论
issues: write # 评论本质上走 issue API
steps:
- name: 检出代码
uses: actions/checkout@v4
with:
fetch-depth: 0 # 完整历史,确保 diff 计算准确
- name: 安装 Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: 缓存 Claude Code CLI(加速安装、省时)
uses: actions/cache@v4
with:
path: ~/.npm-global
key: claude-code-cli-${{ runner.os }}-v1
restore-keys: |
claude-code-cli-${{ runner.os }}-
- name: 安装 Claude Code CLI
run: npm install -g @anthropic-ai/claude-code
- name: 设置 Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: 执行 AI 代码审查
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
REPO: ${{ github.repository }}
run: python scripts/claude_review.py
- name: 上传审查报告为构件(便于归档追溯)
if: always()
uses: actions/upload-artifact@v4
with:
name: claude-review-report
path: review_report.json
retention-days: 14
关键设计说明:
concurrency+cancel-in-progress:同一 PR 多次推送时,取消旧的审查任务,避免重复消耗 API 额度。fetch-depth: 0:拿到完整 git 历史,git diff才能正确对比 base 与 head。permissions遵循最小权限原则,只给pull-requests: write用于评论。- 缓存 npm 全局目录,加速后续运行。
四、Claude Code 自动审查脚本编写
这是整条流水线的核心。脚本完成三件事:取 diff → 调 Claude → 解析结果。代码使用 Python 标准库,无需安装第三方包。
#!/usr/bin/env python3
# scripts/claude_review.py
"""调用 Claude Code CLI 对 PR 变更进行自动审查,并输出结构化报告。"""
import json
import os
import subprocess
import sys
import urllib.request
# ---------- 1. 获取代码变更 ----------
def get_diff(base_sha: str, head_sha: str) -> str:
"""计算 base...head 之间的 diff,并做大小截断以控制成本。"""
result = subprocess.run(
["git", "diff", f"{base_sha}...{head_sha}", "--unified=3"],
capture_output=True, text=True, check=True,
)
diff = result.stdout
# 单次审查的 diff 上限:约 3 万字符,超出则截断并提示
MAX_DIFF_CHARS = 30000
if len(diff) > MAX_DIFF_CHARS:
diff = diff[:MAX_DIFF_CHARS] + "\n...(diff 过长已截断,请分批审查)\n"
return diff
# ---------- 2. 调用 Claude Code 审查 ----------
def run_claude_review(diff: str) -> dict:
"""构造提示词并调用 claude CLI,返回解析后的 JSON。"""
prompt = f"""你是一位严谨的资深代码审查专家。请审查下面的 Git diff,重点关注:
1. 潜在 Bug(空指针、越界、并发问题、资源泄漏)
2. 安全风险(注入、硬编码密钥、不安全的反序列化)
3. 性能隐患(N+1 查询、不必要的循环、内存浪费)
4. 可维护性(命名、复杂度、重复代码)
请严格只输出一个 JSON 对象,不要任何额外文字:
{{
"summary": "一句话总体评价",
"score": 1到10的整数,
"issues": [
{{
"severity": "high | medium | low",
"file": "文件路径",
"line": 行号或0,
"description": "问题描述",
"suggestion": "修复建议"
}}
]
}}
待审查的 diff:
{diff}
"""
result = subprocess.run(
[
"claude", "-p", prompt,
"--output-format", "json",
"--max-turns", "1", # 单轮,避免越界操作
],
capture_output=True, text=True, check=True,
)
# claude --output-format json 返回的顶层结构包含 result 字段
wrapper = json.loads(result.stdout)
content = wrapper.get("result", "{}")
# 去除模型可能包裹的 ```json 代码块标记
content = content.strip()
if content.startswith("```"):
content = content.split("```")[1]
if content.startswith("json"):
content = content[4:]
return json.loads(content)
# ---------- 3. 解析并落盘报告 ----------
def save_report(review: dict, wrapper: dict) -> None:
"""把审查结果与成本信息一并写入 review_report.json。"""
report = {
"review": review,
"meta": {
"cost_usd": wrapper.get("cost_usd"),
"duration_ms": wrapper.get("duration_ms"),
"num_turns": wrapper.get("num_turns"),
},
}
with open("review_report.json", "w", encoding="utf-8") as f:
json.dump(report, f, ensure_ascii=False, indent=2)
# ---------- 4. 主流程 ----------
def main() -> None:
base_sha = os.environ["BASE_SHA"]
head_sha = os.environ["HEAD_SHA"]
diff = get_diff(base_sha, head_sha)
if not diff.strip():
print("没有检测到代码变更,跳过审查。")
return
wrapper = run_claude_review_wrapper(diff)
review = json.loads(wrapper.get("result", "{}"))
save_report(review, wrapper)
print("审查完成,报告已写入 review_report.json")
def run_claude_review_wrapper(diff: str) -> dict:
"""封装一次调用,返回 claude 的完整 JSON 包装。"""
prompt = f"""你是一位严谨的资深代码审查专家。请审查下面的 Git diff,重点关注:
1. 潜在 Bug
2. 安全风险
3. 性能隐患
4. 可维护性
请严格只输出一个 JSON 对象:
{{"summary":"一句话评价","score":1到10,"issues":[{{"severity":"high|medium|low","file":"路径","line":0,"description":"问题","suggestion":"建议"}}]}}
待审查的 diff:
{diff}
"""
result = subprocess.run(
["claude", "-p", prompt, "--output-format", "json", "--max-turns", "1"],
capture_output=True, text=True, check=True,
)
return json.loads(result.stdout)
if __name__ == "__main__":
main()
说明:上面的
run_claude_review与run_claude_review_wrapper在真实项目里可合并为一个函数。这里拆开是为了让"解析模型输出"和"获取原始包装"两步职责清晰,便于你按需裁剪。
五、自动修复流程
审查只是第一步,更有价值的是让 Claude 直接修复简单问题。我们通过一个独立的脚本 + workflow 步骤实现:给 Claude 授予文件编辑能力,让它改完代码后由 CI 自动提交一个修复 commit。
首先,新增一个修复脚本:
#!/usr/bin/env bash
# scripts/claude_autofix.sh
# 让 Claude Code 自动修复当前工作区的简单问题,并生成提交。
set -euo pipefail
BASE_SHA="${BASE_SHA}"
HEAD_SHA="${HEAD_SHA}"
# 取出变更文件清单,只让 Claude 处理本次改动的文件
FILES=$(git diff --name-only "${BASE_SHA}...${HEAD_SHA}" | grep -E '\.(py|js|ts|go|java)$' || true)
if [ -z "$FILES" ]; then
echo "没有可自动修复的源码文件,退出。"
exit 0
fi
# 调用 Claude,允许其使用文件读写与 bash 工具来完成修复
claude -p "以下文件在最近的提交中发生了变更,请逐一检查并修复其中的明显问题(如空指针、未处理异常、明显的逻辑错误)。只修改必要的部分,不要重写整段逻辑。修改完成后,简要说明你做了哪些改动。涉及文件:
$FILES" \
--output-format json \
--max-turns 8 \
--allowedTools "Read,Edit,Bash(git diff:*)"
# 将 Claude 的修改提交为一个 fixup commit
git config user.name "claude-code-bot"
git config user.email "bot@claude.local"
git add -A
if git diff --cached --quiet; then
echo "Claude 未产生任何修改。"
else
git commit -m "fix: Claude Code 自动修复(基于 AI 审查建议)"
echo "已生成自动修复提交。"
fi
然后在 workflow 中新增一个条件触发的修复 job(注意与审查 job 分离,避免互相影响):
autofix:
needs: review
runs-on: ubuntu-latest
# 仅当审查报告里存在 high 级别问题时才自动修复(示例:可由前置 job 输出决定)
if: github.event.action == 'opened'
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm install -g @anthropic-ai/claude-code
- name: 运行自动修复
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: bash scripts/claude_autofix.sh
- name: 推送修复提交
run: git push origin HEAD:${{ github.head_ref }}
安全提醒: 自动修复会直接 push 到分支。生产环境建议改为推到一个新的修复分支并自动开启 PR,由人工确认后再合并,避免 AI 直接污染主开发分支。
六、审查报告生成与 PR 评论
审查结果只有贴回 PR 才对开发者有用。下面的脚本读取 review_report.json,格式化为 Markdown,并通过 GitHub API 评论到 PR 上。
#!/usr/bin/env python3
# scripts/post_review_comment.py
"""读取审查报告,格式化为 Markdown 并评论到对应 PR。"""
import json
import os
import urllib.request
SEVERITY_EMOJI = {"high": "🔴", "medium": "🟡", "low": "🟢"}
def format_markdown(review: dict, meta: dict) -> str:
lines = ["## 🤖 Claude Code 自动审查报告", ""]
lines.append(f"**总体评价**:{review.get('summary', '无')}")
lines.append(f"**质量评分**:{review.get('score', '-')}/10")
lines.append(f"**本次成本**:${meta.get('cost_usd', 0):.4f} | "
f"耗时 {meta.get('duration_ms', 0)} ms")
lines.append("")
issues = review.get("issues", [])
if not issues:
lines.append("✅ 未发现明显问题,代码看起来不错!")
return "\n".join(lines)
lines.append(f"共发现 **{len(issues)}** 个潜在问题:\n")
for i, issue in enumerate(issues, 1):
sev = issue.get("severity", "low")
icon = SEVERITY_EMOJI.get(sev, "⚪")
lines.append(f"### {i}. {icon} [{sev.upper()}] "
f"{issue.get('file', '?')}:{issue.get('line', '?')}")
lines.append(f"- **问题**:{issue.get('description', '')}")
lines.append(f"- **建议**:{issue.get('suggestion', '')}")
lines.append("")
lines.append("---")
lines.append("_由 Claude Code + GitHub Actions 自动生成,仅供参考。_")
return "\n".join(lines)
def post_comment(repo: str, pr_number: str, body: str, token: str) -> None:
url = f"https://api.github.com/repos/{repo}/issues/{pr_number}/comments"
data = json.dumps({"body": body}).encode("utf-8")
req = urllib.request.Request(
url, data=data, method="POST",
headers={
"Authorization": f"token {token}",
"Accept": "application/vnd.github.v3+json",
"Content-Type": "application/json",
},
)
with urllib.request.urlopen(req) as resp:
print(f"评论发布状态:{resp.status}")
def main() -> None:
with open("review_report.json", encoding="utf-8") as f:
report = json.load(f)
body = format_markdown(report["review"], report.get("meta", {}))
post_comment(
repo=os.environ["REPO"],
pr_number=os.environ["PR_NUMBER"],
body=body,
token=os.environ["GITHUB_TOKEN"],
)
if __name__ == "__main__":
main()
在 workflow 的审查 job 末尾追加一步即可调用它:
- name: 将报告评论到 PR
if: always()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: python scripts/post_review_comment.py
运行后,PR 上会出现一条带严重等级、文件定位和修复建议的评论,开发者可以直接对照修改。
七、性能优化与成本控制
AI 审查按 token 计费,如果不加控制,一个活跃仓库的月度账单可能令人吃惊。以下是几条经过验证的策略。
7.1 路径过滤,只审查真正的代码
在 workflow 触发条件中用 paths 过滤,避免文档、配置变更触发无意义的审查:
on:
pull_request:
paths:
- "src/**"
- "tests/**"
- "!**/*.md"
7.2 diff 截断与分批
超大型 PR 一次性喂给模型既贵又容易超出上下文。前文脚本中的 MAX_DIFF_CHARS 截断是第一道防线;更成熟的做法是按文件分批调用,每批控制在合理 token 量内。
7.3 模型分级:简单问题用小模型
Claude Code 支持通过环境变量指定模型。对于纯格式、命名类问题,可切换到更轻量、更便宜的模型:
- name: 执行 AI 代码审查(轻量任务用 Haiku)
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_MODEL: "claude-haiku-4-5" # 成本显著低于 Sonnet/Opus
run: python scripts/claude_review.py
7.4 缓存依赖,减少安装耗时
前文已用 actions/cache 缓存 npm 全局目录。你也可以缓存 ~/.cache/claude 等运行时缓存目录,进一步缩短冷启动。
7.5 并发去重 + 成本看板
concurrency.cancel-in-progress已能避免同一 PR 重复计费。- 建议把
review_report.json中的cost_usd聚合到一个简单的成本看板(可用 GitHub Actions 的workflow_run把每日成本写入一个 issue 或外部表),及时发现异常消耗。
7.6 限制工具与轮次
审查任务用 --max-turns 1 且不授予写工具;修复任务才放开 Edit 并设 --max-turns 8。这样既省钱又安全——审查阶段不会发生意料之外的文件改动。
八、常见问题 FAQ
Q1:Claude Code 审查经常"幻觉"出根本不存在的问题,怎么办?
这是 LLM 审查的通病。缓解方法:(1) 在提示词里强制要求"只针对 diff 中实际出现的代码评论,不得臆测未展示的上下文";(2) 要求输出 JSON 时附带 file 和 line,并在评论脚本里校验该行号是否真实存在于 diff 中,过滤掉定位不到的条目;(3) 把严重级别阈值调高,只对 high 级别问题强提醒,medium/low 仅作参考。
Q2:workflow 报错 claude: command not found,但本地明明能跑。
CI 环境与本地不同。请确认:(1) setup-node 步骤在 npm install -g 之前执行;(2) 全局 npm bin 目录已加入 PATH,必要时显式 echo "$(npm config get prefix)/bin" >> $GITHUB_PATH;(3) 缓存键命中后,确认缓存的二进制确实兼容当前 runner 架构。
Q3:API 费用增长很快,怎么快速定位是哪个环节烧钱?
每条审查都会在 review_report.json 里记录 cost_usd。短期可以下载 artifact 人工汇总;长期建议在 workflow 末尾把单次成本通过 curl 上报到一个简单的统计服务(或写入一个专用 issue 的 comment),形成趋势图。同时开启路径过滤、diff 截断、小模型分级三件套,通常可降低 60% 以上成本。
Q4:自动修复把代码改坏了,如何回滚和预防?
首先,自动修复提交都是独立的 commit,git revert 即可回滚。预防层面:(1) 不要让 autofix 直接 push 到主开发分支,改为推到独立分支并开 PR;(2) 在修复提示词里明确"只修复确定性问题,遇到歧义保持原样";(3) 给 autofix job 配置必需的 CI 检查门禁,修复 PR 必须通过测试才能合并。
Q5:审查评论每次推送都刷屏,能否更新而不是追加?
可以。在 post_review_comment.py 中,先调用 GET /repos/{repo}/issues/{pr}/comments,查找上一条以"🤖 Claude Code 自动审查报告"开头的评论。若存在,改用 PATCH /repos/{repo}/issues/comments/{comment_id} 更新它;否则才 POST 新评论。这样每个 PR 始终只有一条最新的审查评论,干净清爽。
Q6:私有仓库能直接用官方 anthropics/claude-code-action 吗,和我自己写脚本有何区别?
能用,官方 Action 封装了认证、触发、PR 评论等常用能力,适合快速接入。自己写脚本的优势在于:完全掌控提示词、报告格式、成本统计与修复逻辑,便于和团队既有规范深度集成。建议先用官方 Action 跑通链路,再按需迁移到自定义脚本。
Q7:Claude Code 能审查非英文注释或中文命名的代码吗?
可以。Claude 对中文理解良好,审查中文注释和中文变量名没有障碍。若团队代码以中文为主,建议在提示词里注明"请用中文输出审查报告",并确保 review_report.json 以 UTF-8 写入(脚本中已设置 encoding="utf-8"),避免评论出现乱码。
九、总结
把 Claude Code 嵌入 GitHub Actions,本质上是给 CI/CD 流水线装上了一个"永不疲倦的初级审查员"。它的价值不在于替代人工判断,而在于:
- 把响应时间从"小时级"压缩到"秒级",PR 提交即出报告。
- 把机械问题挡在人工之前,让审查者聚焦架构与业务。
- 闭环到修复,对确定性问题自动生成补丁,缩短"发现→修复"的链路。
本文给出的方案是一条可复制、可裁剪的基线:workflow YAML 负责编排,Python 脚本负责审查与报告,Bash 脚本负责修复,缓存与分级策略负责控成本。落地时,建议从"仅审查、不自动修复"起步,观察一段时间的报告质量与误报率,再逐步放开自动修复的权限边界。
AI 代码审查不是银弹,但当它和人工审查形成互补时,团队就能在质量与速度之间找到一个更舒服的平衡点。现在,就去你的仓库里加上第一个 .github/workflows/claude-review.yml 吧。