工具链全景#
flowchart LR
subgraph 开发["开发环境"]
CC["Claude Code
(AI 编码)"]
end
subgraph VCS["版本控制"]
GL["GitLab
(代码托管)"]
end
subgraph CI["持续集成"]
Pipeline["CI Pipeline
(构建验证)"]
PRA["pr-agent
(AI Code Review)"]
end
subgraph CD["持续部署"]
SSH["SSH 远程部署
(Docker Compose)"]
end
CC -->|"git push"| GL
GL -->|"触发"| Pipeline
GL -->|"MR 创建"| PRA
PRA -->|"评审反馈"| CC
Pipeline -->|"构建成功"| SSH
style CC fill:#8b5cf6,color:#fff
style GL fill:#fc6d26,color:#fff
style Pipeline fill:#3b82f6,color:#fff
style PRA fill:#10b981,color:#fff
style SSH fill:#ef4444,color:#fff
| 工具 | 角色 | 核心能力 |
|---|---|---|
| Claude Code | AI 编码助手 | 代码编写、脚本批处理、问题诊断、Git 操作 |
| GitLab CI | 持续集成 | Hugo 构建验证、Docker 镜像打包 |
| pr-agent | AI 代码评审 | 自动 Code Review、安全扫描、改进建议 |
| glab CLI | GitLab 命令行 | MR 创建、CI 状态查询、变量管理 |
核心工作流#
每一次代码变更都遵循严格的闭环流程:
flowchart TD
A["1. Claude Code
分析需求 + 编写代码"] --> B["2. git commit
遵循 Conventional Commits"]
B --> C["3. git push
推送到功能分支"]
C --> D["4. glab mr create
创建 Merge Request"]
D --> E["5. CI Pipeline
自动触发构建"]
D --> F["6. /review
触发 pr-agent 评审"]
E --> G{"构建通过?"}
F --> H{"评审通过?"}
G -->|"失败"| I["诊断日志
修复问题"]
H -->|"有问题"| I
I --> B
G -->|"通过"| J{"全部通过?"}
H -->|"通过"| J
J -->|"是"| K["7. glab mr merge
--squash 合并"]
K --> L["8. 自动部署
SSH + Docker Compose"]
L --> M["9. 清理分支"]
style A fill:#8b5cf6,color:#fff
style F fill:#10b981,color:#fff
style K fill:#3b82f6,color:#fff
style L fill:#ef4444,color:#fff
实战案例详解#
案例 1:顺利通过——一次性合并#
MR #8:about 页面 shortcodes 增强
# 1. Claude Code 编写代码
# (修改 content/about.md,使用 timeline/alert/mermaid/button)
# 2. 提交推送
git checkout -b optimize/content-enhancement
git add content/about.md
git commit -m "style: 增强 about 页面,使用 Blowfish shortcodes 优化展示"
git push origin optimize/content-enhancement
# 3. 创建 MR + 触发评审
glab mr create --title "style: 增强 about 页面展示效果" --target-branch main
glab mr note 8 --message "/review"
pr-agent 评审结果:
评审工作量: 1/5
安全问题: 邮箱地址暴露(建议级别,非阻断)
重大问题: 无
邮箱是博主有意公开的联系方式,评审建议合理但不需要修复。直接合并。
案例 2:CI 失败 → 修复 → 再次通过#
MR #7:全面优化博客质量
第一次提交后 CI 构建失败:
ERROR: error calling Fill: this method is only available for raster images.
根因:启用了 author.svg 作为头像,但 Blowfish 主题对头像调用 .Fill 方法(仅支持 PNG/JPG)。
修复流程:
# 1. 诊断:查看 CI 日志
glab api "projects/.../jobs/47046/trace" | tail -20
# 2. 修复:SVG → PNG 转换
python3 -c "import cairosvg; cairosvg.svg2png(...)"
# 3. 追加提交
git add assets/img/author.png config/_default/languages.zh-cn.toml
git commit -m "fix: 修复 author image SVG 导致 Hugo 构建失败"
git push origin optimize/blog-quality-improvements
# 4. CI 自动重跑 → 通过 → 合并
案例 3:pr-agent 发现问题 → 修复 → 迭代#
MR #10:Favicon + 分类统一 + 分享按钮
pr-agent 评审发现了一个真实的命名不一致:
分类命名不一致:
categories: ["Docker", "docker-compose", "DevOps"]
建议统一命名风格,例如将 docker-compose 改为 Docker Compose,
以保持与其他分类的一致性。
修复:
# 接受评审建议,修改 2 个文件
# gitlab-deploy.md: "docker-compose" → "Docker Compose"
# nexus-use-traefik-proxy.md: "docker-compose" → "Docker Compose"
git add content/posts/gitlab-deploy.md content/posts/nexus-use-traefik-proxy.md
git commit -m "fix: 统一 docker-compose 分类命名为 Docker Compose"
git push origin optimize/favicon-sharing-categories
CI 重跑通过,合并。
案例 4:多轮迭代——SSH 部署调试#
MR #22 → #23 → #24 → #25:这是迭代最多的一组。
第 1 轮(MR #22):添加 SSH 部署 job,pr-agent 指出 StrictHostKeyChecking=no 安全问题,修复后合并。
第 2 轮(MR #23):部署失败,error in libcrypto。Docker Alpine 镜像的 OpenSSL 不支持 OpenSSH 密钥格式。改用默认 runner 镜像。
第 3 轮(MR #24):默认 runner 同样报 libcrypto 错误。添加 ssh-keygen -p -m PEM 运行时转换。
第 4 轮(MR #25):ssh-keygen 本身也无法读取 OpenSSH 格式。最终方案:本地预先转换为 PEM 格式,通过 glab variable update 重新上传。
# 本地转换密钥格式
ssh-keygen -p -m PEM -f /tmp/key -N "" -q
# -----BEGIN RSA PRIVATE KEY----- (PEM 格式)
# 更新 GitLab CI 变量
glab variable update SSH_PRIVATE_KEY --type file --value "$(cat /tmp/key)"
flowchart LR
A["MR #22
SSH 部署"] -->|"pr-agent:
安全问题"| B["修复
StrictHostKeyChecking"]
B --> C["MR #23
libcrypto 错误"]
C -->|"换 runner"| D["MR #24
仍然报错"]
D -->|"ssh-keygen
也失败"| E["MR #25
本地转 PEM"]
E -->|"重试 job"| F["部署成功"]
style A fill:#ef4444,color:#fff
style F fill:#10b981,color:#fff
复盘:4 轮迭代看似低效,但每轮都精确缩小了问题范围。如果一开始就"猜"答案直推 main,可能需要更多次试错还会影响线上。
Claude Code 的 Git 操作模式#
Claude Code 在整个流程中承担的不仅是"写代码",而是完整的 Git 工作流:
标准操作序列#
# 1. 创建功能分支
git checkout -b fix/xxx
# 2. 编写代码(Claude Code 核心工作)
# ... 修改文件 ...
# 3. 提交(Conventional Commits 规范)
git add <specific-files>
git commit -m "fix: 具体修复内容"
# 4. 推送
git push origin fix/xxx
# 5. 创建 MR
glab mr create --title "fix: ..." --target-branch main
# 6. 触发评审
glab mr note <mr-id> --message "/review"
# 7. 等待 CI + 评审
sleep 45 && glab ci list --per-page=1
# 8. 检查评审结果
glab api "projects/.../merge_requests/<id>/notes"
# 9. 合并
glab mr merge <id> --squash --yes
# 10. 清理
git checkout main && git pull && git branch -D fix/xxx
关键设计决策#
为什么用 squash merge?
每个 MR 可能有多次 fix 提交(主提交 + 评审修复),squash 合并后 main 分支保持干净的线性历史。
为什么每次都创建新分支?
GitLab 的 pre-receive hook 会校验提交信息格式,直接推 main 的长提交信息可能被拒绝。分支 + MR 是最安全的路径。
为什么用 glab 而不是 Web UI?
Claude Code 运行在终端环境,glab CLI 让整个流程可以自动化,无需切换到浏览器。从创建 MR 到合并到清理分支,全部在命令行完成。
pr-agent 评审模式分析#
评审触发方式#
# 在 MR 中添加评论触发
glab mr note <mr-id> --message "/review"
pr-agent 收到 /review 后自动分析 diff,生成 Persistent Review(会随新提交自动更新)。
评审输出结构#
每次评审包含 4 个维度:
| 维度 | 说明 | 示例 |
|---|---|---|
| Effort | 评审工作量(1-5) | 1 🔵⚪⚪⚪⚪ |
| Tests | 测试相关建议 | No relevant tests |
| Security | 安全扫描 | SSH 密钥暴露、邮箱泄露 |
| Focus Areas | 重点关注区域 | 具体代码行 + 建议 |
32 个 MR 的评审统计#
| 评审结果 | 数量 | 占比 |
|---|---|---|
| 无问题直接通过 | 19 | 59% |
| 有建议但非阻断 | 8 | 25% |
| 发现需修复的问题 | 5 | 16% |
pr-agent 发现的真实有效问题:
docker-compose分类命名不一致(MR #10)StrictHostKeyChecking=no安全风险(MR #22)- CSS 选择器全局作用域过大(MR #11)
- Favicon 文件路径需确认存在(MR #10)
- 硬编码路径缺乏灵活性(MR #11)
CI 流水线架构#
stages:
- build # Hugo 构建(MR + main 都触发)
- docker-build # Docker 镜像(仅 main)
- deploy # Pages + SSH 远程部署(仅 main)
flowchart TB
subgraph MR["MR 触发"]
B1["build
(Hugo 构建验证)"]
end
subgraph Main["main 触发"]
B2["build"] --> D["docker-build
(镜像打包推送)"]
D --> P["pages
(GitLab Pages)"]
D --> S["deploy-production
(SSH 远程部署)"]
end
style B1 fill:#3b82f6,color:#fff
style D fill:#f59e0b,color:#fff
style P fill:#10b981,color:#fff
style S fill:#ef4444,color:#fff
MR 阶段只跑 build 验证(Hugo 能否成功构建),不触发部署。这保证了评审阶段的安全性——即使代码有问题也不会影响线上。
合并到 main 后触发完整流水线:构建 → Docker 打包 → 双通道部署(Pages + SSH)。
效率数据#
时间分布#
| 环节 | 平均耗时 | 说明 |
|---|---|---|
| 代码编写 | 1-5 分钟 | Claude Code 自动化 |
| CI 构建 | 30-45 秒 | Hugo 增量构建 |
| pr-agent 评审 | 10-30 秒 | 自动触发 |
| 合并 + 部署 | 2 分钟 | 全自动 |
| 单次迭代 | 4-8 分钟 | 从提交到上线 |
32 个 MR 总耗时#
整个博客优化项目(32 个 MR、涉及 200+ 文件变更)在单次会话中完成。
最佳实践总结#
1. 小步提交,快速迭代#
每个 MR 只做一件事。宁可多开 4 个 MR 调试 SSH 部署(#22-#25),也不要把所有改动塞进一个巨型 MR。
2. CI 是安全网#
所有代码必须通过 CI 验证才能合并。author.svg 导致构建失败、summaryLength=0 导致卡片拉伸——这些问题都被 CI 拦住了。
3. 认真对待 pr-agent 建议#
pr-agent 的建议不一定都需要修复(59% 无问题直接通过),但 16% 的真实问题值得认真对待。docker-compose 命名不一致这种细节,人工评审很难注意到。
4. 善用 glab CLI#
# 创建 MR
glab mr create --title "..." --target-branch main
# 触发评审
glab mr note <id> --message "/review"
# 查看 CI 状态
glab ci list --per-page=1
# 查看评审结果
glab api "projects/.../merge_requests/<id>/notes"
# 合并
glab mr merge <id> --squash --yes
# 管理 CI 变量(敏感信息不入代码库)
glab variable set SSH_PRIVATE_KEY --type file --value "$(cat key)"
5. Conventional Commits 不是形式主义#
GitLab pre-receive hook 强制校验提交信息格式。feat:、fix:、style:、docs: 等前缀让 changelog 自动生成成为可能,也便于快速定位每个 MR 的性质。
附录:pr-agent 部署指南#
本项目的 pr-agent 采用 Helm Chart + Kubernetes 方式部署,源码托管在 devops/gitops-apps 仓库。
架构概览#
flowchart LR
GL["GitLab"] -->|"Webhook
(MR event)"| PA["pr-agent
(K8s Pod)"]
PA -->|"API 调用"| LLM["LLM 网关
(Claude Sonnet 4.6)"]
LLM -->|"评审结果"| PA
PA -->|"MR Comment"| GL
style GL fill:#fc6d26,color:#fff
style PA fill:#10b981,color:#fff
style LLM fill:#8b5cf6,color:#fff
Helm Chart 结构#
pr-agent/
├── Chart.yaml # Chart 元信息
├── values.yaml # 配置参数
└── templates/
├── deployment.yaml # Pod 部署
├── service.yaml # NodePort 服务
├── configmap.yaml # 环境变量
├── secret.yaml # 敏感信息
├── serviceaccount.yaml # 服务账号
└── ingress-traefik.yaml # Traefik 路由(可选)
核心配置(values.yaml)#
# 镜像配置
image:
repository: yangzun/pr-agent
tag: "gitlab"
pullPolicy: Always
# 服务端口
service:
type: NodePort
port: 38081
# LLM 模型配置
env:
CONFIG__MODEL: "claude-sonnet-4-6"
CONFIG__MODEL_TURBO: "claude-sonnet-4-6"
CONFIG__FALLBACK_MODELS: '["claude-sonnet-4-6"]'
CONFIG__CUSTOM_MODEL_MAX_TOKENS: "2048000"
# GitLab 集成
CONFIG__GIT_PROVIDER: "gitlab"
GITLAB__URL: "https://gitlab.cpinnov.run"
CONFIG__RESPONSE_LANGUAGE: "zh-CN"
# 评审行为
CONFIG__IGNORE_BOT_COMMENTS: "true"
PR_CODE_SUGGESTIONS__COMMITABLE_CODE_SUGGESTIONS: "true"
CONFIG__LARGE_PATCH_POLICY: "compress"
# 自定义标签
PR_DESCRIPTION__CUSTOM_LABELS: >-
["Bug 修复", "功能增强", "文档更新", "测试覆盖",
"配置变更", "代码重构", "CI/CD 维护", "依赖升级"]
PR_DESCRIPTION__PUBLISH_LABELS: "true"
部署步骤#
# 1. 克隆 GitOps 仓库
git clone git@gitlab.cpinnov.run:devops/gitops-apps.git
cd gitops-apps/pr-agent
# 2. 配置密钥(values.yaml 中的 secret 部分)
# GITLAB__PERSONAL_ACCESS_TOKEN: GitLab PAT(api scope)
# GITLAB__SHARED_SECRET: Webhook 签名密钥
# OPENAI__KEY: LLM API 密钥
# 3. 部署到 Kubernetes
helm install pr-agent . -n pr-agent --create-namespace
# 4. 配置 GitLab Webhook
# URL: http://<node-ip>:38081/webhook
# Trigger: Merge request events + Note events
# Secret: 与 GITLAB__SHARED_SECRET 一致
GitLab Webhook 配置#
在每个需要启用 pr-agent 的仓库中:
Settings → Webhooks → Add webhook
| 配置项 | 值 |
|---|---|
| URL | http://<pr-agent-host>:38081/webhook |
| Secret token | 与 GITLAB__SHARED_SECRET 一致 |
| Trigger | Merge request events, Comments |
| SSL verification | 按实际环境选择 |
配置完成后,在任意 MR 中评论 /review 即可触发 AI 代码评审。
写在最后#
这套工作流的核心价值不在于"AI 写了代码",而在于 AI 融入了完整的工程化闭环:
- Claude Code 不只是代码生成器,它理解 Git 规范、操作 glab CLI、诊断 CI 日志
- pr-agent 不只是语法检查器,它能发现命名不一致、安全隐患、架构建议
- CI Pipeline 不只是构建工具,它是防止错误上线的最后一道防线
三者协同,让 32 个 MR 在一次会话中从创建到上线,每个变更都经过构建验证和 AI 评审,没有一次直推 main。
查看博客优化复盘