Claude Code 批量 PR Review 与 CI 集成 — --bare 模式、管道输入与 stream-json 流式输出
简介
当你需要将 Claude Code 集成到 CI/CD 流水线中,或者需要一次性审查数十个 Pull Request 时,交互式的 TUI 模式就不再适用了。
--bare模式、管道输入和stream-json流式输出功能让 Claude Code 可以像命令行工具一样被脚本化调用,完美适应自动化场景。
想象一下这样的场景:你的团队每天有 50 个 PR 需要审查,手动审查不仅耗时,而且容易遗漏问题。通过 Claude Code 的批量处理能力,你可以在 CI 流水线中自动触发代码审查,生成结构化的审查报告,并将结果推送到 PR 评论中——整个过程无需人工干预。
本文将深入讲解三个核心功能:--bare 模式的无头运行方式、管道输入的处理机制、stream-json 的结构化输出格式,以及如何将它们组合起来构建完整的自动化 PR 审查流水线。
目录
- 一、bare 模式详解
- 二、管道输入机制
- 三、stream-json 流式输出
- 四、批量 PR 审查脚本
- 五、CI/CD 集成
- 六、GitHub Actions 实战
- 七、性能优化
- 八、总结
- 九、下篇预告
一、bare 模式详解
1.1 什么是 bare 模式?
bare 模式(无头模式)让 Claude Code 在没有交互式终端的情况下运行。它接受输入、处理请求、输出结果,然后自动退出——完全适合脚本调用和 CI 环境。
# 交互式模式(需要终端)
claude
# TUI 界面,需要键盘交互
# bare 模式(无头运行)
claude --bare "审查 src/auth/ 目录下的所有文件"
# 处理完成后自动退出,结果输出到 stdout1.2 基本用法
# 单个任务
claude --bare "请审查这个 PR 的代码质量"
# 指定工作目录
cd /path/to/project
claude --bare "运行所有测试并修复失败的用例"
# 结合工作树和 PR
claude --bare --from-pr 42 "进行完整的代码审查并生成报告"1.3 bare 模式 vs 交互式模式
| 特性 | 交互式模式 (TUI) | bare 模式 |
|---|---|---|
| 终端要求 | 需要交互式终端 | 不需要 |
| 用户交互 | 支持键盘输入 | 不支持 |
| 输出格式 | 彩色文本 + TUI | 纯文本 / JSON |
| 退出方式 | 手动 /exit | 任务完成自动退出 |
| 适用场景 | 日常开发 | CI/CD、脚本、批处理 |
| 超时控制 | 无限制 | 可配置超时 |
1.4 退出码
claude --bare "some task"
echo $?
# 退出码含义:
# 0 - 成功完成
# 1 - 通用错误
# 2 - 超时
# 3 - 权限被拒绝
# 4 - API 错误1.5 超时控制
# 设置最大运行时间(秒)
claude --bare --timeout 300 "审查代码"
# 设置最大轮次
claude --bare --max-turns 20 "修复所有 lint 错误"二、管道输入机制
2.1 基本管道
Claude Code 支持从标准输入接收指令:
# 通过管道传递指令
echo "审查 src/ 目录下的所有变更" | claude --bare
# 从文件读取指令
cat review-instructions.txt | claude --bare
# 使用 heredoc
claude --bare << 'EOF'
请对以下文件进行安全审查:
1. src/auth/login.py
2. src/auth/session.py
3. src/auth/middleware.py
重点关注:
- SQL 注入风险
- 会话管理安全
- 密码处理
EOF2.2 批量管道输入
# 批量审查多个目录
for dir in src/auth src/api src/utils; do
echo "审查 $dir 目录" | claude --bare
done
# 从 PR 列表批量审查
cat pr-list.txt | while read pr_number; do
echo "审查 PR #$pr_number" | claude --bare --from-pr "$pr_number"
done2.3 JSON 管道输入
对于复杂的任务,可以使用 JSON 格式传递结构化指令:
cat << 'EOF' | claude --bare --input-format json
{
"task": "code_review",
"target": "src/auth/",
"focus_areas": ["security", "performance", "testing"],
"output_format": "structured",
"severity_threshold": "medium"
}
EOF三、stream-json 流式输出
3.1 基本用法
--output-format stream-json 让 Claude Code 以 JSON Lines 格式输出结果,每一行是一个完整的 JSON 对象:
claude --bare --output-format stream-json "审查代码"3.2 输出格式示例
{"type": "status", "message": "Starting code review..."}
{"type": "tool_use", "tool": "Read", "target": "src/auth/login.py"}
{"type": "tool_result", "tool": "Read", "status": "success"}
{"type": "tool_use", "tool": "Grep", "target": "src/auth/", "pattern": "password"}
{"type": "tool_result", "tool": "Grep", "status": "success", "matches": 12}
{"type": "finding", "severity": "high", "file": "src/auth/login.py", "line": 45, "message": "Hardcoded password detected"}
{"type": "finding", "severity": "medium", "file": "src/auth/session.py", "line": 78, "message": "Missing CSRF token validation"}
{"type": "summary", "total_findings": 5, "high": 1, "medium": 2, "low": 2, "status": "completed"}3.3 处理 JSON 输出
# 使用 jq 提取所有发现
claude --bare --output-format stream-json "审查代码" \
| jq -r 'select(.type == "finding") | "\(.severity): \(.file):\(.line) - \(.message)"'
# 输出:
# high: src/auth/login.py:45 - Hardcoded password detected
# medium: src/auth/session.py:78 - Missing CSRF token validation# 统计各严重程度数量
claude --bare --output-format stream-json "审查代码" \
| jq -s '[.[] | select(.type == "finding")] | group_by(.severity) | map({severity: .[0].severity, count: length})'
# 输出:
# [{"severity": "high", "count": 1}, {"severity": "medium", "count": 2}, {"severity": "low", "count": 2}]# 提取摘要信息
claude --bare --output-format stream-json "审查代码" \
| jq -r 'select(.type == "summary") | "Total: \(.total_findings) findings (\(.high) high, \(.medium) medium, \(.low) low)"'
# 输出:
# Total: 5 findings (1 high, 2 medium, 2 low)四、批量 PR 审查脚本
4.1 基础批量审查脚本
#!/bin/bash
# batch-review.sh - 批量审查 PR 列表
set -e
REPO="owner/my-project"
SEVERITY_THRESHOLD="medium"
# 获取所有开放的 PR
pr_numbers=$(gh pr list --repo "$REPO" --state open --json number --jq '.[].number')
echo "📋 找到 $(echo "$pr_numbers" | wc -l) 个待审查的 PR"
results_dir="review-results/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$results_dir"
for pr_num in $pr_numbers; do
echo "🔍 正在审查 PR #$pr_num..."
output_file="$results_dir/pr-${pr_num}.json"
claude --bare \
--from-pr "$pr_num" \
--output-format stream-json \
--timeout 300 \
"进行全面代码审查,重点关注安全性和代码质量" \
> "$output_file" 2>&1
# 提取审查结果
total=$(jq -s '[.[] | select(.type == "summary")] | .[0].total_findings // 0' "$output_file")
high=$(jq -s '[.[] | select(.type == "summary")] | .[0].high // 0' "$output_file")
echo " ✅ PR #$pr_num 完成: $total 个发现, $high 个高危"
# 如果有高危问题,发送通知
if [ "$high" -gt 0 ]; then
echo " ⚠️ PR #$pr_num 有 $high 个高危问题,需要关注"
# 可以添加 webhook 通知等
fi
done
echo ""
echo "📊 批量审查完成!"
echo " 结果保存在: $results_dir/"4.2 带并行处理的批量审查
#!/bin/bash
# parallel-review.sh - 并行批量审查
set -e
REPO="owner/my-project"
MAX_PARALLEL=3
results_dir="review-results/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$results_dir"
# 获取 PR 列表
pr_numbers=$(gh pr list --repo "$REPO" --state open --json number --jq '.[].number')
# 并行处理函数
review_pr() {
local pr_num=$1
local output_file="$results_dir/pr-${pr_num}.json"
echo "[PR #$pr_num] 开始审查..."
claude --bare \
--from-pr "$pr_num" \
--output-format stream-json \
--timeout 300 \
"进行全面代码审查" \
> "$output_file" 2>&1
echo "[PR #$pr_num] 审查完成"
}
# 使用 xargs 并行执行
echo "$pr_numbers" | xargs -P $MAX_PARALLEL -I {} bash -c 'review_pr "$@"' _ {}
echo "📊 所有 PR 审查完成!"五、CI/CD 集成
5.1 在 CI 中使用 bare 模式
# .gitlab-ci.yml
stages:
- review
claude-code-review:
stage: review
image: node:20-alpine
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
script:
- npm install -g @anthropic-ai/claude-code
- export ANTHROPIC_API_KEY=$CLAUDE_API_KEY
- claude --bare --from-pr $CI_MERGE_REQUEST_IID --output-format stream-json "审查此 MR 的代码质量" > review-result.json
- cat review-result.json | jq -r 'select(.type == "summary")' > review-summary.json
- if [ "$(jq '.high // 0' review-summary.json)" -gt 0 ]; then
echo "发现高危问题,需要人工审查";
exit 1;
fi
artifacts:
reports:
- review-summary.json5.2 Jenkins Pipeline
pipeline {
agent any
stages {
stage('Claude Code Review') {
steps {
sh '''
claude --bare \\
--from-pr ${CHANGE_ID} \\
--output-format stream-json \\
--timeout 600 \\
"全面代码审查" > review-output.json
# 检查高危问题
HIGH_COUNT=$(cat review-output.json | jq -s '[.[] | select(.type == "summary")] | .[0].high // 0')
if [ "$HIGH_COUNT" -gt 0 ]; then
echo "发现 $HIGH_COUNT 个高危问题"
# 将结果添加到 PR 评论
gh pr comment ${CHANGE_ID} --body-file review-output.json
exit 1
fi
'''
}
}
}
}六、GitHub Actions 实战
6.1 完整的 GitHub Actions 工作流
# .github/workflows/claude-review.yml
name: Claude Code PR Review
on:
pull_request:
types: [opened, synchronize, reopened]
schedule:
# 每天早上 9 点批量审查所有开放 PR
- cron: '0 9 * * *'
permissions:
contents: read
pull-requests: write
jobs:
review:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Claude Code
run: |
npm install -g @anthropic-ai/claude-code
- name: Claude Code Review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
claude --bare \
--from-pr ${{ github.event.pull_request.number }} \
--output-format stream-json \
--timeout 600 \
"进行全面代码审查,输出结构化报告" > review-output.json
- name: Parse Review Results
id: parse
run: |
SUMMARY=$(cat review-output.json | jq -s '[.[] | select(.type == "summary")] | .[0]')
echo "total=$(echo $SUMMARY | jq '.total_findings // 0')" >> $GITHUB_OUTPUT
echo "high=$(echo $SUMMARY | jq '.high // 0')" >> $GITHUB_OUTPUT
echo "medium=$(echo $SUMMARY | jq '.medium // 0')" >> $GITHUB_OUTPUT
echo "low=$(echo $SUMMARY | jq '.low // 0')" >> $GITHUB_OUTPUT
- name: Post Review Comment
if: always()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
COMMENT="## 🔍 Claude Code 审查报告\n\n"
COMMENT+="| 严重程度 | 数量 |\n"
COMMENT+="|---------|------|\n"
COMMENT+="| 🔴 高危 | ${{ steps.parse.outputs.high }} |\n"
COMMENT+="| 🟡 中危 | ${{ steps.parse.outputs.medium }} |\n"
COMMENT+="| 🟢 低危 | ${{ steps.parse.outputs.low }} |\n"
COMMENT+="| **总计** | **${{ steps.parse.outputs.total }}** |\n\n"
# 添加详细发现
COMMENT+="### 详细发现\n\n"
FINDINGS=$(cat review-output.json | jq -r 'select(.type == "finding") | "- \(.severity): \(.file):\(.line) - \(.message)"')
COMMENT+="$FINDINGS"
gh pr comment ${{ github.event.pull_request.number }} --body "$COMMENT"
- name: Fail on High Severity Issues
if: steps.parse.outputs.high > '0'
run: |
echo "::error::发现 ${{ steps.parse.outputs.high }} 个高危问题,需要人工审查"
exit 16.2 定时批量审查工作流
# .github/workflows/batch-review.yml
name: Batch PR Review
on:
schedule:
- cron: '0 9 * * 1-5' # 工作日每天 9 点
jobs:
batch-review:
runs-on: ubuntu-latest
timeout-minutes: 120
steps:
- uses: actions/checkout@v4
- name: Setup Claude Code
run: npm install -g @anthropic-ai/claude-code
- name: Get Open PRs
id: prs
run: |
PRS=$(gh pr list --state open --json number,title --jq '.[].number')
echo "numbers=$PRS" >> $GITHUB_OUTPUT
- name: Review Each PR
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
for pr in ${{ steps.prs.outputs.numbers }}; do
echo "Reviewing PR #$pr"
claude --bare \
--from-pr "$pr" \
--output-format stream-json \
--timeout 300 \
"全面代码审查" > "review-pr-$pr.json" 2>&1 || true
done
- name: Aggregate Results
run: |
echo "## 📊 批量审查报告" > summary.md
echo "" >> summary.md
for f in review-pr-*.json; do
pr=$(echo $f | sed 's/review-pr-//;s/.json//')
summary=$(cat "$f" | jq -s '[.[] | select(.type == "summary")] | .[0]')
total=$(echo $summary | jq '.total_findings // 0')
high=$(echo $summary | jq '.high // 0')
echo "- PR #$pr: $total 个发现 ($high 高危)" >> summary.md
done
- name: Create Summary Issue
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh issue create \
--title "Claude Code 批量审查报告 - $(date +%Y-%m-%d)" \
--body-file summary.md \
--label "automated-review"七、性能优化
7.1 并发控制
#!/bin/bash
# 控制并发审查数量,避免 API 限流
MAX_CONCURRENT=5
SEMAPHORE_FILE="/tmp/claude-review-semaphore"
# 初始化信号量
mkdir -p "$SEMAPHORE_FILE"
acquire_semaphore() {
while [ "$(ls "$SEMAPHORE_FILE" | wc -l)" -ge "$MAX_CONCURRENT" ]; do
sleep 5
done
touch "$SEMAPHORE_FILE/$$"
}
release_semaphore() {
rm -f "$SEMAPHORE_FILE/$$"
}
# 审查函数
review_with_semaphore() {
local pr_num=$1
acquire_semaphore
claude --bare --from-pr "$pr_num" --output-format stream-json "审查" > "result-$pr_num.json"
release_semaphore
}
# 启动并行审查
for pr in $PR_LIST; do
review_with_semaphore "$pr" &
done
wait
rm -rf "$SEMAPHORE_FILE"7.2 缓存策略
#!/bin/bash
# 缓存已审查的 PR,避免重复审查
CACHE_DIR=".claude/review-cache"
mkdir -p "$CACHE_DIR"
should_review() {
local pr_num=$1
local cache_file="$CACHE_DIR/pr-${pr_num}.json"
if [ -f "$cache_file" ]; then
# 检查 PR 是否有新提交
last_reviewed=$(jq -r '.reviewed_at' "$cache_file")
last_commit=$(git log -1 --format=%ct "origin/pr/$pr_num")
if [ "$last_commit" -le "$last_reviewed" ]; then
echo "PR #$pr_num 自上次审查后无变更,跳过"
return 1
fi
fi
return 0
}
for pr in $PR_LIST; do
if should_review "$pr"; then
claude --bare --from-pr "$pr" --output-format stream-json "审查" > "result-$pr.json"
# 缓存结果
jq --arg ts "$(date +%s)" '.reviewed_at = $ts' "result-$pr.json" > "$CACHE_DIR/pr-${pr_num}.json"
fi
done7.3 成本预估
#!/bin/bash
# 批量审查成本预估
estimate_cost() {
local pr_count=$1
local avg_lines_per_pr=200
local tokens_per_line=50
local total_tokens=$((pr_count * avg_lines_per_pr * tokens_per_line))
local cost_per_million=3.0 # Sonnet 4 价格
local cost=$(echo "scale=2; $total_tokens / 1000000 * $cost_per_million" | bc)
echo "预估 $pr_count 个 PR 审查成本: \$$cost (约 $total_tokens tokens)"
}
estimate_cost 50
# 输出: 预估 50 个 PR 审查成本: $15.00 (约 5000000 tokens)八、总结
--bare 模式、管道输入和 stream-json 输出让 Claude Code 从交互式编程助手转变为可编程的自动化工具。通过合理组合这三个功能,你可以构建出高效的批量 PR 审查流水线,将代码质量检查无缝集成到 CI/CD 流程中。
关键要点:
--bare模式让 Claude Code 无头运行,适合脚本和 CI 调用- 管道输入支持从文件、stdin 批量传递审查指令
stream-json提供结构化输出,可用 jq 等工具解析- 批量审查脚本可结合并行处理提高效率
- GitHub Actions 工作流可实现自动 PR 审查
- 缓存和信号量机制优化成本和并发
- 合理的超时和退出码处理保证流水线稳定性
九、下篇预告
Claude Code 成本优化深度指南 — 掌握 max-turns、max-budget-usd、fallback-model 等成本控制策略,学习 Token 监控、模型降级、提示优化等实战技巧,让你的 AI 编程助手既强大又经济。