☰
Greploop 在 GitLab 上的 Greptile 评审自动化:基于 glab api 的 MR 轮询、置信度提取与讨论闭环
2026/10/13 2:49:19 网站建设 项目流程
  • 人工智能
  • AI 应用
  • 语音
  • 音频
  • 本地部署
  • 桌面应用

【免费下载链接】auto-subs

On-device subtitle generation that connects directly to DaVinci Resolve, Premiere, and After Effects.

项目地址:https://gitcode.com/gh_mirrors/au/auto-subs
点击查看免费下载

本文聚焦 auto-subs 仓库中 Greploop Agent 技能的 GitLab 侧 API 参考文档 gitlab-api.md。Greploop 是一个驱动 AI 代码评审机器人 Greptile 反复评审 PR/MR 直至拿到 5/5 置信度且零未解决评论的循环工作流,本文以 GitLab(glabCLI + GitLab REST API)为对象,完整讲解如何用glab api获取 MR 详情、轮询 pipeline/job 状态、从 MR notes 中解析置信度分数、抓取并逐条解决 Greptile 的 inline 讨论。读完本文,你可以把整套“触发评审 → 轮询完成 → 提取结果 → 修复 → 解决讨论 → 再触发”的闭环命令组合直接落地到自己的 GitLab 项目上。

1. 背景:greploop 循环与 GitLab API 参考的定位

Greploop 技能的完整定义见 SKILL.md,其核心是一个有限轮次的迭代循环:

  1. 触发 Greptile 评审(GitHub 发@greptile review评论、GitLab 发 MR note、Perforce 重新 shelve);
  2. 轮询评审完成状态(GitHub 用 check runs、GitLab 用 pipelines/jobs);
  3. 从 MR/PR 描述、评论、review 中解析置信度分数(如3/5、5/5);
  4. 修复所有可执行的 inline 评论;
  5. 解决所有已处理的讨论线程;
  6. 提交并推送,回到第 1 步。

退出条件在 SKILL.md 中写得很明确:置信度 5/5 且零未解决评论,或达到最多 5 次迭代上限(防止失控循环)。

references/目录为该循环提供了两个平台的 API 参考:GitHub 侧是 graphql-queries.md(GraphQL review threads 与批量 resolve),GitLab 侧即本文主体 gitlab-api.md(REST +glab api)。SKILL.md 的 GitLab 轮询伪代码中直接引用了后者(原文见 SKILL.md 第 164 行的 “see GitLab API reference”)。

2. 前提:glab api 与 :fullpath 占位符

GitLab 侧所有命令都通过glab api发起 REST 请求,文档开头给出的关键前提是:

glab apiautomatically resolves:fullpathto the URL-encoded project path from the local git remote.

即glab api会自动从本地 git remote 解析出 URL 编码后的项目路径,替换命令中的:fullpath占位符。你不需要手动拼接owner%2Frepo,这对以下场景尤其关键:

  • 自托管 GitLab 实例(URL 编码规则可能与实例路径有关);
  • 仓库路径中包含特殊字符或大小写混用的 group 名;
  • 命令要在脚本中复用、不硬编码仓库标识的情况。

适用前提:本地目录是一个已配置origin远端的 git 工作区,且glab已完成认证(SKILL.md 的 compatibility 声明为 “Requires git, gh (GitHub CLI) or glab (GitLab CLI) authenticated, and Greptile installed on the repo”)。

另外注意一个平台映射关系(SKILL.md 中的 “Key field differences”):

概念GitHub 字段GitLab 字段
MR/PR 内部编号numberiid(用这个,不是全局id)
源分支headRefNamesource_branch
HEAD 提交headRefOidsha
评审触发issue comment@greptile reviewMR note@greptile review
评审运行状态check runspipelines + jobs
通用评论issue commentsMR notes
inline 评审线程GraphQL review threadsdiscussions(DiffNote)
解决线程resolveReviewThreadmutation(可批量)discussions PUT(无批量接口)

3. 获取 MR 详情

第一步永远是定位 MR 本体。文档给出的命令是glab mr view <MR_IID> --output json:

glab mr view <MR_IID> --output json

需要重点关注的字段(原文 Key fields 部分):

  • iid— 项目内部的 MR 编号。必须使用它而不是id:GitLab 中id是全局唯一的数据库主键,而所有merge_requests/<MR_IID>形式的 REST 路径参数都要求项目内的iid;
  • source_branch— 等价于 GitHub 的headRefName,后续git push前可用来确认自己位于正确的分支;
  • sha— 该 MR 当前 HEAD 提交的 SHA,后面“按提交 SHA 定位 pipeline”一节会用到;
  • description— MR 正文。注意Greptile 可能会在每轮评审后回写/更新这个字段(例如追加置信度分数块),所以每轮循环都要重新拉取而不是缓存。

