> 本文记录了在 GitLab 项目中集成 Claude Code CLI,实现"给 Issue 打标签即可自动修复 Bug"的完整方案与实践过程。
---
## 目录
- [方案概述](#方案概述)
- [整体架构](#整体架构)
- [前置条件](#前置条件)
- [第一步:配置 CI/CD Variables](#第一步配置-cicd-variables)
- [第二步:创建 Pipeline Trigger Token](#第二步创建-pipeline-trigger-token)
- [第三步:编写 .gitlab-ci.yml](#第三步编写-gitlab-ciyml)
- [Job 1: claude:scan-issues — 扫描 Issue](#job-1-claudescan-issues--扫描-issue)
- [Job 2: claude:fix-issue — 执行修复](#job-2-claudefix-issue--执行修复)
- [第四步:创建定时调度](#第四步创建定时调度)
- [第五步:编写 CLAUDE.md 项目指南](#第五步编写-claudemd-项目指南)
- [使用流程](#使用流程)
- [手动触发脚本](#手动触发脚本)
- [关键设计细节](#关键设计细节)
- [踩坑记录与调试经验](#踩坑记录与调试经验)
- [完整 .gitlab-ci.yml 参考](#完整-gitlab-ciyml-参考)
---
## 方案概述
核心思路:利用 GitLab CI/CD 的 **Schedule(定时调度)** + **Trigger(触发器)** 机制,定时扫描带有 `auto-fix` 标签的 Issue,自动启动 Claude Code CLI 分析代码、修复 Bug、提交代码并在 Issue 上回复修复摘要。
**一句话流程**:开发者给 Issue 打上 `auto-fix` 标签 → 定时任务自动触发 → Claude Code 读取 Issue、分析代码、实施修复 → 自动 commit & push → 在 Issue 评论中报告结果。
---
## 整体架构
```
┌─────────────────────────────────────────────────────────────────┐
│ GitLab 项目 │
│ │
│ ┌──────────┐ ┌──────────────────┐ ┌───────────────────┐ │
│ │ Issue │ │ Schedule (定时) │ │ Trigger Pipeline │ │
│ │ auto-fix │───▶│ claude:scan-issues│───▶│ claude:fix-issue │ │
│ │ 标签 │ │ (alpine:3.19) │ │ (node:20) │ │
│ └──────────┘ └──────────────────┘ └─────────┬─────────┘ │
│ │ │
│ ┌───────────▼─────────┐ │
│ │ Claude Code CLI │ │
│ │ (npm i -g 安装) │ │
│ │ │ │
│ │ 1. 读取 Issue 详情 │ │
│ │ 2. 分析项目代码 │ │
│ │ 3. 实施修复 │ │
│ │ 4. 验证类型检查 │ │
│ │ 5. 提交 & 推送 │ │
│ │ 6. 评论 Issue │ │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### 标签状态机
Issue 通过标签流转追踪处理状态,确保不会被重复处理:
```
auto-fix ──▶ claude-processing ──▶ claude-done
(用户添加) (扫描时切换) (修复完成后设置)
```
---
## 前置条件
| 条件 | 说明 |
|------|------|
| GitLab 实例 | 需要支持 CI/CD Schedules 和 Pipeline Triggers |
| Runner | 可执行 Docker 镜像的 GitLab Runner |
| Anthropic API 访问 | 直接使用 Anthropic API 或通过内部代理 |
| GitLab Personal Access Token | 需要 `api` scope,用于操作 Issue 和推送代码 |
---
## 第一步:配置 CI/CD Variables
进入 GitLab 项目 → **Settings → CI/CD → Variables**,添加以下变量:
| 变量名 | 类型 | 说明 |
|--------|------|------|
| `GITLAB_ACCESS_TOKEN` | Masked | Personal Access Token(api scope),扫描 Job 使用 |
| `ANTHROPIC_AUTH_TOKEN` | Masked | Anthropic API Key(或内部代理的 auth token) |
| `TRIGGER_TOKEN` | 见下一步 | Pipeline 触发器 Token |
> **安全提示**:`GITLAB_ACCESS_TOKEN` 和 `ANTHROPIC_AUTH_TOKEN` 务必勾选 **Masked**,防止在日志中泄露。
### 关于 Anthropic API 访问
如果你直接使用 Anthropic 官方 API,需要设置:
- `ANTHROPIC_AUTH_TOKEN` = 你的 Anthropic API Key
如果使用内部代理(如本项目使用 `openai.nie.netease.com`),则在 CI Job 中通过环境变量指定:
```yaml
variables:
ANTHROPIC_BASE_URL: "https://openai.nie.netease.com" # 内部代理地址
```
---
## 第二步:创建 Pipeline Trigger Token
进入 **Settings → CI/CD → Pipeline trigger tokens**,点击 **Add trigger** 创建一个触发器。
创建后会得到一个 Token,将其记录下来。扫描 Job 将用此 Token 触发修复 Pipeline。
将该 Token 设置为 CI/CD Variable `TRIGGER_TOKEN`。
---
## 第三步:编写 .gitlab-ci.yml
在 `.gitlab-ci.yml` 中添加 `ai_fix` Stage 和两个 Job。
### Job 1: claude:scan-issues — 扫描 Issue
这是一个轻量级 Job,使用 `alpine:3.19` 镜像,只做 API 调用:
```yaml
stages:
- ai_fix
claude:scan-issues:
stage: ai_fix
image: alpine:3.19
before_script:
- apk add --no-cache curl jq
script:
- |
echo "Scanning for Issues with label 'auto-fix'..."
# 校验必要变量
for var in GITLAB_ACCESS_TOKEN TRIGGER_TOKEN ANTHROPIC_AUTH_TOKEN; do
eval "val=\$$var"
if [ -z "$val" ]; then
echo "ERROR: $var is not set."
exit 1
fi
done
# 查询带 auto-fix 标签的 Issue,排除已处理的
ISSUES=$(curl -s --header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues?labels=auto-fix¬%5Blabels%5D=claude-processing,claude-done&state=opened&per_page=10")
COUNT=$(echo "$ISSUES" | jq 'length')
echo "Found $COUNT issue(s) with 'auto-fix' label."
if [ "$COUNT" -eq 0 ] || [ "$COUNT" = "null" ]; then
echo "Nothing to do."
exit 0
fi
# 逐个处理
echo "$ISSUES" | jq -r '.[].iid' | while read -r IID; do
TITLE=$(echo "$ISSUES" | jq -r ".[] | select(.iid == $IID) | .title")
echo ">>> Triggering fix for Issue #${IID}: ${TITLE}"
# 1) 切换标签: auto-fix → claude-processing
curl -s --request PUT \
--header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--data "{\"remove_labels\": \"auto-fix\", \"add_labels\": \"claude-processing\"}" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$IID" > /dev/null
# 2) 通过 Trigger API 触发修复 Pipeline
curl -s -X POST \
--form "token=${TRIGGER_TOKEN}" \
--form "ref=${CI_DEFAULT_BRANCH}" \
--form "variables[ISSUE_IID]=${IID}" \
--form "variables[GITLAB_TOKEN]=${GITLAB_ACCESS_TOKEN}" \
--form "variables[ANTHROPIC_AUTH_TOKEN]=${ANTHROPIC_AUTH_TOKEN}" \
"https://${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/trigger/pipeline"
done
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"' # 定时触发
- if: '$CI_PIPELINE_SOURCE == "web"' # 手动触发
when: manual
allow_failure: true
timeout: 5m
```
**核心逻辑**:
1. 通过 GitLab Issues API 查询 `labels=auto-fix` 且排除 `claude-processing` 和 `claude-done` 的 Issue
2. 对每个 Issue 切换标签(防止重复处理)
3. 通过 Pipeline Trigger API 启动独立的修复 Pipeline,传入 `ISSUE_IID`、`GITLAB_TOKEN`、`ANTHROPIC_AUTH_TOKEN`
### Job 2: claude:fix-issue — 执行修复
这是核心修复 Job,使用 `node:20` 镜像,安装并运行 Claude Code:
```yaml
claude:fix-issue:
stage: ai_fix
image: node:20
variables:
GIT_STRATEGY: clone
GIT_DEPTH: 0 # 完整克隆,Claude 需要完整历史
ANTHROPIC_BASE_URL: "https://api.anthropic.com" # 或内部代理地址
ANTHROPIC_MODEL: "claude-sonnet-4-6"
DISABLE_AUTOUPDATER: "1" # 禁止 Claude Code 自动更新
DISABLE_PROMPT_CACHING: "1"
before_script:
- apt-get update && apt-get install -y git curl jq
- npm install -g @anthropic-ai/claude-code
- git config --global user.email "claude-bot@example.com"
- git config --global user.name "Claude AI Bot"
script:
- |
# Step 1: 获取 Issue 详情
if [ -z "$ISSUE_IID" ]; then
echo "ERROR: ISSUE_IID variable is required."
exit 1
fi
ISSUE_JSON=$(curl -s --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$ISSUE_IID")
ISSUE_TITLE=$(echo "$ISSUE_JSON" | jq -r '.title // empty')
ISSUE_DESCRIPTION=$(echo "$ISSUE_JSON" | jq -r '.description // empty')
if [ -z "$ISSUE_TITLE" ]; then
echo "ERROR: Could not fetch Issue #$ISSUE_IID"
exit 1
fi
echo "Processing Issue #${ISSUE_IID}: ${ISSUE_TITLE}"
- |
# Step 2: 切换到主分支
git checkout "$CI_DEFAULT_BRANCH"
git pull origin "$CI_DEFAULT_BRANCH"
- |
# Step 3: 运行 Claude Code
export ANTHROPIC_API_KEY="${ANTHROPIC_AUTH_TOKEN}"
PROMPT="你是一个代码修复助手。请分析并修复以下 GitLab Issue:
## Issue #${ISSUE_IID}: ${ISSUE_TITLE}
${ISSUE_DESCRIPTION}
请:
1. 分析项目代码,定位问题根因
2. 实施修复
3. 确保代码能通过类型检查(TypeScript 项目运行 npx tsc --noEmit)
4. 用中文输出修复摘要(包括修改了哪些文件、改了什么、为什么这样改)
项目根目录已准备就绪。"
RESULT=$(claude -p "$PROMPT" \
--bare \
--permission-mode acceptEdits \
--allowedTools "Bash,Read,Edit,Write,Glob,Grep" \
--max-turns 30 \
--output-format text \
2>&1) || true
echo "$RESULT" > repair_summary.txt
- |
# Step 4: 提交并推送
git add -A
git reset -- repair_summary.txt claude_stderr.txt 2>/dev/null || true
if git diff --cached --quiet; then
echo "No file changes detected."
COMMIT_STATUS="no_changes"
else
git commit -m "fix(auto): resolve Issue #${ISSUE_IID} - ${ISSUE_TITLE} [skip ci]"
git push "https://oauth2:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" \
HEAD:"$CI_DEFAULT_BRANCH"
COMMIT_STATUS="pushed"
fi
- |
# Step 5: 在 Issue 上发表评论
SUMMARY=$(cat repair_summary.txt | head -200 | \
sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | sed ':a;N;$!ba;s/\n/\\n/g')
if [ "$COMMIT_STATUS" = "pushed" ]; then
BODY="## ✅ Claude Code 自动修复完成\n\n修复已推送到 \`${CI_DEFAULT_BRANCH}\`。\n\nPipeline: ${CI_PIPELINE_URL}\n\n修复详情
\n\n\`\`\`\n${SUMMARY}\n\`\`\`\n "
else
BODY="## ⚠️ Claude Code 分析完成\n\n未检测到需要变更的文件。\n\nPipeline: ${CI_PIPELINE_URL}\n\n分析详情
\n\n\`\`\`\n${SUMMARY}\n\`\`\`\n "
fi
curl -s --request POST \
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
--header "Content-Type: application/json" \
--data "{\"body\": \"${BODY}\"}" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$ISSUE_IID/notes"
- |
# Step 6: 更新标签为 claude-done
curl -s --request PUT \
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
--header "Content-Type: application/json" \
--data "{\"remove_labels\": \"claude-processing\", \"add_labels\": \"claude-done\"}" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$ISSUE_IID" > /dev/null
rules:
- if: '$CI_PIPELINE_SOURCE == "trigger" && $ISSUE_IID' # 被 scan-issues 触发
- if: '$CI_PIPELINE_SOURCE == "web" && $ISSUE_IID' # 手动触发(需提供 ISSUE_IID)
when: manual
allow_failure: true
timeout: 30m
```
**Claude Code CLI 关键参数说明**:
| 参数 | 说明 |
|------|------|
| `-p "$PROMPT"` | 非交互模式,直接传入提示词 |
| `--bare` | 精简输出,去掉装饰性格式 |
| `--permission-mode acceptEdits` | 自动接受文件编辑(CI 环境必需) |
| `--allowedTools "Bash,Read,Edit,Write,Glob,Grep"` | 限制可用工具,仅保留文件系统操作 |
| `--max-turns 30` | 最大对话轮次,防止无限循环 |
| `--output-format text` | 纯文本输出 |
---
## 第四步:创建定时调度
进入 **CI/CD → Schedules**,创建定时任务:
| 配置项 | 建议值 | 说明 |
|--------|--------|------|
| Description | Auto-fix Issue Scanner | 任务描述 |
| Interval Pattern | `*/5 * * * *` | 每 5 分钟扫描一次 |
| Cron Timezone | 你的时区 | 例如 Asia/Shanghai |
| Target branch | `main` | 主分支 |
保存后,Schedule 会按设定频率运行 `claude:scan-issues` Job。
---
## 第五步:编写 CLAUDE.md 项目指南
在项目根目录创建 `CLAUDE.md`,Claude Code 在 CI 中运行时会自动读取此文件作为上下文指导:
```markdown
# Project Guidelines
## 项目结构
- `src/` — 主要源码
- `tests/` — 测试文件
## 开发规范
- 修改 TypeScript 代码后运行 `npx tsc --noEmit` 验证类型
- 不要修改 `.gitlab-ci.yml`(除非 Issue 明确要求)
## 关键技术
- 描述项目使用的框架、库、协议等
```
这个文件非常重要,它相当于给 Claude Code 提供了"项目知识",让 AI 了解项目结构和约束,从而做出更合理的修复。
---
## 使用流程
### 自动模式
1. 在 GitLab 上创建一个 Issue,描述清楚 Bug 的现象、复现步骤、期望行为
2. 给该 Issue 添加标签 **`auto-fix`**
3. 等待定时调度运行(默认每 5 分钟扫描一次)
4. 标签自动变为 `claude-processing`(正在处理)
5. 修复完成后标签变为 `claude-done`,Issue 评论中会出现修复摘要
6. 如果有代码变更,commit 会自动推送到 `main` 分支
### 手动模式(命令行触发)
项目提供了便捷脚本 `scripts/trigger-fix.sh`:
```bash
# 设置环境变量
export TRIGGER_TOKEN="你的 Trigger Token"
export PROJECT_ID="项目 ID" # Settings → General 可查看
export GITLAB_TOKEN="你的 PAT"
export ANTHROPIC_AUTH_TOKEN="你的 API Key"
# 触发修复
./scripts/trigger-fix.sh 42 # 42 是 Issue 的 IID
```
脚本内容很简洁,直接调用 GitLab Pipeline Trigger API:
```bash
curl -X POST \
--form "token=${TRIGGER_TOKEN}" \
--form "ref=main" \
--form "variables[ISSUE_IID]=${ISSUE_IID}" \
--form "variables[GITLAB_TOKEN]=${GITLAB_TOKEN}" \
--form "variables[ANTHROPIC_AUTH_TOKEN]=${ANTHROPIC_AUTH_TOKEN}" \
"https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/trigger/pipeline"
```
---
## 关键设计细节
### 1. 为什么用两个 Job + Trigger 而非一个 Job?
- **隔离性**:扫描和修复在不同 Pipeline 中运行,一个 Issue 修复失败不影响其他 Issue
- **并行**:多个 Issue 可以同时被触发修复(各自独立的 Pipeline)
- **可追溯**:每个修复有独立的 Pipeline 记录
### 2. 标签状态机防重复
```
auto-fix → claude-processing → claude-done
```
- 扫描 Query 排除 `claude-processing` 和 `claude-done`
- 保证每个 Issue **只被处理一次**
### 3. `[skip ci]` 避免递归触发
commit message 中包含 `[skip ci]`,防止修复 commit 触发新的 Pipeline,形成无限循环。
### 4. 临时文件排除
`repair_summary.txt` 和 `claude_stderr.txt` 通过 `git reset` 排除暂存区,通过 `.gitignore` 排除版本控制:
```gitignore
repair_summary.txt
claude_stderr.txt
```
### 5. Upload Job 与 Fix Job 互不干扰
Upload Job 的 rules 中明确排除 `schedule` 和 `trigger` 来源:
```yaml
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: never
- if: '$CI_PIPELINE_SOURCE == "trigger"'
when: never
```
### 6. 完整克隆
```yaml
variables:
GIT_STRATEGY: clone
GIT_DEPTH: 0
```
Claude Code 需要完整的仓库来分析代码,不能使用浅克隆。
---
## 踩坑记录与调试经验
### 1. Anthropic API 配置
**问题**:Claude Code CLI 的环境变量名是 `ANTHROPIC_API_KEY`,但 CI 变量传递时用了别的名字。
**解决**:在 script 中显式映射:
```bash
export ANTHROPIC_API_KEY="${ANTHROPIC_AUTH_TOKEN}"
```
如果使用内部代理,还需设置 `ANTHROPIC_BASE_URL`。额外的 `ANTHROPIC_MODEL` 和 `ANTHROPIC_CUSTOM_MODEL_OPTION` 变量用于指定模型。
### 2. Claude Code 自动更新
**问题**:CI 环境中 Claude Code 尝试自动更新,导致超时或失败。
**解决**:设置环境变量禁用:
```yaml
DISABLE_AUTOUPDATER: "1"
```
### 3. Issue 查询排除已处理的 Issue
**问题**:早期版本未排除 `claude-processing`/`claude-done`,导致同一 Issue 被反复触发。
**解决**:使用 GitLab API 的 `not[labels]` 参数:
```
?labels=auto-fix¬%5Blabels%5D=claude-processing,claude-done&state=opened
```
### 4. Git Push 认证
**问题**:CI 环境默认的 git remote 可能不支持 push。
**解决**:使用 OAuth2 Token 方式构建 push URL:
```bash
git push "https://oauth2:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" HEAD:main
```
### 5. 中文输出
**问题**:Claude Code 默认可能以英文回复。
**解决**:在 Prompt 中明确要求 "请使用中文回复",确保修复摘要和 Issue 评论为中文。
### 6. 评论中的特殊字符转义
**问题**:Claude 的输出可能包含 `"`、`\`、换行等字符,直接放入 JSON 会破坏格式。
**解决**:通过 `sed` 链式转义:
```bash
SUMMARY=$(cat repair_summary.txt | head -200 | \
sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | sed ':a;N;$!ba;s/\n/\\n/g')
```
---
## 完整 .gitlab-ci.yml 参考
以下是 `ai_fix` Stage 的完整配置,可直接复制到你的 `.gitlab-ci.yml` 中:
```yaml
stages:
# ... 你的其他 stages
- ai_fix
# ============================================================
# AI Fix Stage - Auto-fix GitLab Issues with Claude Code
# ============================================================
# Required CI/CD Variables:
# - GITLAB_ACCESS_TOKEN: Personal Access Token (api scope)
# - ANTHROPIC_AUTH_TOKEN: Anthropic API Key
# - TRIGGER_TOKEN: Pipeline trigger token
#
# Setup:
# 1. Create a Schedule (CI/CD → Schedules), e.g. every 5 min
# 2. Add label "auto-fix" to an Issue → auto triggers fix
claude:scan-issues:
stage: ai_fix
image: alpine:3.19
before_script:
- apk add --no-cache curl jq
script:
- |
for var in GITLAB_ACCESS_TOKEN TRIGGER_TOKEN ANTHROPIC_AUTH_TOKEN; do
eval "val=\$$var"
if [ -z "$val" ]; then echo "ERROR: $var not set."; exit 1; fi
done
ISSUES=$(curl -s --header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues?labels=auto-fix¬%5Blabels%5D=claude-processing,claude-done&state=opened&per_page=10")
COUNT=$(echo "$ISSUES" | jq 'length')
echo "Found $COUNT issue(s)."
[ "$COUNT" -eq 0 ] || [ "$COUNT" = "null" ] && exit 0
echo "$ISSUES" | jq -r '.[].iid' | while read -r IID; do
TITLE=$(echo "$ISSUES" | jq -r ".[] | select(.iid == $IID) | .title")
echo ">>> Issue #${IID}: ${TITLE}"
curl -s --request PUT \
--header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--data "{\"remove_labels\": \"auto-fix\", \"add_labels\": \"claude-processing\"}" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$IID" > /dev/null
curl -s -X POST \
--form "token=${TRIGGER_TOKEN}" \
--form "ref=${CI_DEFAULT_BRANCH}" \
--form "variables[ISSUE_IID]=${IID}" \
--form "variables[GITLAB_TOKEN]=${GITLAB_ACCESS_TOKEN}" \
--form "variables[ANTHROPIC_AUTH_TOKEN]=${ANTHROPIC_AUTH_TOKEN}" \
"https://${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/trigger/pipeline"
done
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
- if: '$CI_PIPELINE_SOURCE == "web"'
when: manual
allow_failure: true
timeout: 5m
claude:fix-issue:
stage: ai_fix
image: node:20
variables:
GIT_STRATEGY: clone
GIT_DEPTH: 0
ANTHROPIC_BASE_URL: "https://api.anthropic.com"
ANTHROPIC_MODEL: "claude-sonnet-4-6"
DISABLE_AUTOUPDATER: "1"
DISABLE_PROMPT_CACHING: "1"
before_script:
- apt-get update && apt-get install -y git curl jq
- npm install -g @anthropic-ai/claude-code
- git config --global user.email "claude-bot@example.com"
- git config --global user.name "Claude AI Bot"
script:
- |
[ -z "$ISSUE_IID" ] && echo "ERROR: ISSUE_IID required." && exit 1
[ -z "$GITLAB_TOKEN" ] && echo "ERROR: GITLAB_TOKEN required." && exit 1
ISSUE_JSON=$(curl -s --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$ISSUE_IID")
ISSUE_TITLE=$(echo "$ISSUE_JSON" | jq -r '.title // empty')
ISSUE_DESCRIPTION=$(echo "$ISSUE_JSON" | jq -r '.description // empty')
[ -z "$ISSUE_TITLE" ] && echo "ERROR: Cannot fetch Issue" && exit 1
- |
git checkout "$CI_DEFAULT_BRANCH" && git pull origin "$CI_DEFAULT_BRANCH"
- |
export ANTHROPIC_API_KEY="${ANTHROPIC_AUTH_TOKEN}"
PROMPT="你是一个代码修复助手。请使用中文回复。请分析并修复以下 GitLab Issue:
## Issue #${ISSUE_IID}: ${ISSUE_TITLE}
${ISSUE_DESCRIPTION}
请:
1. 分析项目代码,定位问题根因
2. 实施修复
3. 确保代码能通过类型检查(TypeScript 项目运行 npx tsc --noEmit)
4. 用中文输出修复摘要
项目根目录已准备就绪。"
RESULT=$(claude -p "$PROMPT" \
--bare \
--permission-mode acceptEdits \
--allowedTools "Bash,Read,Edit,Write,Glob,Grep" \
--max-turns 30 \
--output-format text \
2>&1) || true
echo "$RESULT" > repair_summary.txt
- |
git add -A
git reset -- repair_summary.txt claude_stderr.txt 2>/dev/null || true
if git diff --cached --quiet; then
COMMIT_STATUS="no_changes"
else
git commit -m "fix(auto): resolve Issue #${ISSUE_IID} - ${ISSUE_TITLE} [skip ci]"
git push "https://oauth2:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" \
HEAD:"$CI_DEFAULT_BRANCH"
COMMIT_STATUS="pushed"
fi
- |
SUMMARY=$(cat repair_summary.txt | head -200 | \
sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | sed ':a;N;$!ba;s/\n/\\n/g')
if [ "$COMMIT_STATUS" = "pushed" ]; then
BODY="## ✅ Claude Code 自动修复完成\n\n修复已推送到 \`${CI_DEFAULT_BRANCH}\`。\nPipeline: ${CI_PIPELINE_URL}\n\n修复详情
\n\n\`\`\`\n${SUMMARY}\n\`\`\`\n "
else
BODY="## ⚠️ Claude Code 分析完成\n\n未检测到需要变更的文件。\nPipeline: ${CI_PIPELINE_URL}\n\n分析详情
\n\n\`\`\`\n${SUMMARY}\n\`\`\`\n "
fi
curl -s --request POST \
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
--header "Content-Type: application/json" \
--data "{\"body\": \"${BODY}\"}" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$ISSUE_IID/notes"
- |
curl -s --request PUT \
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
--header "Content-Type: application/json" \
--data "{\"remove_labels\": \"claude-processing\", \"add_labels\": \"claude-done\"}" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$ISSUE_IID" > /dev/null
rules:
- if: '$CI_PIPELINE_SOURCE == "trigger" && $ISSUE_IID'
- if: '$CI_PIPELINE_SOURCE == "web" && $ISSUE_IID'
when: manual
allow_failure: true
timeout: 30m
```
---
## 总结
| 特性 | 实现方式 |
|------|----------|
| 触发机制 | GitLab Schedule 定时扫描 + Pipeline Trigger 触发修复 |
| AI 引擎 | Claude Code CLI (`@anthropic-ai/claude-code`) |
| 权限控制 | `--permission-mode acceptEdits` + `--allowedTools` 白名单 |
| 防重复 | 标签状态机 `auto-fix → claude-processing → claude-done` |
| 防递归 | commit message 中 `[skip ci]` |
| 结果反馈 | 自动在 Issue 上发表评论,含修复摘要 |
| 项目上下文 | `CLAUDE.md` 项目指南文件 |
这套方案已在实际项目中运行,成功自动修复了多个 Bug Issue。核心优势是**零人工介入**——开发者只需写好 Issue 描述并打上标签,AI 自动完成从分析到修复到提交的全流程。