Files
BlueRoseNote/07-Other/AI/AI Agent/ClaudeCode/在 GitLab CICD 中部署 Claude Code 自动修复 Issue Bug.md

691 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> 本文记录了在 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 Tokenapi 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&not%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<details><summary>修复详情</summary>\n\n\`\`\`\n${SUMMARY}\n\`\`\`\n</details>"
else
BODY="## ⚠️ Claude Code 分析完成\n\n未检测到需要变更的文件。\n\nPipeline: ${CI_PIPELINE_URL}\n\n<details><summary>分析详情</summary>\n\n\`\`\`\n${SUMMARY}\n\`\`\`\n</details>"
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&not%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&not%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<details><summary>修复详情</summary>\n\n\`\`\`\n${SUMMARY}\n\`\`\`\n</details>"
else
BODY="## ⚠️ Claude Code 分析完成\n\n未检测到需要变更的文件。\nPipeline: ${CI_PIPELINE_URL}\n\n<details><summary>分析详情</summary>\n\n\`\`\`\n${SUMMARY}\n\`\`\`\n</details>"
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 自动完成从分析到修复到提交的全流程。