Headless 模式与 CI/CD 集成——让 Claude Code 融入自动化流水线
Windows 10/11 · Claude Code v2.1.32+ · DeepSeek V4 Pro / Anthropic API · 🟡 中度时效 · 最后更新 2026-05-11
一、这篇教程解决什么问题
一句话定位:读完本篇,你会用 claude -p 把 Claude Code 嵌入 GitHub Actions 自动审查每个 PR、用 --output-format json 让脚本消费结构化输出、用 --max-turns 和 --allowedTools 给 CI 管道上锁、以及理解什么时候该用 Headless + CI、什么时候该用 Routines——让 Claude Code 从"你在终端里跟它聊天"变成"它在流水线里自动干活"。
跳读指南:如果你只关心 GitHub Actions 怎么配,跳到 第五节。想了解安全控制参数,跳到 第四节。已经有 CI 基础、想直接看企业级部署方案,跳到 第八节。搞不清楚 Routines 和 Headless 什么时候用哪个,跳到 第九节。
阅读前提(硬条件,可逐条验证):
- Claude Code CLI 已安装并能正常启动(
claude --version验证) - 了解 Git 基础操作(branch、commit、diff)
- 读过《新手上路(一)》了解权限模式(
--allowedTools和--disallowedTools与权限模式直接相关) - 拥有 GitHub 账号(第五节涉及 GitHub Actions)
- 了解 CI/CD 基本概念(知道 GitHub Actions / GitLab CI / Jenkins 是什么即可)
DeepSeek 用户注意:Headless 模式是 CLI 本地功能,全部可用。CI/CD 集成通过 API 调用,配置
ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic即可。本文所有示例同时标注 Anthropic API 和 DeepSeek 两种环境变量配置。
读完能得到什么:
- 一张 Headless 模式完整命令速查表——所有相关 flag 的含义、组合方式和适用场景
- 三种输出格式的解析代码——text 人工读、json 脚本消费、stream-json 实时监控
- 三套开箱即用的 GitHub Actions YAML——PR 审查 / Push 测试 / 定时扫描
- GitLab CI 和 Jenkins 的适配配置模板
- 两个自动化脚本范例——批量 CHANGELOG 生成 + 依赖更新审查
- 企业托管策略配置——
managed-settings.json锁定 CI 环境权限 - Routines vs Headless + CI 决策树——一眼判断该用哪个
- 5 个真实 Debug 场景的五段式排查
二、什么是 Headless 模式:没有界面的 Claude Code
2.1 你现在的 Claude Code 是什么形态
打开终端 → 输入指令 → Claude 分析 → 需要读文件?弹确认框 →
需要跑命令?弹确认框 → 需要改代码?弹确认框 → 输出结果 → 等下一个指令
这是 交互模式(Interactive / REPL)。它的核心假设是:有一个人在屏幕前,随时可以做决策。
但在以下场景,没有人坐在屏幕前:
- GitHub Actions 收到一个 PR,需要自动审查代码
- 每晚 3 点定时扫描依赖漏洞
- 一个脚本需要批量分析 50 个文件然后退出
这时候就需要 Headless 模式——Claude Code 作为一个普通的命令行工具,接收指令、执行、返回结果、退出。没有确认框、没有进度条、没有 REPL。
先看全景图——下面是 Claude Code 作为 CI/CD 组件的四层调用框架,后文的所有内容都围绕这张图展开:

配套 Mermaid 源码见 CI框架图.mmd,可在 GitHub、掘金、知乎等支持 Mermaid 渲染的平台直接粘贴使用。
2.2 -p 标志:从交互到非交互的一行命令
Headless 模式的入口是 --print 标志,简写 -p:
# 交互模式:打开 REPL,等用户输入
claude
# Headless 模式:执行完就退出,输出到 stdout
claude -p "解释 auth.py 中的登录流程"
-p 告诉 Claude Code:不要打开交互界面,执行完这条指令后直接退出。输出打印到 stdout,退出码 0 表示成功。
-p 模式下的能力边界:
| 能力 | 交互模式 | -p 模式 |
|---|---|---|
| 多轮对话 | ✓ | ✗(每次调用独立) |
斜杠命令(/review、/compact) | ✓ | ✗(不可用) |
| 工具调用(读文件、跑命令、编辑) | ✓(需确认) | ✓(需配置自动批准) |
| 会话记忆 | ✓(跨轮持久) | ✗(除非用 --session-id + --resume) |
| 自动压缩(Auto-compaction) | ✓ | ✗(超出上下文直接报错) |
| OAuth 登录 | ✓ | ✗(CI 环境只能用 API Key) |
| MCP 服务器 | ✓ | ✓(需显式配置) |
| Hooks | ✓ | ✗(PermissionRequest 类不触发) |
2.3 管道输入:把任何内容喂给 Claude
-p 模式接受 stdin 输入,这意味着你可以像使用 grep 或 jq 一样使用它:
# 分析一个文件
cat src/auth/login.py | claude -p "找出这段代码中所有的安全漏洞"
# 审查一次提交的改动
git diff HEAD~1 | claude -p "写一份 CHANGELOG 摘要"
# 分析测试输出
npm test 2>&1 | claude -p "总结测试失败的原因,给出修复建议"
# 与 jq 组合——先取 JSON 字段,再让 Claude 分析
gh pr view --json body | jq -r '.body' | claude -p "判断这个 PR 描述是否包含了验收标准,回复 yes 或 no"
stdin 的内容会作为上下文放在你的 prompt 前面。Claude 能同时看到管道传入的数据和你的指令。
2.4 --bare 模式:CI 环境的最佳起点
在 CI 环境中,你不希望 Claude Code 加载本地才有的东西——同事的 hooks、项目的 MCP 服务器、全局的 CLAUDE.md。--bare 跳过所有这些:
claude -p "审查代码" --bare
--bare 跳过以下自动发现:
- Hooks(
~/.claude/hooks/和项目.claude/hooks/) - Skills(
~/.claude/skills/) - 插件(已安装的 plugin)
- MCP 服务器(
.mcp.json和 settings 中的配置) - Auto Memory
- CLAUDE.md(项目和全局)
在 bare 模式下,Claude 只有 Bash、Read、Edit 三种基础工具。如果需要 CLAUDE.md 中的项目规范,用 --add-dir . 显式加载:
claude -p "遵循项目规范审查代码" --bare --add-dir .
CI 环境推荐组合:
claude -p "你的任务" \
--bare \
--no-session-persistence \
--output-format json \
--max-turns 10 \
--max-budget-usd 1.00
| Flag | 作用 | CI 必要性 |
|---|---|---|
--bare | 跳过 hooks/skills/MCP/CLAUDE.md 自动发现 | 推荐——保证可复现 |
--no-session-persistence | 不写会话到磁盘 | 推荐——节省空间 |
--output-format json | 结构化输出 | 推荐——方便脚本解析 |
--max-turns N | 硬限制工具调用轮数 | 强烈推荐——防止死循环 |
--max-budget-usd N | 硬限制单次费用 | 推荐——控制成本 |
三、输出格式完全指南:text、json、stream-json
3.1 三种格式对比
--output-format 决定 Claude Code 怎么返回结果。选错格式的代价:人工读 JSON 眼睛累、脚本解析纯文本容易出错。
| 格式 | Flag | 适合谁 | 一句话 |
|---|---|---|---|
| text | --output-format text(默认) | 人类 | 可读的 Markdown,直接看 |
| json | --output-format json | 脚本 | 一次返回完整结构,含 cost、session_id |
| stream-json | --output-format stream-json | 实时系统 | 逐行 JSON 事件,边跑边出结果 |
3.2 text:默认格式,给人看的
claude -p "这段代码有问题吗?" --output-format text < src/auth.py
输出就是 Claude 的纯文本回复,Markdown 格式,人类友好。适合手动跑完直接看的场景。
3.3 json:给脚本消费的
claude -p "分析代码复杂度" --output-format json < src/main.py
返回一个 JSON 对象,关键字段:
{
"result": "这段代码的圈复杂度为 8,建议拆分...",
"session_id": "abc123-def456",
"total_cost_usd": 0.0234,
"usage": {
"input_tokens": 1200,
"output_tokens": 450
},
"model": "claude-sonnet-4-6",
"subtype": "success"
}
total_cost_usd 字段是 CI 成本追踪的关键——每次调用的费用精确到小数点后四位,你可以把它打入你的监控系统。
json 格式还支持 --json-schema 强制输出符合特定结构:
claude -p "审查这个 PR" \
--output-format json \
--json-schema '{
"type": "object",
"properties": {
"has_bugs": {"type": "boolean"},
"severity": {"enum": ["critical", "major", "minor", "none"]},
"summary": {"type": "string"}
},
"required": ["has_bugs", "severity", "summary"]
}' < pr.diff
这样下游脚本不需要解析自然语言,直接读字段。
3.4 stream-json:实时流式输出
当任务可能跑几分钟、你想实时看到进度时,用 stream-json:
claude -p "审查整个项目" \
--output-format stream-json \
--verbose \
--include-partial-messages
每一行是一个独立的 JSON 事件(NDJSON 格式)。常见事件类型:
| 事件 type | 含义 | 关键字段 |
|---|---|---|
system / init | 会话初始化 | tools, model, session_id, permissionMode |
stream_event / content_block_delta | 逐 token 文本 | event.delta.text |
stream_event / tool_use | 工具调用开始 | event.tool.name, event.tool.input |
user | 工具执行结果 | 工具返回的内容 |
system / api_retry | API 重试通知 | attempt, retry_delay_ms, error |
result / success | 最终结果 | 同 json 格式的完整 envelope |
实战——用 jq 实时显示 Claude 的输出文本:
claude -p "写一首诗" \
--output-format stream-json --verbose --include-partial-messages \
| jq -rj 'select(.type == "stream_event" and .event.delta.type? == "text_delta") | .event.delta.text'
-r 输出原始字符串(不带引号),-j 不添加换行。效果是你看到 Claude 的文本像打字一样逐字流出。
实战——监控 API 重试:
claude -p "..." --output-format stream-json --verbose \
| jq -c 'select(.type == "system" and .subtype == "api_retry")'
输出:{"type":"system","subtype":"api_retry","attempt":1,"max_retries":5,"retry_delay_ms":2000,"error":"rate_limit"}
实战——提取最终 cost:
claude -p "..." --output-format stream-json --verbose \
| jq -r 'select(.type == "result") | .total_cost_usd'
3.5 双向流:--input-format stream-json
高级场景——你需要持续向 Claude 发送消息、同时接收实时响应(比如构建一个自定义 Web UI):
claude -p \
--input-format stream-json \
--output-format stream-json \
--verbose \
--replay-user-messages
--replay-user-messages 把你发送给 Claude 的消息重新输出到 stdout,实现"发送 → 确认收到 → 响应"的完整闭环。这是 Agent SDK 的底层协议,一般用户不需要直接使用,但了解它的存在有助于理解为什么 Agent SDK 能做实时 UI。
四、CI 环境的安全控制:给 Claude 上锁
在交互模式下,你做安全决策——每次工具调用弹出权限确认,你点"允许"或"拒绝"。在 CI 环境里,没有人在屏幕前,所以安全控制必须预先写在命令行里。
4.1 权限控制全景图
Claude Code 提供了五个层级的安全控制,从松到严:
--dangerously-skip-permissions → 所有工具静默执行(仅限隔离容器)
└── --permission-mode acceptEdits → 编辑放行,其他工具需 allow
└── --allowedTools → 列出的工具跳过确认(权限流)
└── --tools → 限制可用的工具集(根本不让用)
└── --disallowedTools → 从上下文中彻底移除指定工具
最容易混淆的概念:
| Flag | 做什么 | 不做什么 |
|---|---|---|
--allowedTools "Read,Edit" | 跳过 Read 和 Edit 的确认弹框 | 不限制其他工具——未列出的工具仍然可用,但会触发确认(在 CI 中等于卡死) |
--tools "Read,Edit,Grep" | Read、Edit、Grep 之外的工具根本不存在 | 不跳过确认——工具仍然需要权限检查 |
--disallowedTools "Bash(git push *)" | 从上下文中移除匹配的工具 | 不是 deny(deny 会显示一个被拒绝的提示)——disallow 是"这个工具不存在" |
CI 环境的正确组合:
claude -p "审查代码,只读" \
--tools "Read,Glob,Grep" \
--permission-mode acceptEdits
--tools 把工具限制到只读三个,--permission-mode acceptEdits 跳过这三个工具的确认弹框。效果:Claude 只能读、不能写也不能跑命令。
4.2 --max-turns:防止死循环
-p 模式默认没有 max-turns 限制。如果任务模糊,Claude 可能不断尝试、消耗大量 token。在 CI 中,必须设上限:
# 审查任务:3-5 个 turn 通常足够
claude -p "审查这个 PR" --max-turns 5
# 代码生成任务:可能需要更多
claude -p "生成测试文件" --max-turns 15
# 纯文本任务:1 个 turn 即可
claude -p "分类这个 Issue" --max-turns 1
达到上限时,Claude 以 error_max_turns 退出——这是一个非零退出码,意味着 CI 步骤会被标记为失败。
4.3 --allowedTools:精细化放行
使用 permission rule 语法精确控制哪些命令可以自动执行:
# 只允许安全的 git 操作
claude -p "..." \
--allowedTools "Bash(git diff *)" "Bash(git log *)" "Read" "Glob" "Grep"
# 允许运行测试、禁止其他命令
claude -p "跑完测试后修复失败的用例" \
--allowedTools "Bash(npm test *)" "Bash(npm run lint *)" "Read" "Edit"
* 的使用规则:Bash(git diff *) 中 * 前有一个空格——这启用了前缀匹配,允许所有以 git diff 开头的命令。如果没有空格和 *,则是精确匹配。
4.4 --dangerously-skip-permissions:核选项
跳过所有权限检查,Claude 可以执行任何操作。只在以下环境使用:
- Docker 容器内
- 临时 EC2 / GitHub Actions runner
- Git Worktree 隔离分支
永远不要在你的开发笔记本上使用它。破坏力:一个 prompt 注入就能变成 shell 注入。
4.5 --max-budget-usd:硬费用上限
claude -p "审查整个项目" --max-budget-usd 0.50
达到费用上限时,Claude 停止并返回 error_max_budget_usd。配合 GitHub Actions 的 timeout-minutes 使用,形成双重保护:
| 保护层 | 机制 | 防护什么 |
|---|---|---|
--max-turns N | 限制工具调用轮数 | 逻辑死循环 |
--max-budget-usd N | 限制 API 费用 | 成本失控 |
timeout-minutes | GitHub Actions 级别超时 | 进程僵死 |
五、GitHub Actions 集成:三个开箱即用的 Workflow
GitHub Actions 是 Headless 模式最常用的 CI/CD 平台。以下是三个经过验证的完整 YAML。
5.1 准备工作:安装与密钥
在你的 GitHub 仓库中:
# 1. 添加 API Key 到 Secrets
# Settings → Secrets and variables → Actions → New repository secret
# Name: ANTHROPIC_API_KEY
# Value: sk-ant-api03-xxxx
# DeepSeek 用户额外添加:
# Name: ANTHROPIC_BASE_URL
# Value: https://api.deepseek.com/anthropic
5.2 Workflow 一:PR 自动代码审查
每打开或更新一个 PR,Claude Code 自动审查 diff 并发布评论:
# .github/workflows/claude-pr-review.yml
name: Claude PR Review
on:
pull_request:
types: [opened, synchronize]
permissions:
contents: read
pull-requests: write
jobs:
review:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install Claude Code
run: npm install -g @anthropic-ai/claude-code
- name: Generate PR diff
run: |
git diff origin/${{ github.base_ref }}...HEAD \
-- '*.ts' '*.tsx' '*.js' '*.py' '*.go' > pr.diff
echo "DIFF_SIZE=$(wc -c < pr.diff)" >> $GITHUB_ENV
- name: Skip if no diff
if: env.DIFF_SIZE == '0'
run: echo "No code changes — skipping review" && exit 0
- name: Claude Code Review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }}
run: |
claude -p \
"Review this PR diff as a senior engineer. Respond in markdown with:
## Summary
One paragraph.
## Issues Found
Numbered list with severity (Critical / Major / Minor).
For each: file:line, what's wrong, why it matters, how to fix.
## Security
Any security concerns. If none, say 'No security concerns.'
## Suggestions
Optional improvements. Keep brief.
Be direct. Skip nitpicks on formatting." \
--output-format text \
--max-turns 5 \
--max-budget-usd 0.50 \
--bare \
--no-session-persistence \
--dangerously-skip-permissions \
< pr.diff > review.md
- name: Post review to PR
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const body = fs.readFileSync('review.md', 'utf8');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `## Claude Code Review\n\n${body}`
});
关键设计决策:
fetch-depth: 0:必须取完整历史,否则git diff origin/main...HEAD拿不到正确的 diff-- '*.ts' '*.tsx' ...:过滤 diff,避免分析 lockfile 和 markdown 的噪音DIFF_SIZE == '0'提前退出:避免对纯文档 PR 浪费 API 调用--dangerously-skip-permissions:在 GitHub Actions 隔离 runner 中是安全的——每个 job 跑在全新 VM 里
5.3 Workflow 二:Push 自动测试生成
每次推代码,检测未覆盖的文件,自动生成测试 PR:
# .github/workflows/claude-test-gen.yml
name: Claude Test Generator
on:
push:
branches:
- 'feature/**'
- 'fix/**'
paths:
- 'src/**'
permissions:
contents: write
pull-requests: write
jobs:
generate-tests:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install dependencies
run: npm ci
- name: Install Claude Code
run: npm install -g @anthropic-ai/claude-code
- name: Find files without tests
id: missing-tests
run: |
CHANGED=$(git diff --name-only origin/main...HEAD -- 'src/**/*.ts' 'src/**/*.tsx' | grep -v '\.test\.')
MISSING=""
for file in $CHANGED; do
TESTFILE=$(echo "$file" | sed 's/\.ts$/.test.ts/' | sed 's/\.tsx$/.test.tsx/')
if [ ! -f "$TESTFILE" ]; then
MISSING="$MISSING $file"
fi
done
echo "files=$MISSING" >> $GITHUB_OUTPUT
- name: Generate tests
if: steps.missing-tests.outputs.files != ''
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }}
run: |
claude -p \
"For each of these files: ${{ steps.missing-tests.outputs.files }}
Create a corresponding test file following the project's existing test patterns.
Cover nominal cases AND edge cases. Run 'npm test' to verify they pass." \
--output-format json \
--max-turns 15 \
--max-budget-usd 2.00 \
--dangerously-skip-permissions \
> test-gen-result.json
- name: Create PR
if: steps.missing-tests.outputs.files != ''
uses: peter-evans/create-pull-request@v7
with:
commit-message: "test: auto-generated tests for changed files"
branch: auto/generated-tests-${{ github.run_id }}
title: "Test: Claude-generated tests for missing coverage"
body: |
Claude Code 自动生成的测试文件。
**合并前请检查:**
- [ ] 测试覆盖了正常路径
- [ ] 测试覆盖了边界情况
- [ ] 测试覆盖了错误场景
- [ ] `npm test` 全部通过
5.4 Workflow 三:定时安全扫描
每周日凌晨 3 点,Claude Code 审查关键路径代码:
# .github/workflows/claude-security-scan.yml
name: Claude Security Scan
on:
schedule:
- cron: '0 3 * * 0' # 每周日 3:00 UTC
workflow_dispatch: # 允许手动触发
permissions:
contents: read
issues: write
jobs:
scan:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install Claude Code
run: npm install -g @anthropic-ai/claude-code
- name: Security scan
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }}
run: |
FILES=$(find src -name '*.ts' -not -name '*.test.*' | head -50)
echo "$FILES" | claude -p \
"Review these file paths for the codebase. Identify which files likely handle:
auth, payment, user data, or API input.
For each such file, read it and flag:
1. Missing input validation
2. Hardcoded secrets or keys (even placeholders)
3. SQL/NoSQL injection risks
4. Missing auth checks
Output as a numbered list with file:line references." \
--output-format text \
--max-turns 10 \
--max-budget-usd 1.00 \
--dangerously-skip-permissions \
< /dev/null > security-report.md
- name: Create issue if issues found
if: success()
run: |
if grep -q "^\d+\." security-report.md; then
gh issue create \
--title "Security Scan Findings — $(date +%Y-%m-%d)" \
--body-file security-report.md \
--label "security,automated"
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
5.5 使用官方 Action(简化版)
如果你不想手动管理 npm install 和 flag 组合,Anthropic 提供了官方 Action:
# .github/workflows/claude-simple.yml
name: Claude Code 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
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Review this pull request for bugs, security issues, and performance.
Post your findings as review comments.
claude_args: "--max-turns 5 --max-budget-usd 0.50"
claude_args 透传任意 CLI 参数。但官方 Action 封装了更多细节(GitHub API 交互、评论发布),适合快速上手。手动 CLI 方案的优势是完全控制——你可以定制 prompt 结构、输出格式和后处理逻辑。
六、GitLab CI 与 Jenkins 集成要点
Headless 模式在所有主流 CI 平台上的核心逻辑相同,差异只在密钥管理和安装方式。
6.1 GitLab CI
# .gitlab-ci.yml
stages:
- review
claude-review:
stage: review
image: node:22-alpine
only:
- merge_requests
before_script:
- npm install -g @anthropic-ai/claude-code
- apk add --no-cache git jq
script:
- |
DIFF=$(git diff origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD | head -c 8000)
if [ -z "$DIFF" ]; then
echo "No diff — skipping review"
exit 0
fi
echo "$DIFF" | claude -p \
"Review this diff. Flag bugs, security issues, and improvements." \
--output-format json \
--max-turns 5 \
--bare \
--no-session-persistence \
> review.json
- |
REVIEW=$(jq -r '.result' review.json)
curl -s -X POST \
"https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes" \
--header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
--data-urlencode "body=$REVIEW"
variables:
ANTHROPIC_API_KEY: $ANTHROPIC_API_KEY
GitLab CI 特有的注意事项:
- MR 的源分支和目标分支变量名不同:
$CI_MERGE_REQUEST_SOURCE_BRANCH_NAMEvs$CI_MERGE_REQUEST_TARGET_BRANCH_NAME head -c 8000截断 diff:防止大 PR 超出上下文窗口(-p模式无自动压缩)- GitLab 私有化部署需要配置
ANTHROPIC_BASE_URL
6.2 Jenkins
// Jenkinsfile
pipeline {
agent {
docker { image 'node:22-alpine' }
}
environment {
ANTHROPIC_API_KEY = credentials('anthropic-api-key')
}
stages {
stage('Install Claude Code') {
steps {
sh 'npm install -g @anthropic-ai/claude-code'
}
}
stage('Code Review') {
when { changeRequest() }
steps {
sh '''
DIFF=$(git diff origin/$CHANGE_TARGET...HEAD | head -c 8000)
if [ -n "$DIFF" ]; then
echo "$DIFF" | claude -p \
"Review this diff for bugs and security issues." \
--output-format json \
--max-turns 5 \
--bare > review.json
jq -r '.result' review.json > review.md
fi
'''
emailext body: '${FILE,path="review.md"}',
subject: "Claude Code Review: ${env.BUILD_TAG}",
to: 'team@example.com'
}
}
}
}
Jenkins 特有的注意事项:
- 使用 Credentials Binding 管理 API Key:
credentials('anthropic-api-key') when { changeRequest() }确保只在 PR 时触发(对应 Multibranch Pipeline)- Jenkins 通常是长期运行的 agent,注意清理残留的 session 文件
6.3 CI 平台速查表
| CI 平台 | 密钥存储 | 安装命令 | 超时设置 |
|---|---|---|---|
| GitHub Actions | ${{ secrets.ANTHROPIC_API_KEY }} | npm install -g @anthropic-ai/claude-code | timeout-minutes: N |
| GitLab CI | $ANTHROPIC_API_KEY (CI Variable) | 同上 | timeout: N minutes |
| Jenkins | Credentials Binding | 同上 | timeout(time: N, unit: 'MINUTES') |
| CircleCI | Context / env variable | 同上 | no_output_timeout: Nm |
七、自动化脚本实战
7.1 批量 CHANGELOG 生成
#!/bin/bash
# auto-changelog.sh — 从 commit 历史自动生成 CHANGELOG
set -e
TAG=${1:-"HEAD~10..HEAD"}
OUTPUT="CHANGELOG.md"
echo "## Changelog ($(date +%Y-%m-%d))" > $OUTPUT
echo "" >> $OUTPUT
MODULES=("auth" "api" "ui" "db" "config")
for MODULE in "${MODULES[@]}"; do
COMMITS=$(git log $TAG --oneline -- "src/$MODULE/**" 2>/dev/null || true)
if [ -n "$COMMITS" ]; then
echo "### $MODULE" >> $OUTPUT
echo "$COMMITS" | claude -p \
"Convert these commits into a user-facing changelog in Chinese.
Group by type: Features, Fixes, Improvements.
Each entry: one line, starting with '- '.
Example: '- 修复了登录页面在移动端的布局错位问题'" \
--output-format text \
--max-turns 1 \
--bare \
>> $OUTPUT
echo "" >> $OUTPUT
fi
done
echo "Done. Output: $OUTPUT"
7.2 依赖更新自动审查
#!/bin/bash
# dependency-audit.sh — 审查 package.json 依赖变更
BRANCH=${1:-"HEAD"}
BASE=${2:-"main"}
DEP_DIFF=$(git diff $BASE...$BRANCH -- package.json | grep '^[+-]' | grep -v '^[+-]\{3\}')
if [ -z "$DEP_DIFF" ]; then
echo "No dependency changes."
exit 0
fi
echo "$DEP_DIFF" | claude -p \
"Review these dependency changes for a Node.js project. For each change:
1. Is this a major version bump that could break the API?
2. Does the new version have known security vulnerabilities? (flag if uncertain)
3. Is this dependency actively maintained? (flag if seems abandoned)
4. Overall risk level: Low / Medium / High
Format as markdown table: Package | Change | Risk | Notes" \
--output-format text \
--max-turns 5 \
--max-budget-usd 0.30 \
--bare \
--no-session-persistence
7.3 脚本中的通用 Shell 函数
把重复的 Claude 调用封装为函数:
# 添加到你的 CI 脚本
run_claude() {
local prompt="$1"
local max_turns="${2:-5}"
local budget="${3:-0.50}"
claude -p "$prompt" \
--output-format json \
--max-turns "$max_turns" \
--max-budget-usd "$budget" \
--bare \
--no-session-persistence \
--dangerously-skip-permissions
}
# 使用
RESULT=$(echo "$DIFF" | run_claude "审查代码" 5 0.50)
COST=$(echo "$RESULT" | jq -r '.total_cost_usd')
echo "审查完成,费用: $COST"
echo "$RESULT" | jq -r '.result' > review.md
八、企业托管策略:managed-settings.json
当 Claude Code 被部署到一个团队的所有 CI 管道中时,安全团队通常需要一个强制性的最小权限策略——不管开发者在自己的 settings.json 里配了什么,CI 管道中的行为必须一致且受限。
8.1 什么是 Managed Settings
managed-settings.json 是一个只读配置文件,优先级高于用户和项目的 settings.json。它通常由运维或安全团队部署到 CI 环境中。
关键字段示例:
{
"permissions": {
"deny": [
"Bash(curl:*)",
"Bash(wget:*)",
"Bash(gh repo delete:*)",
"Bash(git push origin main:*)",
"Bash(git push origin master:*)",
"WebFetch",
"WebSearch"
],
"allow": [
"Read",
"Edit",
"Glob",
"Grep",
"Bash(git diff:*)",
"Bash(git log:*)",
"Bash(npm test:*)",
"Bash(npm run lint:*)"
],
"defaultMode": "dontAsk"
},
"enableAllProjectMcpServers": false,
"enableAllProjectSkills": false,
"autoUpdates": false
}
核心锁定的含义:
| 配置 | 效果 |
|---|---|
deny 规则 | 即使 --dangerously-skip-permissions 也不能绕过 |
defaultMode: "dontAsk" | 未在 allow 列表中的操作一律拒绝 |
enableAllProjectMcpServers: false | 项目的 .mcp.json 被忽略 |
enableAllProjectSkills: false | 项目的 skills 不加载 |
autoUpdates: false | CI 环境不自动更新 Claude Code 版本 |
8.2 部署到 CI Runner
在 GitHub Actions 中部署 Managed Settings:
- name: Deploy managed settings
run: |
mkdir -p /opt/claude-config
echo '${{ secrets.MANAGED_SETTINGS_JSON }}' > /opt/claude-config/managed-settings.json
export CLAUDE_CODE_MANAGED_SETTINGS=/opt/claude-config/managed-settings.json
- name: Run Claude Code
env:
CLAUDE_CODE_MANAGED_SETTINGS: /opt/claude-config/managed-settings.json
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
claude -p "..." --bare --dangerously-skip-permissions
managed-settings.json 的 deny 规则会覆盖 --dangerously-skip-permissions,所以即使在隔离容器中全开权限,某些操作(如 curl、git push main)仍然被阻断。
九、什么时候用 Routines,什么时候用 Headless + CI
这是最容易混淆的决策。两者都能让 Claude Code "自动干活",但运行位置、触发方式和适用场景完全不同。
9.1 决策树
需要关机后继续运行?
├── 是 → Routines(唯一选择)
└── 否 → 继续
├── 由 Git 事件触发(PR 打开、Push、Issue 创建)?
│ └── 是 → Headless + CI/CD
├── 由定时触发(cron)?
│ ├── 用 GitHub Actions 的 schedule 触发器 → Headless + CI
│ └── 用 Routines 的 Cron 触发器 → Routines(关机也能跑)
└── 由外部系统通过 API 触发?
├── 需要长期无人值守 → Routines
└── 作为管道的一步 → Headless + CI
9.2 对比表
| 维度 | Routines(云端) | Headless + CI/CD |
|---|---|---|
| 运行位置 | Anthropic 云端基础设施 | CI Runner(GitHub / GitLab / Jenkins) |
| 触发方式 | Cron / HTTP POST / GitHub Webhook | Git Events / Schedule / 手动 / 外部调用 |
| 需要机器开机 | 否 | 否(Runner 按需启动) |
| 最长运行时间 | 30 分钟 | 取决于 CI 平台(GitHub Actions 最长 6h) |
| 文件隔离 | 云端 clone 仓库,推到 claude/ 分支 | CI Runner 的工作目录 |
| 安全模型 | 预定义 Prompt,无人审批,受限分支 | --allowedTools + --max-turns + Managed Settings |
| 成本 | 配额制(Pro 5次/天、Max 15次/天) | API 调用费(按量付费) |
| 配置方式 | Web UI 或 CLI /routine | YAML 文件(.github/workflows/) |
| 适用场景 | 定时巡检、Issue 自动分类、API 驱动的分析 | PR 审查、Push 测试、作为 CI 管道的一步 |
| DeepSeek 兼容 | ✗(仅 Anthropic API) | ✓ |
9.3 一句话选型
- "每天凌晨自动分类 Issue" → Routines(关机也不影响)
- "每个 PR 打开时自动审查代码" → Headless + CI(事件驱动,作为 CI 管道一步)
- "Datadog 告警后自动分析日志" → Routines(API 触发,长期等待告警)
- "发布前自动生成 CHANGELOG" → Headless + CI(作为 release pipeline 的一步)
十、CI 环境的 API Key 安全存储
10.1 GitHub Actions Secrets
# 正确:密钥通过 secrets 注入
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
# 错误:绝不要把密钥硬编码在 YAML 里
env:
ANTHROPIC_API_KEY: "sk-ant-api03-xxxx" # 危险!
GitHub Secrets 的安全特性:
- 在日志中自动脱敏(显示为
***) - 不能从 fork PR 中访问(除非使用
pull_request_target) - 有访问日志,可以追踪谁读了密钥
10.2 使用 GitHub App 替代 Personal API Key(进阶)
对于组织级使用,建议通过 /install-github-app 命令安装 GitHub App:
# 在 Claude Code 终端中运行
/install-github-app
这会创建专用的 GitHub App + OAuth 授权,比 Personal API Key 更安全——App 的权限可以精确到单个仓库,且可以随时吊销。
10.3 HashiCorp Vault / AWS Secrets Manager
对于使用私有 CI Runner 的团队:
# AWS Secrets Manager 示例
- name: Fetch API Key
run: |
ANTHROPIC_API_KEY=$(aws secretsmanager get-secret-value \
--secret-id anthropic-api-key \
--query SecretString --output text | jq -r '.ANTHROPIC_API_KEY')
echo "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" >> $GITHUB_ENV
十一、DeepSeek 用户在 CI 中的完整配置
Headless 模式对 DeepSeek 用户完全可用。只需要额外设置 ANTHROPIC_BASE_URL。
11.1 GitHub Actions 中的 DeepSeek 配置
- name: Claude Code Review (DeepSeek)
env:
ANTHROPIC_BASE_URL: https://api.deepseek.com/anthropic
ANTHROPIC_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
CLAUDE_CODE_MODEL: deepseek-v4-pro
run: |
claude -p "审查代码" \
--output-format json \
--max-turns 5 \
--max-budget-usd 0.10 \
--bare \
--no-session-persistence \
--dangerously-skip-permissions
DeepSeek 用户的 CI 成本优势:同样一个 PR 审查任务,Claude Sonnet 4.6 可能花 0.005-0.02/次(约为 1/10)。
11.2 模型选择建议
# 低复杂度任务(分类、摘要):用 Flash 省钱
CLAUDE_CODE_MODEL: deepseek-v4-flash
# 中等复杂度任务(代码审查、测试生成):用 Pro
CLAUDE_CODE_MODEL: deepseek-v4-pro
# 注意:DeepSeek 是纯文本模型,不要用包含图片分析的任务
11.3 DeepSeek 在 CI 中的限制
| 限制 | 影响 | 应对 |
|---|---|---|
| 纯文本模型 | 不能分析截图/图表 | CI 中很少需要图片分析 |
仅支持 text 输出 | --output-format stream-json 可用但部分事件可能缺失 | 用 json 格式最稳定 |
不支持 --json-schema | 结构化输出需在 prompt 中描述格式 | 在 prompt 中明确写出期望的 JSON 结构 |
Debug 速查卡
Debug 1:Error: stdin is not a TTY
报错:
Error: stdin is not a TTY
根因:在非交互环境中(CI、管道、脚本)调用了不带 -p 的 claude 命令。Claude Code 默认尝试打开 REPL,但 stdin 不是终端。
对比:
| 错误写法 | 正确写法 |
|---|---|
claude "审查代码" | claude -p "审查代码" |
echo "$DIFF" | claude | echo "$DIFF" | claude -p "审查" |
修复:给所有 CI/脚本中的 claude 命令加上 -p 标志。
验证:
echo "hello" | claude -p "回复 hi" --output-format text
# 应该返回 "hi" 或其他回复,不应报 TTY 错误
Debug 2:CI 中 claude -p 卡住不动
报错:CI job 运行到 claude -p 步骤后一直不返回,直到超时。
根因:-p 模式下,如果 Claude 使用了 --allowedTools 之外的未授权工具,它会试图请求权限——但 CI 环境没有交互界面,于是永远等待。
对比:
| 场景 | 行为 |
|---|---|
| 交互模式 + 未授权工具 | 弹出确认框 |
-p + --allowedTools "Read" + Claude 想用 Bash | 永久等待(无确认框,也不拒绝) |
-p + --dangerously-skip-permissions | 所有工具静默执行 |
-p + --permission-mode dontAsk + --allowedTools "Read,Edit" | 未授权工具被拒绝(不会卡住) |
修复:在 CI 环境中必须使用以下之一:
# 方案一:跳过所有权限检查(隔离容器)
claude -p "..." --dangerously-skip-permissions
# 方案二:明确拒绝未授权操作
claude -p "..." --permission-mode dontAsk --allowedTools "Read,Glob,Grep"
验证:在本地模拟 CI 环境:
echo "print('hello')" | claude -p "审查代码" --permission-mode dontAsk --allowedTools "Read" --output-format text --max-turns 1
# 应该 5 秒内返回结果或拒绝,不应卡住
Debug 3:--allowedTools 不生效,Claude 仍在使用其他工具
报错:你设置了 --allowedTools "Read,Edit",但 Claude 仍然跑了 Bash 命令。
根因:混淆了 --allowedTools 和 --tools。--allowedTools 只跳过权限确认,不限制工具可用性。不限制的工具仍然可用。
对比:
| Flag | 效果 | Claude 能用什么工具 |
|---|---|---|
--allowedTools "Read" | Read 不需确认 | 所有工具都可用(需确认的会卡住) |
--tools "Read" | 只暴露 Read 工具 | 只有 Read 工具 |
--tools "Read" --allowedTools "Read" | 只有 Read,且不需确认 | 只有 Read,不卡 |
修复:
# 如果你想让 Claude 只能用 Read + Grep,且不卡住
claude -p "分析代码" \
--tools "Read,Grep" \
--permission-mode acceptEdits
验证:
claude -p "当前目录有什么文件?用 Bash(ls) 看一下" \
--tools "Read,Grep" --permission-mode acceptEdits --output-format json --max-turns 1
# Claude 应该回复"我没有执行命令的能力"或类似信息,因为 Bash 工具不在 tools 列表中
Debug 4:JSON 解析失败——输出不是合法的 JSON
报错:
parse error: Expected string but got "..."
根因:使用 json 格式但解析了整个 stdout 包含非 JSON 内容,或用 JSON.parse 解析了 NDJSON 流。
对比:
| 配置 | 行为 | 正确解析方式 |
|---|---|---|
--output-format json | 单个 JSON 对象 | jq '.' 或 JSON.parse() |
--output-format stream-json | 每行独立 JSON(NDJSON) | 逐行 jq '.' |
--output-format stream-json + 用 JSON.parse(entire) | 必然失败——NDJSON 不是合法 JSON |
修复:
# 对于 json 格式:直接用 jq 解析
claude -p "..." --output-format json | jq -r '.result'
# 对于 stream-json 格式:逐行解析
claude -p "..." --output-format stream-json --verbose | while IFS= read -r line; do
echo "$line" | jq -r '.type' 2>/dev/null || true
done
# 安全地提取最终结果(stream-json)
claude -p "..." --output-format stream-json --verbose | \
jq -r 'select(.type == "result") | .result // empty'
验证:
claude -p "回复 hello world" --output-format json --max-turns 1 | jq -r '.result'
# 应该输出 "hello world" 或类似回复,jq 不应报错
Debug 5:CI Runner 上 claude: command not found
报错:
claude: command not found
根因:Claude Code 包安装失败或 Node.js 版本过低(要求 Node 18+)。
对比:
| Runner | 默认 Node 版本 | Claude Code 兼容 |
|---|---|---|
ubuntu-latest (2026) | 22.x | ✓ |
ubuntu-22.04 | 18.x | ✓ |
ubuntu-20.04 | 16.x(已弃用) | ✗ |
macos-latest | 22.x | ✓ |
node:20-alpine (Docker) | 20.x | ✓ |
修复:
# 总是显式指定 Node 版本
- uses: actions/setup-node@v4
with:
node-version: '22'
# 安装后验证
- name: Install Claude Code
run: |
npm install -g @anthropic-ai/claude-code
claude --version
DeepSeek 用户额外注意:某些旧版 Claude Code 不支持自定义 ANTHROPIC_BASE_URL。确保使用最新版:
npm install -g @anthropic-ai/claude-code@latest
claude --version # 应输出 >= v2.1.32
验证:
docker run --rm node:22-alpine sh -c "npm install -g @anthropic-ai/claude-code && claude --version"
# 应输出版本号,不报错
速查卡
路径汇总
| 文件 / 目录 | 用途 |
|---|---|
.github/workflows/claude-*.yml | GitHub Actions 工作流定义 |
.gitlab-ci.yml | GitLab CI 管道定义 |
Jenkinsfile | Jenkins 管道定义 |
managed-settings.json | 企业级强制权限策略 |
~/.claude/settings.json | 用户级配置(CI 中用 --bare 跳过) |
.claude/settings.json | 项目级配置(CI 中用 --bare 跳过) |
Headless 模式命令速查
# 基础用法
claude -p "你的 prompt" # 非交互执行
echo "$DATA" | claude -p "分析" # 管道输入
# 输出格式
claude -p "..." --output-format text # 默认,人类可读
claude -p "..." --output-format json # 结构化,含 cost
claude -p "..." --output-format stream-json --verbose # 实时流
claude -p "..." --output-format stream-json --verbose --include-partial-messages # token 级流
# 安全控制
claude -p "..." --max-turns 5 # 限制轮数
claude -p "..." --max-budget-usd 0.50 # 限制费用
claude -p "..." --tools "Read,Glob,Grep" # 限制可用工具
claude -p "..." --allowedTools "Read,Bash(git diff *)" # 跳过指定工具的确认
claude -p "..." --disallowedTools "Bash(curl *)" # 移除指定工具
claude -p "..." --permission-mode dontAsk # 未授权操作→拒绝
claude -p "..." --permission-mode acceptEdits # 编辑放行,其他需确认
claude -p "..." --dangerously-skip-permissions # 全开(仅隔离环境)
# CI 优化
claude -p "..." --bare # 跳过 hooks/skills/MCP/CLAUDE.md
claude -p "..." --no-session-persistence # 不写会话到磁盘
claude -p "..." --bare --add-dir . # bare 模式 + 显式加载 CLAUDE.md
# 会话管理
claude -p "第一步" --session-id "ci-task-42" # 命名会话
claude -p "第二步" --resume --session-id "ci-task-42" # 续接会话
# 环境变量
ANTHROPIC_API_KEY=sk-ant-api03-xxxx # Anthropic API Key
ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic # DeepSeek 端点
CLAUDE_CODE_MODEL=deepseek-v4-pro # 指定模型
CLAUDE_CODE_MAX_TURNS=10 # 默认 max-turns
报错速查
| 报错信息 | 根因 | 修复关键词 |
|---|---|---|
stdin is not a TTY | 缺少 -p | 加 -p |
| CI 步骤卡住不返回 | 缺少 --dangerously-skip-permissions | 加权限 flag |
--allowedTools 不生效 | 混淆了 allowedTools 和 --tools | 改用 --tools |
| JSON 解析失败 | 用 JSON.parse 解析 NDJSON 流 | 逐行解析 |
claude: command not found | Node 版本 < 18 | 升级到 Node 22 |
扩展阅读
- 高手进阶(二):Routines 云端自动化——让 Claude Code 在关机后继续干活 —— Routines vs Headless+CI 的详细对比
- 新手上路(一):Claude Code 六种权限模式 ——
--permission-mode的完整说明 - 新手上路(四):MCP 协议实战 —— CI 环境中的 MCP 配置
- 高手进阶(五):子代理与并行开发 —— 子代理在 CI 中的应用
- 高手进阶(三):代码审查 2026 —— 自动代码审查深度指南
参考文献
- Run Claude Code programmatically — Claude Code Docs — Headless 模式官方文档
- CLI reference — Claude Code Docs — 完整 CLI flag 参考
- Claude Code GitHub Actions — Claude Code Docs — 官方 GitHub Actions 文档
- anthropics/claude-code-action — GitHub — 官方 Action 仓库
- Headless Mode and CI/CD — SFEIR Institute — Headless + CI 综合教程
- Headless Claude in CI — AgentPatterns.ai — CI 安全最佳实践
- Claude Code Headless Mode: Complete Self-Hosting Guide — amux —
--bare模式与权限组合 - How to Build a PR Auto-Review Pipeline — jangwook.net — CI flags 实战组合
- Claude Code stream-json — Background Claude — stream-json 深度解析
- Claude Code Headless Mode: --print Flag and CI Use — LLMversus —
-p模式与交互模式的差异