SKILL.md 的实际用法是配合jq快速取两个字段并切分支:

glab mr view --output json | jq '{iid: .iid, branch: .source_branch}'

4. 触发 Greptile 评审:MR note

GitLab 上没有 GitHub check run 那种显式触发接口,Greploop 的触发方式是给 MR 发一条 note(评论):

glab mr note <MR_IID> --message "@greptile review"

触发前有一个重要的去重判断:先查当前 MR 是否已有 pipeline 在跑,避免重复触发(完整命令见下一节):

PIPELINES=$(glab api "projects/:fullpath/merge_requests/<MR_IID>/pipelines") GREPTILE_RUNNING=$(echo "$PIPELINES" | jq '[.[] | select(.status == "running" or .status == "pending")] | length') if [ "$GREPTILE_RUNNING" = "0" ]; then glab mr note <MR_IID> --message "@greptile review" fi

这段逻辑来自 SKILL.md 第 147–160 行的 GitLab 分支:只有当running/pendingpipeline 计数为0时才发触发评论,否则直接进入等待轮询。

5. Pipeline 与 Job:状态轮询的完整模式

5.1 获取 MR 的 pipelines

glab api "projects/:fullpath/merge_requests/<MR_IID>/pipelines"

返回数组中每个 pipeline 对象有一个status字段,文档列出的取值集合为:running、pending、success、failed、canceled、skipped。

“是否有任何 pipeline 在跑”的判定一行完成:

glab api "projects/:fullpath/merge_requests/<MR_IID>/pipelines" | \ jq '[.[] | select(.status == "running" or .status == "pending")] | length'

返回0即表示当前没有 running/pending 的 pipeline。注意这里只统计running和pending——success/failed/canceled是终态,skipped也不会阻塞新评审,因此都不应计入“仍在运行”。

5.2 按提交 SHA 定位 pipeline

Greptile 的 pipeline 是针对某个 HEAD 提交触发的,因此轮询时不能只取“最新 pipeline”,而必须按 MR 当前 HEAD SHA 匹配,防止拿到上一轮提交的 pipeline:

glab api "projects/:fullpath/merge_requests/<MR_IID>/pipelines" | \ jq -r --arg sha "COMMIT_SHA" '[.[] | select(.sha == $sha)] | sort_by(.id) | last | .id // empty'

这条 jq 值得逐段拆开:

  • --arg sha "COMMIT_SHA"— 通过参数注入 SHA,避免把 SHA 直接拼进 jq 程序;实际使用时 SHA 来自glab mr view <MR_IID> --output json | jq -r '.sha';
  • [.[] | select(.sha == $sha)]— 过滤出该 SHA 的 pipeline(同一 SHA 理论上可能有多个 pipeline 对象,例如重跑场景);
  • sort_by(.id) | last— pipeline ID 单调递增,取最大 ID 即该 SHA 的最新一次运行;
  • .id // empty—关键的防空处理:匹配不到时输出空(而不是null字符串),外层 bash 可以用[ -z "$PIPELINE_ID" ]判断“pipeline 还没出现,继续等”。

5.3 获取 pipeline 的 jobs 并锁定 Greptile job

glab api "projects/:fullpath/pipelines/<PIPELINE_ID>/jobs"

在返回的 jobs 里,按name字段与greptile做大小写不敏感匹配(文档原文:Filter jobs wherenamematchesgreptile(case-insensitive));job 的终态集合是success、failed、canceled。

SKILL.md 把它组合成了完整的轮询循环(第 166–199 行),这就是 Greploop 在 GitLab 上的“等待评审完成”实现:

HEAD_SHA=$(glab mr view <MR_IID> --output json | jq -r '.sha') while true; do PIPELINES=$(glab api "projects/:fullpath/merge_requests/<MR_IID>/pipelines") # Find the most recent pipeline for this SHA PIPELINE_ID=$(echo "$PIPELINES" | jq -r --arg sha "$HEAD_SHA" \ '[.[] | select(.sha == $sha)] | sort_by(.id) | last | .id // empty') if [ -z "$PIPELINE_ID" ]; then echo "Waiting for Greptile pipeline to appear..." sleep 5 continue fi JOBS=$(glab api "projects/:fullpath/pipelines/$PIPELINE_ID/jobs") GREPTILE_JOB=$(echo "$JOBS" | jq '.[] | select(.name | test("greptile"; "i"))') if [ -z "$GREPTILE_JOB" ]; then echo "Waiting for Greptile job to appear..." sleep 5 continue fi JOB_STATUS=$(echo "$GREPTILE_JOB" | jq -r '.status') if [ "$JOB_STATUS" = "success" ] || [ "$JOB_STATUS" = "failed" ] || [ "$JOB_STATUS" = "canceled" ]; then echo "Greptile job completed with: $JOB_STATUS" break fi echo "Waiting for Greptile... (status: $JOB_STATUS)" sleep 10 done

