本教程教你用 Claude Code 的 headless 无头模式(claude -p)搭配 GitHub Actions,从零搭一个「PR 一开就自动评审、自动贴评论」的机器人。学完你能做到三件事:在脚本里非交互地驱动 Claude、把 claude -p 包成可复现的 CI 步骤、用官方 claude-code-action 让团队成员在评论区敲一句 @claude 就能召唤评审。适合已经有 CI 基础、想把 AI 评审常态化接入研发流水线的后端/DevOps 工程师。
claude --version。本文涉及 --bare、--json-schema、流式事件等较新特性,建议直接升到最新。ANTHROPIC_API_KEY:在 console.anthropic.com 申请,注意别提交进仓库。|、>)、装了 jq(用于解析 JSON 输出)。交互式的 claude 是聊天框;headless 模式把它变成一个可以管道喂入、可以结构化吐出的命令行工具。加 -p(或 --print)即进入非交互模式:
claude -p "What does the auth module do?"
真正决定 CI 行为的是它后面那几个开关,先记住这张表:
| 开关 | 作用 | CI 里为什么关键 |
|---|---|---|
--bare |
跳过自动加载 hooks/skills/plugins/MCP/CLAUDE.md | 在干净 runner 上保证「每台机器结果一致」,将来会成为 -p 的默认 |
--output-format |
text / json / stream-json |
脚本要拿 session_id、cost 就得用 json |
--allowedTools |
自动批准工具,用权限规则语法 | 锁死能干什么,比如只允许 Bash(git diff *) |
--permission-mode |
dontAsk / acceptEdits |
dontAsk 适合锁定 CI:不在白名单里的一律拒绝 |
一条把四件事都用上的「CI 体感」命令长这样:
claude --bare -p "Review this diff for bugs" \
--output-format json \
--allowedTools "Read" \
--permission-mode dontAsk
预期结果:终端打印一段 JSON,里面有 result(正文)、session_id、total_cost_usd。整个过程不弹任何权限提示——因为 dontAsk + 只开了 Read,碰任何写操作它会直接中止。
常见问题:
- 报「需要登录 / 找不到凭证」? bare 模式跳过 OAuth 和 keychain,必须用环境变量 ANTHROPIC_API_KEY,或在 --settings 里配 apiKeyHelper。
- 想让它读项目里的 CLAUDE.md? bare 模式默认不读,需要用 --append-system-prompt-file CLAUDE.md 显式喂进去。
上 CI 之前先在本地把管道打通,能省下大量「到底哪一步炸了」的排查时间。核心思路是把 git diff 通过 stdin 喂给 Claude,这样它连 Bash 读文件的权限都不用给:
git diff main | claude -p \
'You are a senior reviewer. For each real problem in this diff,
print `file:line` then a one-line issue. Return nothing if clean.' \
--output-format json | jq -r '.result'
为什么用管道而不是让 Claude 自己 git diff?因为管道意味着 Claude 不需要 Bash 权限就能拿到 diff,权限面缩到最小——这正是 CI 安全的第一原则。
把上面这条命令存成 package.json 脚本,团队任何人本地都能复现同一套评审口径:
{
"scripts": {
"review:local": "git diff main | claude -p \"you are a typo linter. for each typo in this diff, report filename:line on one line and the issue on the next. return nothing else.\""
}
}
预期结果:跑 npm run review:local,要么空输出(干净),要么逐行列出问题。
常见问题:
- diff 太大被截断? 自 v2.1.128 起,piped stdin 上限 10MB。超了会非零退出。解法是把大 diff 写进文件,prompt 里引用路径,让 Claude 用 Read 分段读。
@claude 之所以能在 PR 里「说话」,靠的是装在仓库上的 Claude GitHub App。最快的方式是在本地 Claude Code 终端里跑:
/install-github-app
它会交互式地帮你:把 App 装到仓库 → 生成 workflow 骨架 → 把 API key 写进仓库 Secret(名字固定为 ANTHROPIC_API_KEY)。注意:
手动装的话:去 github.com/apps/claude 装 App → Settings → Secrets 里加 ANTHROPIC_API_KEY → 把官方 examples/claude.yml 拷进 .github/workflows/。
常见问题:
- 评论里 @claude 没反应? 九成是用了 /claude(斜杠)而非 @claude;或者 App 没装、workflow 没启用、Secret 没配。
@claude 召唤装好 App 后,最小可用的 workflow 就是「监听评论、识别 @claude、交给 action」:
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
claude:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
提交它,然后在任意 PR 评论里写 @claude 这个函数有竞态吗?,几秒后 Claude 就会以机器人身份回评。
升级注意(v1.0 GA 有 breaking change):如果你用的是旧的
@beta,必须改:@beta→@v1、删掉mode:、direct_prompt→prompt、max_turns/model/allowed_tools等全部搬进claude_args。下面是迁移对照:
| 旧 beta 输入 | 新 v1 输入 |
|---|---|
mode: "tag" |
(移除,自动检测) |
direct_prompt |
prompt |
custom_instructions |
claude_args: --append-system-prompt |
max_turns / model / allowed_tools |
claude_args: --max-turns / --model / --allowedTools |
常见问题: - Claude 自己 commit 后 CI 没触发? 因为 Actions 用户触发的 push 默认不跑后续 workflow。改用 GitHub App(或自建 App)身份提交即可,App 身份的 push 会正常触发 CI。
被动等 @claude 还不够省事。真正的机器人是 PR 一开 / 一更新就自动跑评审并贴评论。把触发器换成 pull_request,用 prompt 驱动:
name: PR Auto Review
on:
pull_request:
types: [opened, synchronize]
permissions:
contents: read
pull-requests: write # 要能贴评审评论
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 让 Claude 能看到完整 diff 上下文
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Review the changes in this pull request.
Focus on: correctness bugs, security, and obvious logic errors.
Post your findings as a review comment. Be concise.
claude_args: |
--max-turns 10
--model claude-sonnet-4-6
--allowedTools "Read,Bash(git diff *),Bash(git log *)"
这里有几个值得抠的点:
actions/checkout + fetch-depth: 0:浅克隆拿不到 main 分支做 diff,必须拉完整历史。claude_args 直接透传给底层 claude,所以第 1、2 步学到的 --allowedTools、--permission-mode、--bare 都能在这用。--max-turns 10 是成本刹车:防止模型在一个 PR 上无限探索烧钱。预期结果:开 PR → Actions 跑起来 → 约 30–90 秒后 PR 上出现一条机器人的评审评论,列出 0~N 条问题。
常见问题:
- 评审跑超时? 设 workflow 级 timeout-minutes,并用 --max-turns 收紧。
- 多个 PR 同时来炸 runner? 加并发控制:
yaml
concurrency:
group: pr-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
每个项目对「什么叫问题」定义不同。两件工具能让评审稳定下来:
1. 仓库根的 CLAUDE.md 写死红线,例如「禁止提交 console.log、禁止 any 类型、所有新 API 必须有单测」。Claude 评审/改码时都会读它。
2. 把评审流程做成 Skill,放在 .claude/skills/code-review/SKILL.md,然后在 workflow 里用 prompt 调用 /code-review:
- uses: actions/checkout@v4 # 跑 skill 前必须 checkout
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: "/code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
官方甚至提供了现成的 code-review 插件,每个 PR 自动评审、无需 @claude 触发:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
plugin_marketplaces: "https://github.com/anthropics/claude-code.git"
plugins: "code-review@claude-code-plugins"
prompt: "/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
常见问题:
- Skill 没生效? 仓库内 Skill 必须先 actions/checkout action 才能读到 .claude/skills/;插件 Skill 要先在 plugin_marketplaces + plugins 里装好。
纯文本评论是给人看的;如果你想拿评审结果自动打 label、卡 merge、汇总到日报,就要让 Claude 吐结构化数据。--output-format json + --json-schema 做到这件事:
claude -p "Analyze this PR diff for severity" \
--output-format json \
--json-schema '{
"type":"object",
"properties":{
"severity":{"type":"string","enum":["blocker","warning","nit"]},
"issues":{"type":"array","items":{"type":"object",
"properties":{"file":{"type":"string"},"line":{"type":"integer"},
"problem":{"type":"string"}},
"required":["file","line","problem"]}}
},
"required":["severity","issues"]
}' | jq '.structured_output'
结果落在 structured_output 字段,是一个严格符合 schema 的对象。后续脚本就能 jq -r '.structured_output.severity' 判断要不要给 PR 打 do-not-merge 标签。
此外,json 输出还带 total_cost_usd 和按模型拆分的费用——可以直接喂进你的成本看板,不用再去翻用量页。
常见问题: - schema 校验失败反复重试? 把 schema 写严(枚举、required 列全),别留模糊字段;模型越明确,单次出对的概率越高。
云端跑 AI 是会真金白银烧钱的,三道闸必须拉上:
--max-turns(默认 10)。一个 PR 最多让模型转几圈,超了强制停。timeout-minutes: 10,挡住卡死的 agent。concurrency 限制同一 PR 的重复评审,避免「推一下就重跑一遍」。还有个容易踩的坑:后台任务。如果某次 claude -p 里 Claude 启动了 dev server 之类的后台 Bash 任务,Claude 返回后这个 shell 会被约 5 秒后终止(v2.1.163 之前会无限挂住进程,导致 CI 永不结束)。后台子代理/工作流则会被等到完成,上限默认 10 分钟,可用环境变量 CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS 调整、设 0 表示不限。
把前面八步捏成一个文件 .github/workflows/pr-review.yml,提交即用。它会:开/更新 PR 时自动评审、按 schema 出结构化结果、严重程度达 blocker 就打标签、整个流程受并发与超时保护。
name: PR Auto Review
on:
pull_request:
types: [opened, synchronize]
permissions:
contents: read
pull-requests: write
concurrency:
group: pr-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
review:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run Claude review
id: claude
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Review this PR. Output JSON via your schema.
Post a human-readable summary as a review comment.
claude_args: |
--max-turns 10
--model claude-sonnet-4-6
--allowedTools "Read,Bash(git diff *),Bash(git log *)"
--append-system-prompt-file CLAUDE.md
--output-format json
- name: Label blockers
if: steps.claude.outputs.structured_output.severity == 'blocker'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr edit ${{ github.event.pull_request.number }} \
--add-label "do-not-merge"
gh pr review ${{ github.event.pull_request.number }} \
--request-changes --body "Claude 发现 blocker,请处理后再合。"
跑起来的效果:PR 一开 → runner checkout 全历史 → Claude 读 diff + CLAUDE.md 红线 → 评审评论自动贴上 → 若是 blocker,PR 被打 do-not-merge 并 request changes。整个过程无人值守,且每一步都可复现、可追溯。
flowchart TD
A[开发者开/更新 PR] --> B[GitHub 触发 workflow]
B --> C[Actions Runner 启动]
C --> D[checkout 全历史]
D --> E[claude-code-action@v1]
E --> F[claude -p headless 模式]
F --> G[读 diff + CLAUDE.md 红线]
G --> H{--output-format json}
H --> I[result 评审正文]
H --> J[structured_output 严重程度]
I --> K[贴评审评论到 PR]
J --> L{severity == blocker?}
L -- 是 --> M[打 do-not-merge 标签<br/>request changes]
L -- 否 --> N[流程结束]
M --> N
K --> N
--bare、--allowedTools、concurrency 各自挡住了哪类风险。on: schedule)。-p/--bare/--output-format/--json-schema 等核心参数与流式事件的权威说明。claude-code-action@v1 的安装、参数、v1.0 GA 迁移与 Bedrock/Vertex 接法。stream-json 全事件类型与字段的最全第三方参考-p/--output-format 无头管道的实战指南auto/dontAsk/bypassPermissions 选型决策框架[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
