land-renovate 是做什么的?
遍历当前仓库中所有开放的 Renovate PR 队列,安全地合并其中的 PR:枚举、分类、按 PR 编号升序批准并执行 rebase-merge,对冲突的 PR 勾选 rebase 复选框进行提示,并在遇到真实失败或 HOLD 情况时干净地停止。
此技能完全通过 gh 操作现有的远程 PR。它不会调用 /create-commit、/create-pr 或 /wrap,也不会创建本地提交。
参数
| 参数 | 默认值 | 描述 |
|---|---|---|
limit=N | 30 | 限制最多遍历的 PR 数量。 |
<pr-number>... | 所有 Renovate 创建的开放 PR | 空格分隔的 PR 编号;仅操作这些 PR。 |
默认值:当前仓库(通过 gh repo view --json nameWithOwner 获取)、Renovate 创建的开放 PR、按 PR 编号升序、无标签过滤。
工作流程
1. 解析仓库
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
2. 枚举 Renovate PR
列出所有开放的、由 Renovate 创建的 PR,按编号升序(最旧的优先):
gh pr list -R "$REPO" --author "app/renovate" --state open --limit 30 \
--json number,title,mergeable,mergeStateStatus,statusCheckRollup
按 number 升序排序并遍历。如果传入了显式的 PR 编号作为参数,则限定到这些 PR。
注意:
gh pr list常常返回mergeable: UNKNOWN,因为 GitHub 惰性计算合并性。对单个 PR 执行gh pr view <n> --json mergeable可以强制计算。当某个 PR 的mergeable是UNKNOWN时,在分类前先用gh pr view重新读取。
3. 分类每个 PR
使用 statusCheckRollup + mergeable + mergeStateStatus 将每个 PR 归入以下桶:
| 桶 | 信号 | 操作 |
|---|---|---|
| GREEN | rollup 中没有 FAILURE/CANCELLED/TIMED_OUT,没有待处理项,mergeable=MERGEABLE | 批准 + rebase-merge(步骤 4) |
| CONFLICTING | mergeable=CONFLICTING 或 mergeStateStatus=DIRTY | 勾选 rebase 复选框(步骤 5),然后监控 |
| REAL_FAILURE | rollup 中包含非后升级自动修复的 FAILURE | 跳过;留给人工排查 |
| HOLD | 需要跨文件协调(人工检查清单 — 见下文) | 提示用户;不继续处理 |
rollup 中的检查在待处理状态是指 CheckRun 的 status != COMPLETED 或 StatusContext 的 state 为 PENDING/EXPECTED。失败状态是指 CheckRun.conclusion 为 FAILURE/CANCELLED/TIMED_OUT/ACTION_REQUIRED/STARTUP_FAILURE,或 StatusContext.state 为 FAILURE/ERROR。
HOLD 检查清单(人工判断 — 非硬编码)
HOLD 检测是代理的判断决策,而非自动化规则。当单独合并一个 PR 会破坏另一个文件中的内容时,将该 PR 视为 HOLD 候选,例如:
- terraform-version 版本不匹配 — Terraform provider/CLI 升级需要在其他地方更新相应的版本约束(例如
.terraform-version、CI 工作流或required_version约束),否则不安全。 - 需要代码编辑的 schema 升级 — 依赖项的主版本升级改变了手写代码仍在使用的 schema/类型;后升级钩子无法生成代码编辑。
- 任何变更日志中指明需要后升级作业不会执行的协调编辑的升级。
当 PR 看起来像是 HOLD 时,将其展示给用户并询问 — 不要自动合并,也不要勾选 rebase 复选框。继续遍历队列中的其余 PR。
4. 批准 + 合并(GREEN)
gh pr review "$PR" -R "$REPO" --approve \
--body "LGTM — 作为 Renovate 批量合并的一部分自动批准。"
gh pr merge "$PR" -R "$REPO" --rebase --delete-branch
5. 勾选 rebase 复选框(CONFLICTING)
勾选 Renovate 的 rebase 复选框以触发重新基准,然后监控:
body=$(gh pr view "$PR" -R "$REPO" --json body -q .body)
new=$(printf '%s' "$body" | sed 's|^\( *\)\(- \[ \] <!-- rebase-check -->\)|\1- [x] <!-- rebase-check -->|')
printf '%s' "$new" | gh pr edit "$PR" -R "$REPO" --body-file - >/dev/null
在勾选复选框之前,捕获当前头部 SHA 作为基线,然后运行监控(步骤 6),以便区分 rebase 和起始状态:
BASELINE=$(gh pr view "$PR" -R "$REPO" --json headRefOid -q .headRefOid)
绝对不要在 REAL_FAILURE 上勾选 rebase 复选框 — 这会浪费 Renovate 的周期并隐藏真正的问题。
6. 监控直至稳定
附带的 scripts/monitor-renovate-pr.sh 脚本会轮询单个 PR,直到其检查稳定并输出代理通过 Monitor 工具消费的判定结果。
scripts/monitor-renovate-pr.sh <pr-number> <repo> <baseline-head-sha-short>
- 每 45 秒(可通过
POLL_INTERVAL覆盖)轮询gh pr view --json headRefOid,mergeable,mergeStateStatus,statusCheckRollup,仅在签名${head:0:9}|<mergeable>:<state>|ok=N pend=M fail=K变化时打印一行。 - 一旦头部离开基线、没有待处理检查且签名在连续两次轮询中稳定,则发出判定。
- 最终的标准输出行是以下之一:
GREEN: #<n> ready to merge→ 返回步骤 4 并批准 + 合并。NOT-GREEN: #<n> settled failures=X mergeable=Y state=Z→ 重新分类;现在它是 REAL_FAILURE 或 HOLD。
- 可选环境变量:
POLL_INTERVAL(秒,默认 45)、MAX_POLLS(在 N 次轮询后放弃,0= 无限制;超时时输出NOT-GREEN: #<n> timeout ...)。
为什么使用轮询循环而不是 gh pr checks --watch
对于已经可合并且仅运行检查的 PR,gh pr checks <n> --watch 很好用,但它在本技能专门处理的 rebase 路径上会给出误导:
- 它在过时的头部上返回绿色。 勾选 rebase 复选框后,PR 的_当前_检查已经终结 — 它们在 rebase 前(冲突)的头部上通过了。
--watch看到“没有待处理的检查”后立即返回成功,而 Renovate 甚至还没有开始 rebase。head moved off baseline条件正是防止这种假绿色的守卫。 - 它只知道检查,不知道合并性。 GREEN 判定需要
mergeable=MERGEABLE+mergeStateStatus=CLEAN,而 CONFLICTING 正是通过那些字段_检测_的 —gh pr checks从不报告它们。一次gh pr view轮询即可同时覆盖两者。 - 头部可能移动两次(Renovate 推送,然后应用机器人后升级推送)。在这两次推送之间存在一个窗口,已知的检查会暂时完成;
--watch可能在这个间隙退出。“没有待处理检查且稳定通过两次轮询”的守卫可以安全通过这个窗口。
7. 预期的 rebase 后提交链
勾选 rebase 复选框后(或 Renovate 自行 rebase),一个 GREEN 的合并通常会经过以下链 — 了解这一点可以帮助你了解监控中的波动是预期的,而非失败:
- Renovate 推送新的头部,作为
renovate[bot]。 pull_request工作流触发。check.yml的后升级步骤运行eslint --fix、prettier --write、vitest --update、projen以及beachball变更条目。- 如果后升级步骤产生了差异,
commit-upgrade-changes作业会通过actions/create-github-app-token@v2生成一个 App 令牌,并将修复推送为<app-slug>[bot]。该机器人推送会重新触发pull_request工作流,因此头部第二次移动,检查重新运行。 - 最终就绪状态:
mergeable=MERGEABLE、mergeStateStatus=CLEAN,所有检查为SUCCESS/SKIPPED。
由于头部可能移动两次(Renovate,然后是应用机器人),监控器将判定条件定为“头部离开基线 + 无待处理检查 + 两次稳定轮询”,而不是仅看到第一次推送。
停止条件
- 没有剩余的 GREEN PR — 完成。
- REAL_FAILURE — 跳过,继续处理下一个 PR;绝不自动 rebase REAL_FAILURE。
- HOLD 候选 — 展示给用户决策;不自动合并。
- 用户取消。
约束
- 提交主题不得包含问题/PR 引用(项目规则)。此处合并远程 PR 时不适用,但如果本技能将来扩展为创建本地提交,请将引用放在正文中而非主题中。参见
/create-commit。 - 绝不勾选 REAL_FAILURE 的 rebase 复选框。
- 绝不自动合并 HOLD 候选。
- HOLD 检测是人工检查清单,而非硬编码规则。识别上述类别并展示候选,不要尝试自动化决策。
测试说明
干运行分类
枚举并直观检查桶分类而不执行任何操作:
gh pr list -R "$REPO" --author "app/renovate" --state open \
--json number,title,mergeable,mergeStateStatus,statusCheckRollup \
| jq -r '.[] | "\(.number)\t\(.mergeable)\t\(.mergeStateStatus)\t\(.title)"'
为每个 PR 添加 rollup 摘要以查看待处理/失败的检查:
gh pr list -R "$REPO" --author "app/renovate" --state open \
--json number,statusCheckRollup \
| jq -r '.[] | "\(.number)\t" + ([.statusCheckRollup[] | .conclusion // .state] | join(","))'
监控冒烟测试
对一个已知的 PR 运行监控,使用假的基线 SHA(这样头部已经算作“移动”),并使用短间隔观察状态行和最终判定:
POLL_INTERVAL=2 MAX_POLLS=4 \
scripts/monitor-renovate-pr.sh <known-pr> "$REPO" deadbeef0
您应该看到每次签名变化时的状态行,然后是 GREEN: / NOT-GREEN: 判定。传递 PR 的真实头部作为基线则测试无 rebase 路径:头部从不移动,因此永不稳定,运行会在 MAX_POLLS 超时时结束。
不涉及的范围
- 硬编码的 HOLD 检测规则(从上述人工检查清单开始;以后再扩展)。
- 将此技能接入任何编排层。