这个循环有两个“还没出现”的等待窗口:pipeline 对象本身可能尚未由 webhook 创建(等 5 秒重试),pipeline 存在但 Greptile job 尚未生成(再等 5 秒重试);job 出现后每 10 秒轮询一次直到终态。对照 GitHub 侧的实现(轮询check-runs的status/conclusion),可以看到 greploop 的设计模式是统一的:“先判存在、再判终态、固定间隔退避”。

6. 从 MR notes 中提取 Greptile 置信度分数

Greptile 评审完成后,分数与总结会以 note(评论)形式出现在 MR 上。GitLab 侧的获取命令:

glab api "projects/:fullpath/merge_requests/<MR_IID>/notes?per_page=100&sort=desc&order_by=created_at"

三个查询参数的作用:

  • per_page=100— 单页最多 100 条 notes;
  • order_by=created_at与sort=desc— 按创建时间倒序,最新的评论在前。

提取分数时需要两步过滤:

  1. 按作者过滤:检查author.username是否是 Greptile bot。文档特别提醒:“The Greptile bot username on GitLab may differ from GitHub'sgreptile-apps[bot]— check the first Greptile comment on the MR to identify the exact username.”也就是说 GitLab 上的 bot 用户名与 GitHub 侧的greptile-apps[bot]可能不同(取决于该实例的 Greptile 安装配置),首次运行时必须打开 MR 找到 Greptile 的第一条评论,以其中的真实 username 为准,之后再把它固化进脚本;
  2. 按正文模式匹配:在body中扫描3/5、5/5这类置信度模式(SKILL.md 中统一表述为 pattern like3/5or5/5,或Confidence: 3/5形式)。

SKILL.md 在 GitLab 侧还要求同时检查两个来源:MR 描述(glab mr view <MR_IID> --output json | jq -r '.description')和 MR notes,因为 Greptile 可能把分数写回 description,也可能只在 notes 里留总结。以“最近一次更新”的分数为准。

7. 抓取未解决的 inline 讨论(discussions)

GitLab 把 inline 评审意见组织在discussions(讨论串)里,每个 discussion 包含一组 notes。获取命令:

glab api "projects/:fullpath/merge_requests/<MR_IID>/discussions?per_page=100"

分页规则(文档原文):加&page=2、&page=3依次请求,直到返回数组长度小于per_page为止——这是 GitLab REST API 列表接口的通用分页约定,长度等于 100 说明后面可能还有页。

从中筛出“Greptile 发的、未解决的 inline diff 评论”的 jq 过滤:

jq '[.[] | select(.resolved == false and (.notes[0].type == "DiffNote") and (.notes[0].author.username == "GREPTILE_BOT_USERNAME"))]'

三个条件的含义:

  • .resolved == false— 讨论尚未解决(resolved是 discussion 级字段);
  • .notes[0].type == "DiffNote"— 首条 note 是 diff 内联评论(而非普通 MR note),这是 GitLab 区分“评论串是否锚定在 diff 上”的类型标记;
  • .notes[0].author.username == "GREPTILE_BOT_USERNAME"— 作者为 Greptile bot(占位符替换为第 6 节确定的真实用户名)。

每个命中的 discussion 对象里,Greploop 会用到三个字段(文档列出的 “Each discussion has”):

  • id— 讨论串 ID,解决讨论时作为 URL 参数;
  • notes[0].body— 评论正文,即要理解与修复的评审意见文本;
  • notes[0].position.new_path— 被评论的文件路径,用于定位到仓库中的目标文件。

SKILL.md 还补充了一个约束:这些 DiffNote 应位于最新一次提交上且未解决(Filter toDiffNotetype discussions from Greptile that are on the latest commit and not yet resolved),避免把历史提交的旧评论误算进本轮修复清单。

8. 解决讨论:无批量接口的逐条 PUT

修复完成后,需要把已处理的讨论标记为已解决,否则循环的退出条件“零未解决评论”永远不成立。GitLab 的解决接口:

