让 Claude 半夜自己审 PR:Headless 模式 + GitHub Actions 实测

让 Claude 半夜自己审 PR:Headless 模式 + GitHub Actions 实测 一、问题交互式 Claude 没法挂到流水线上人工 Code Review 跟不上 PR 速度交互式调用 Claude 又没法自动化怎么办我们团队前阵子被这事卡住——夜里合了十几条 PR第二天早会一堆谁来 Review的尴尬。顺便一提我维护的雷达鸭 App收录中国一人公司赚钱案例华为应用市场 微信小程序Uni-app arkTS UniCloud攒到一定体量后PR 审查负担也压上来了这才逼着我去翻 Claude Code 的 Headless 模式。核心转变一句话把人确认换成参数约束。交互式下工具要不要执行、文件要不要改都是人按回车Headless 下所有决策都靠参数预先约束好Claude 自己跑完输出结果。最朴素的调用# 最基本的 Headless 调用claude-p分析 src/ 目录的安全漏洞# 等价的完整形式claude--print分析 src/ 目录的安全漏洞-p就是--print的短写跑完直接退出不进交互式 REPL。这就是无人值守的起点。二、4 维参数把交互式的确认翻译成约束Headless 不是简单加个-p而是要把交互式里那些隐式约定全显式化。Claude Code 给了 4 个维度# 维度一输出格式claude-p列出主要问题--output-format text# 纯文本人看claude-p审查 PR--output-format json# 结构化机器解析claude-p分析代码--output-format stream-json# 流式实时监控# 维度二成本与规模护栏claude-p审查代码--max-turns5# 最多 5 轮工具调用claude-p分析日志--max-budget-usd0.50# 单次硬上限 0.5 美元# 维度三安全边界claude-p审查代码变更--allowedToolsRead,Grep,Glob# 只读最安全claude-p生成文档--disallowedToolsBash# 黑名单禁 Bashclaude-p检查 git 历史\--allowedToolsRead,Grep,Glob,Bash(git log *),Bash(git diff *)# 细粒度白名单# 维度四执行控制claude-p检查代码格式--modelclaude-haiku-4-5 claude-p安全漏洞分析--modelclaude-sonnet-4-6 claude-p代码审查--modelclaude-sonnet-4-6 --fallback-model haiku# 主模型过载自动切text输出适合人看json给 CI 后续步骤消费stream-json一行一个事件适合长任务实时盯。我们做 PR 审查统一用json因为后面要用jq提取result字段、用total_cost_usd算账。json输出长这样{type:result,subtype:success,session_id:550e8400-e29b-41d4-a716-446655440000,is_error:false,duration_ms:12345,num_turns:5,total_cost_usd:0.0342,usage:{input_tokens:5000,output_tokens:1500,cache_read_input_tokens:3000},result:发现 2 个 Critical 问题\n1. SQL 注入风险...}session_id留着后面会话管理用total_cost_usd直接是这次审查花了几美分。我们跑了两周平均一条 PR 审查成本在 0.03~0.08 美元之间——比之前请同事加班喝的咖啡便宜多了。三、Unix 管道Claude 当命令行工具用Headless 模式下 Claude 就是个标准 stdin/stdout 程序能塞进任何 Unix 管道# 分析日志文件catserver.log|claude-p找出所有 500 错误并总结根因# 分析 git 变更gitdiffHEAD~1|claude-p总结这次提交的变更格式为 Conventional Commits# Claude 分析 → jq 提取 → 发邮件claude-p检查是否有安全漏洞--output-format json|\jq-r.result|\mail-s安全扫描报告securitycompany.com# grep 预过滤 → Claude 深度分析grep-rTODOsrc/|claude-p将这些 TODO 按优先级分类末尾那个例子是我们日常在用的——先grep把范围收窄再喂给 Claude 分类比让 Claude 自己Grep整个仓库省 token 也省时间。四、GitHub Actions 两种接法官方给了两条路。方式一官方 Action起步首选# .github/workflows/claude-review.ymlname:Claude PR Reviewon:pull_request:types:[opened,synchronize,reopened]issue_comment:types:[created]# 同一 PR 的新提交取消旧的运行——避免浪费concurrency:group:${{github.workflow}}-${{github.event.pull_request.number}}cancel-in-progress:truejobs:review:runs-on:ubuntu-latestif:|github.event_name pull_request || contains(github.event.comment.body, claude)permissions:contents:readpull-requests:writeissues:writesteps:-uses:actions/checkoutv4with:fetch-depth:0# 完整历史用于 diff 分析-uses:anthropics/claude-code-actionv1with:anthropic_api_key:${{secrets.ANTHROPIC_API_KEY}}prompt:|审查这个 PR 的代码变更重点检查 1. 安全漏洞SQL 注入、XSS、敏感信息硬编码 2. 未处理的错误情况和边界条件 3. 性能问题N1 查询、内存泄漏风险claude_args:---allowedTools Read,Grep,Glob--max-turns 10--model claude-sonnet-4-6十几行 YAML 跑起来PR 一开自动评论。concurrency那段必加——同一 PR 连推几个 commit旧的运行会被自动取消不然账单会很难看。方式二直接 CLI精细控制需要自己处理变更文件列表、自己写评论、自己卡门禁的场景用直接 CLIname:Advanced AI Reviewon:pull_request:types:[opened,synchronize]paths:[src/**]# 只在 src 目录变更时运行concurrency:group:${{github.workflow}}-${{github.event.pull_request.number}}cancel-in-progress:truejobs:review:runs-on:ubuntu-latestpermissions:contents:readpull-requests:writesteps:-uses:actions/checkoutv4with:fetch-depth:0-uses:actions/setup-nodev4with:node-version:20-name:Install Claude Coderun:npm install-g anthropic-ai/claude-code-name:Get changed filesid:changedrun:|FILES$(git diff --name-only origin/${{ github.base_ref }}...HEAD) echo files$(echo $FILES | tr \n ) $GITHUB_OUTPUT echo count$(echo $FILES | wc -l) $GITHUB_OUTPUT-name:Run Claude Reviewid:reviewenv:ANTHROPIC_API_KEY:${{secrets.ANTHROPIC_API_KEY}}CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:1run:|claude -p 审查以下 PR 变更文件 ${{ steps.changed.outputs.files }}重点检查安全漏洞、逻辑错误、性能问题。 对每个问题给出文件名和行号。 \--output-format json \--max-turns 10 \--max-budget-usd 0.50 \--model claude-sonnet-4-6 \--allowedTools Read,Grep,Globreview.json RESULT$(jq-r .result review.json) COST$(jq-r .total_cost_usd review.json) echo cost$COST$GITHUB_OUTPUT echo resultEOF$GITHUB_OUTPUT echo $RESULT$GITHUB_OUTPUT echo EOF$GITHUB_OUTPUT-name:Post Review Commentuses:actions/github-scriptv7with:script:|const cost ${{ steps.review.outputs.cost }}; const result ${{ steps.review.outputs.result }}; await github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: ## Claude Code Review\n\n${result}\n\n---\n*Cost: $${cost} | Automated by Claude Code* });-name:Gate on critical issuesrun:|RESULT${{ steps.review.outputs.result }} if echo $RESULT | grep -qi critical; then echo ::error::Critical issues found exit 1 fi末尾那段Gate on critical issues是真有用的——评审里出现critical字样直接让 CI 失败比让人盯着看评论靠谱。两种方式的取舍起步用方式一几分钟接上要卡门禁、要拿total_cost_usd做账、要把结果写到外部系统就上方式二。我们团队最终留了方式二因为要把每次审查成本塞进 Grafana。五、多阶段 CI 管道分析 → 修复 → 验证单步审查只是开始。真正省事的是把 Claude 拆成多个 job按阶段跑、按模型分级name:Multi-Stage AI Pipelineon:pull_request:types:[opened,synchronize]jobs:# 阶段一快速格式检查用便宜的模型lint-check:runs-on:ubuntu-lateststeps:-uses:actions/checkoutv4-name:Format Checkenv:ANTHROPIC_API_KEY:${{secrets.ANTHROPIC_API_KEY}}CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:1run:|npx anthropic-ai/claude-code -p 检查 src/ 下的代码是否符合项目编码规范 \ --model claude-haiku-4-5 \ --max-turns 3 \ --max-budget-usd 0.05 \ --allowedTools Read,Grep,Glob \ --output-format text# 阶段二深度安全审查用最强模型security-scan:runs-on:ubuntu-latestneeds:lint-check# 格式检查通过后再做安全扫描steps:-uses:actions/checkoutv4-name:Security Scanenv:ANTHROPIC_API_KEY:${{secrets.ANTHROPIC_API_KEY}}CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:1run:|npx anthropic-ai/claude-code -p 深度安全扫描重点检查 OWASP Top 10 \ --model claude-sonnet-4-6 \ --append-system-prompt 你是应用安全专家只关注安全漏洞。 \ --max-turns 10 \ --max-budget-usd 1.00 \ --allowedTools Read,Grep,Glob \ --output-format json \ --json-schema {type:object,properties:{vulnerabilities:{type:array,items:{type:object,properties:{severity:{type:string},description:{type:string},file:{type:string}},required:[severity,description]}}},required:[vulnerabilities]} \ security-report.json阶段一用 Haiku便宜、快阶段二才上 Sonnet贵、准。needs: lint-check让安全扫描等格式检查通过再跑——格式都过不了的 PR 没必要烧 Sonnet 的钱。这种分级模型 阶段依赖组合下来我们一条 PR 的平均成本从单一 Sonnet 全量审查的 ~0.15 美元压到了 ~0.06 美元。六、流式输出和会话管理流式输出盯长任务stream-json一行一个事件配while read能实时看 Claude 在干啥claude-p分析整个 src/ 目录的架构问题\--output-format stream-json\--allowedToolsRead,Grep,Glob|whileread-rline;doTYPE$(echo$line|jq-r.type // )case$TYPEinsystem)echo[INIT] 模型:$(echo$line|jq-r.model // unknown);;assistant)TEXT$(echo$line|jq-r.message.content[0].text // );[-n$TEXT]echo[CLAUDE]$TEXT;;result)COST$(echo$line|jq-r.total_cost_usd // 0);TURNS$(echo$line|jq-r.num_turns // 0);echo[DONE] 完成$TURNS轮费用: \$$COST;;esacdone我们把这个管道接到 CI 日志里长任务不再是黑盒等 5 分钟——能实时看到 Claude 读到哪个文件、下了什么结论。会话管理跨步骤复用上下文多阶段管道每个 job 是独立容器默认上下文丢光。--resume接session_id能把上一轮分析接上# 第一次调用分析代码结构RESULT$(claude-p分析 src/ 的模块依赖关系\--output-format json\--allowedToolsRead,Grep,Glob)# 提取 session_idSESSION_ID$(echo$RESULT|jq-r.session_id)# 第二次调用在上一轮分析的基础上继续claude-p基于刚才的分析指出循环依赖问题\--resume$SESSION_ID\--output-format text# 继续当前目录最近的会话不需要知道 session_idclaude-p继续刚才的分析--continue# 创建分支会话保留原始会话不变claude-p如果改用微服务架构呢--resume$SESSION_ID--fork-session# 禁用会话持久化适合完全无状态的 CI 场景claude-p一次性分析--no-session-persistence--continue不用记session_id直接续最近的--fork-session适合假设性提问——分支会话不影响主线。完全无状态的 CI 用--no-session-persistence跑完不落盘。七、五层安全防线让 Claude 半夜自己跑最让人睡不着的就是它会不会偷偷rm -rf。书里给的五层防线我们一层没少层一最小权限——--allowedTools把工具收成白名单审查任务只给Read,Grep,Glob连Edit都不给。层二Secrets 管理——API Key 一律走${{ secrets.ANTHROPIC_API_KEY }}绝不硬编码。硬编码会原样出现在 Actions 日志里谁都能看。层三容器隔离——只读文件系统 无网络jobs:review:runs-on:ubuntu-latestcontainer:image:node:20options:--read-only--tmpfs /tmp--network none--network none直接断网Claude 想外传数据也没出口。层四成本防护——路径过滤 并发限制 单次预算兜底on:pull_request:paths:-src/**-!src/**/*.test.*# 排除测试文件concurrency:group:claude-${{github.event.pull_request.number}}cancel-in-progress:true加上--max-budget-usd 0.50单次跑超 0.5 美元自动停。层五审计日志——JSON 输出天然带审计字段tee落盘claude-p任务--output-format json|tee-a/var/log/claude-audit.jsonl事后谁、什么时候、花了多少钱、跑了多少轮全在日志里。八、CI 环境变量与完整模板CI 里这几个环境变量必设ANTHROPIC_API_KEYsk-ant-...# API 密钥CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC1# 一个变量关掉自动更新、遥测、错误上报DISABLE_AUTOUPDATER1# 禁用自动更新CI 中必须DISABLE_TELEMETRY1# 禁用遥测DISABLE_ERROR_REPORTING1# 禁用错误上报BASH_DEFAULT_TIMEOUT_MS120000# Bash 默认超时 2 分钟CLAUDE_CODE_MAX_OUTPUT_TOKENS32000# 最大输出 tokenCLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC1这个一定要加——不然 CI 跑着跑着 Claude 自己开始检查更新日志里一堆噪音。一个能直接抄的完整模板-name:Run Claude Analysisenv:ANTHROPIC_API_KEY:${{secrets.ANTHROPIC_API_KEY}}CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:1BASH_DEFAULT_TIMEOUT_MS:300000run:|npx anthropic-ai/claude-code -p $PROMPT \ --output-format json \ --max-turns 10 \ --max-budget-usd 1.00 \ --allowedTools Read,Grep,Glob \ --model claude-sonnet-4-6 \ --fallback-model haiku \ --append-system-prompt 遵循项目 CLAUDE.md 中的审查规范。--fallback-model haiku是个容易被忽略的小细节——主模型过载时自动切 Haiku不至于让整条管道挂在 429 上。九、判断跑了两周我们的结论是Headless 模式不是凑合用的替代品是 CI 集成的正解。交互式适合人盯着改代码Headless 适合挂流水线——两个场景两套约束逻辑。反正我们以后的 PR 审查默认走 Claude GitHub Actions人只看 critical 项。半夜合的 PR第二天早会前报告已经躺在评论里了。关于作者雷达鸭 App 独立开发者10 年软件开发经验软件设计师、人工智能应用工程师专注鸿蒙 ArkTS Web 前端探索 AI 自动化。版权声明本文采用 MIT 协议开源转载请注明出处。