glab api --method PUT \ "projects/:fullpath/merge_requests/<MR_IID>/discussions/<DISCUSSION_ID>" \ --field resolved=true

要点:

  • 方法是PUT,不是 POST;resolved通过--field以请求体形式提交;
  • <DISCUSSION_ID>取第 7 节过滤结果里每个 discussion 的id;
  • GitLab 没有批量解决接口(文档原文:GitLab has no batch resolution — issue one PUT per discussion)。这一点与 GitHub 形成鲜明对比——GitHub 侧可以一条 GraphQL mutation 里挂t1: resolveReviewThread(...) t2: resolveReviewThread(...)批量解决(见 graphql-queries.md),GitLab 侧则必须对每个 discussion 发一次 PUT。

脚本化的写法就是一个循环:

for id in $UNRESOLVED_DISCUSSION_IDS; do glab api --method PUT \ "projects/:fullpath/merge_requests/<MR_IID>/discussions/$id" \ --field resolved=true done

9. 端到端串联:gitlab-api.md 在 greploop 循环中的落位

把参考文档中的命令按 SKILL.md 的循环步骤(A 触发 → 等待 → B 取结果 → C 判退出 → D 修复 → E 解决 → F 提交推送)串起来,GitLab 侧的完整数据流是:

glab mr view <MR_IID> --output json # 取 iid / source_branch / sha / description │ ▼ GET .../merge_requests/<MR_IID>/pipelines # 有 running/pending? │ 无 → glab mr note <MR_IID> --message "@greptile review" ▼ GET .../merge_requests/<MR_IID>/pipelines # 按 HEAD_SHA 匹配,sort_by(.id)|last ▼ GET .../pipelines/<PIPELINE_ID>/jobs # test("greptile";"i") 锁定 job │ success / failed / canceled → break ▼ GET .../merge_requests/<MR_IID>/notes?per_page=100&sort=desc&order_by=created_at │ 过滤 Greptile bot 作者,body 中解析 X/5 置信度 ▼ GET .../merge_requests/<MR_IID>/discussions?per_page=100 (&page=N 翻页) │ 过滤 resolved==false && DiffNote && Greptile 作者 ▼ 修复每个 DiffNote 指向的新问题 ▼ PUT .../discussions/<DISCUSSION_ID> --field resolved=true # 逐条 ▼ git commit + git push → 回到 pipelines 轮询(SKILL.md 步骤 F 后回 A)

退出判定(SKILL.md 步骤 C):置信度 5/5 且未解决评论为零则结束并输出报告;达到 5 次迭代上限则报告当前状态并列出剩余未解决评论。

10. 小结与实践提示

  • 占位符约定:文中<MR_IID>、<PIPELINE_ID>、<DISCUSSION_ID>、COMMIT_SHA、GREPTILE_BOT_USERNAME均为运行时变量;:fullpath是glab api原生支持、由本地 remote 自动解析的占位符,不要手写成字面量。
  • iid 而非 id:所有merge_requests/<MR_IID>路径参数一律使用 MR 的项目内编号iid。
  • SHA 锚定轮询:按 MR 的sha过滤 pipeline 并用sort_by(.id) | last取最新,// empty保证“未出现”时轮询能继续等待,而不是拿到错误 pipeline。
  • 状态枚举要写全:pipeline 关注running/pending(非终态)与success/failed/canceled(job 终态);skipped不在运行判定内。
  • bot 用户名要现场确认:Greptile 在 GitLab 上的 bot 用户名因安装而异,首次运行从 MR 上它的第一条评论里确认,再固化到过滤条件中。
  • 解决讨论只能逐条 PUT:没有批量接口,循环发 PUT 是唯一方式;这也是 GitLab 与 GitHub(GraphQL 批量resolveReviewThread)在 API 设计上的实质差异。

以上命令与流程均可在 gitlab-api.md 中逐条对照原文,循环的整体编排(触发去重、轮询节奏、退出条件、报告格式)见 SKILL.md,GitHub 侧的 GraphQL 对照实现见 graphql-queries.md。

  • 人工智能
  • AI 应用
  • 语音
  • 音频
  • 本地部署
  • 桌面应用

【免费下载链接】auto-subs

On-device subtitle generation that connects directly to DaVinci Resolve, Premiere, and After Effects.

项目地址:https://gitcode.com/gh_mirrors/au/auto-subs
点击查看免费下载

相关推荐

上一篇:WorkshopDL终极指南:无需Steam客户端,轻松下载创意工坊模组的开源工具
下一篇:Docker CLI `docker container port` 命令详解:查询容器端口映射的完整实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询