mcpField
P
pr-landing-discipline

pr-landing-discipline

github.com/vivswan开发157 次安装1 天前更新
代码仓库

关于 pr-landing-discipline

pr-landing-discipline 是供变更作者使用的技能,规范 PR 或补丁从评审到落地的流程。它定义草稿与就绪状态、评论分流和收敛检查,并默认由人工决定合并。

主要功能

  1. 01提交待处理时保持草稿状态
  2. 02同一轮处理评审评论
  3. 03CI 与评审线程收敛门槛
  4. 04检查 PR 内容中的个人信息
  5. 05处理机器人评审意见的规则
  6. 06默认由人工决定合并

适用场景

  1. 01管理经历多轮评审的 PR
  2. 02检查 PR 是否达到合并就绪状态
  3. 03处理机器人评论而避免推测性修改

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/vivswan/skills --skill pr-landing-discipline

安装后在 Claude Code 中输入/pr-landing-discipline确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

LICENSE.md
README.md
.codex-plugin/
plugin.json
agents/
openai.yaml

详细介绍

PR 落地规范

PR 在合并前都属于进行中的工作。提交尚未完成时保持草稿;一旦收敛,立即将其设为就绪。每一轮评审都在出现的同一周期内完成分流,依据声明的目的核对代码行数,并默认由人类决定是否合并。

这些规则适用于任何将变更从“已打开”推进到“已落地”的会话:通过评审轮次直至合并的 PR,或直接推送的补丁。

下文中的“作者”指准备变更的人,无论是人类还是 Agent,也无论是独自工作还是在多 Agent 会话中工作。撰写 PR 正文或 issue 是 /pr-and-issue-discipline 技能发挥作用的环节。本技能从 PR 创建后开始适用。

适用时机

  • 开放状态的 PR 刚收到一轮评审意见
  • PR 收敛,或已就绪的 PR 出现需要新提交的工作
  • 变更即将落地,无论是通过合并 PR 还是直接推送
  • 决定由谁合并已收敛的 PR

草稿规范

  • 每个 PR 都以草稿状态打开,并在整个评审过程中保持草稿状态。
  • 一旦收敛,立即设为就绪:绝不批量处理,也绝不延后。只有在重新阅读 /pr-and-issue-discipline 技能规定的 PR 正文后,才能将其设为就绪;因为正文是在 PR 打开时撰写的,而差异此后已有变动。
  • 一旦就绪的 PR 出现需要新提交的工作,立即将其改回草稿状态,并在开始修复轮次前完成此操作(例如,出现新的有效评审意见或门禁检查发现问题)。
  • 草稿状态反映待处理的提交。 收敛状态决定是否可以提出合并。若新评论只需回复,不会让就绪的 PR 重新变为草稿,因为回复并解决评论会在同一周期内完成,无需新提交。只有满足下文完整的收敛定义时,才会提出合并 PR。

已收敛是指评审已按 /rubber-duck-review 技能中的定义收敛(第 7 步是唯一的定义),并且满足 PR 特定标准:CI 全部通过,且所有评审线程均已解决(已修复或已答复)。

全部通过是指 PR 上的每一项检查,无论是否为必需检查,也包括依赖链中每个 PR 上的检查。若基础分支未重新设定目标而导致仍有检查失败,即使必需门禁已通过,也不具备就绪资格。

设为就绪还要求 PR 发布的内容不包含任何 PII。标题、正文、提交消息、差异中新增的行,以及评审、issue 和 CI 评论,都不能让读者了解作者是谁、如何工作,或其机器如何配置。PII 的定义、替代内容和合同例外,均以 /pr-and-issue-discipline 技能中的脱敏规则为准。

以下 grep 检查会查找标识符,以及被替换的真实数据夹具中的数值。引用真实配置、日志或转录内容时,应由评审者阅读,并作为阻塞性问题处理。检查会从 PR 本身读取全部内容,而不是从本地 HEAD 读取;并使用包含固定字符串的 needle 文件进行 grep:

  • 作者的真实标识符,每行一个,包括作者曾公开使用过的所有写法。下方六项是姓名、雇主、登录名、用户名、电子邮件和机器名的占位符。
  • 真实数据夹具中的来源描述短语(measured from、real logs)。
  • 当 PR 替换的夹具来自真实数据测量时: 整个旧文件中的每个完整精度比率,以及所有 4 位或更多位数的整数,避免旧数值进入新夹具、正文或评论。

旧文件通过 PR 合并基点上的仓库 API 读取:比较端点会指出文件,内容端点则会提供该位置的文件内容。因此,不会从本地获取任何内容,被保留的数值同样会成为 needle。

n=<pr number>; out="$(mktemp)"; diff="$(mktemp)"; shas="$(mktemp)"; old="$(mktemp)"; needles="$(mktemp)"
fixture='<path/to/replaced-fixture.json>'   # only when a real-data fixture is being replaced; with none, drop this line and every line that uses $fixture, $mb, $old, or $head
printf '%s\n' 'Real Name' 'ExampleCorp' 'real-login' 'realuser' '[email protected]' 'MacBook-Pro' 'measured from' 'real logs' > "$needles"
head="$(gh pr view "$n" --json headRefOid -q .headRefOid)" &&
mb="$(gh api "repos/{owner}/{repo}/compare/$(gh pr view "$n" --json baseRefOid -q .baseRefOid)...$head" -q .merge_base_commit.sha)" &&
gh api -H 'Accept: application/vnd.github.raw+json' "repos/{owner}/{repo}/contents/$fixture?ref=$mb" > "$old" &&
{ grep -oE '[0-9]+\.[0-9]{3,}|[0-9]{4,}' "$old" >> "$needles" || test $? -eq 1; } &&   # no distinctive figure in the old file adds no needles and is not a stop; exit 2 (a read error) is
gh api -H 'Accept: application/vnd.github.raw+json' "repos/{owner}/{repo}/contents/$fixture?ref=$head" > "$out" &&
gh api --paginate "repos/{owner}/{repo}/pulls/$n/commits" -q '.[].sha' > "$shas" &&
test "$(wc -l < "$shas")" -eq "$(gh api "repos/{owner}/{repo}/pulls/$n" -q .commits)" &&   # the listing stops at 250 commits; a short list is a stop, not a sample
xargs -I{} gh api -H 'Accept: application/vnd.github.diff' "repos/{owner}/{repo}/commits/{}" < "$shas" > "$diff" &&
{ gh pr view "$n" --json title,body -q '.title, .body' &&
  gh api --paginate "repos/{owner}/{repo}/pulls/$n/commits" -q '.[].commit.message' &&
  sed -n '/^+/p' "$diff" &&
  gh api --paginate "repos/{owner}/{repo}/pulls/$n/reviews" -q '.[].body' &&
  gh api --paginate "repos/{owner}/{repo}/pulls/$n/comments" -q '.[].body' &&
  gh api --paginate "repos/{owner}/{repo}/issues/$n/comments" -q '.[].body' &&
  xargs -I{} gh api --paginate "repos/{owner}/{repo}/commits/{}/comments" -q '.[].body' < "$shas"; } >> "$out" &&
  { grep -n -i -F -f "$needles" "$out"; echo "grep exit $?"; }
rm -f "$out" "$diff" "$shas" "$old" "$needles"

grep exit 1 表示检查通过。grep exit 0 会列出命中项;将每项判定为问题前,都必须先阅读。允许的命中项以 /pr-and-issue-discipline 技能的 references/redaction.md 中“保留内容”一节所列项目为准。

若某个 4 位或更多位数的 needle 与无关行中的年份、sha 片段或端口号相符,可在明确说明这一判读后排除。其他所有命中项都属于阻塞性问题。若命中的是来源描述短语,则说明对应的夹具存在问题,必须通过手动编写夹具来修复。 如果没有 grep exit 这一行,就说明链路中某处读取失败了,而运行结果也没有说明 PR 的情况。Fixture 路径错误会导致 404,读取被截断则会返回非零退出码。提交列表比 PR 的提交数量短,会导致其 test 失败;任何提交读取失败,xargs 都会以非零状态退出。

如果旧 fixture 中没有指纹模式匹配的图,就不会添加任何待查项。此时只能通过逐眼对比替换文件与旧文件来检查,因为 grep 没有任何独特内容可供查找。

该代码块读取的内容及原因:

  • 每个提交自身新增的行,绝不只看最终 diff。某个 fixture 在一个提交中新增、下一个提交中删除,仍然是由第一个提交发布的;rebase 或合并提交也会保留两者。只读新增行,因为会脱敏 PII 的 PR 中,被删除的行按定义就包含 PII。
  • PR 头部版本中的整个新 fixture,而不只是更改过的行。替换文件中原封不动保留的旧图,正是指纹要捕捉的漏网内容。
  • 所有评论:评审、issue 和提交评论,包括 CI 评论。 如果 CI 评论包含从真实数据衍生出的图,就应视为对发布该评论的检查本身的发现,而不仅仅是对 PR 的发现。

持续跟进,直到评论收敛

开放的 PR 在合并前都是进行中的工作。机器人评审者(例如 Copilot 代码评审)和人工评审者会在每次推送后留下评论。对每个 PR,循环处理,直到没有新情况:

  1. 每次推送都通过 PR 订阅获取 CI 结果(见下文的 Companion Gates)。
  2. 评审到达时,必须在它出现的同一轮处理 EVERY 条评论,绝不攒在一起处理:
    • 有效发现必须在同一轮修复。
    • 无效或不适用于此处的评论,要回复说明原因并解决其讨论串。没有需要回应内容的评论(例如赞扬、重述 diff)无需回复,直接解决。
  3. 推送修复前,正文必须按 /pr-and-issue-discipline 技能的规定进行每轮编辑:原地修改,覆盖计数,并重新运行大小检查。
  4. 推送修复后,重新开始循环:监看新的 CI,对更改后的内容重新设门禁,机器人也可能再次评审。

机器人评论仅供参考。 非人工评审者(Copilot 代码评审、任何评审机器人)对仓库评审说明的遵循并不一致,而且许多仓库根本没有评审说明。因此,作者应按照这些说明本应采用的筛选标准来审查每条机器人评论:只针对能够证明的缺陷发表评论。

  • 值得修复: 评论指出具体输入或状态,以及此 diff 会导致的错误输出、崩溃或数据丢失。必须在本轮修复。

  • 推测性加固: 无法触达代码的恶意调用者、“考虑添加验证”、“增强健壮性”、单用户工具中的竞态,或针对代码永远不会收到的输入所做的检查。用一句话拒绝并记录,不予实现(/rubber-duck-review 技能的第 6 步针对有外部用户或存在恶意输入面的仓库设有例外,此时由用户设定标准)。

    无论评论如何措辞,只要指出了输入及其造成的损害,就属于第一类。

  • 手写库已有功能: 如果评论指出仓库中已有的库或工具及其覆盖范围,就应按 /code-standards 技能的规定替换。包括本次更改新增、移动、触及、复用或依赖的代码。大型库或适用性不明确的情况交由负责人处理,并暂缓 PR。绝不能因为依赖负担而不采用。未指出库的评论归入下一类。

  • 仓库的 linter 和 formatter 不会强制执行的风格、命名或结构意见,以及表达相同含义的改写: 用一句话拒绝。若该意见已由 linter 报告,则无需回复,直接解决,因为失败检查已经说明问题。

  • 重述 diff、赞扬、总结: 直接解决,无需回复。

这一句拒绝本身就是讨论串的收敛。机器人给出的总结判定(“建议更改”)不是门禁,而且机器人不能替代 Companion Gates 下的阻塞性评审。 返工预算。 如果连续多轮每轮只发现一个问题(大约进行到第 10 轮时),就停止逐个修复问题。列出反复出现的问题类别,一次性全面处理变更中每个类别,然后继续循环。有一次经过 35 轮才收敛,但列出问题类别后,只需几轮批量处理就完成了。

通过 GraphQL 读取讨论串状态,绝不要根据评论时间戳判断(即使讨论串没有新评论,也仍可能尚未解决):

reviewThreads(first: 100) { nodes { isResolved } pageInfo { hasNextPage endCursor } }

当 hasNextPage 为 true 时,使用 after: <endCursor> 翻页。固定读取第一页并不代表拿到了完整集合。

草稿不会自动触发的机器人审查,需要显式请求(例如,在草稿 PR 中将 Copilot 添加为审查者;如果仓库提供了设置,优先选择平衡或高推理)。每次都能成功注册的方式,是使用 GraphQL mutation 和 Copilot 的机器人 ID:

pr_id="$(gh pr view <n> --json id --jq .id)"
gh api graphql -f query='mutation($pr: ID!) { requestReviews(input: {pullRequestId: $pr, botIds: ["BOT_kgDOCnlnWA"], union: true}) { clientMutationId } }' -f pr="$pr_id"

REST 审查者端点是备用方案。它要求登录名必须精确为 Copilot;但有些时候,它会在反复请求后返回 201,requested_reviewers 却为空,时间线中也没有事件;copilot-pull-request-reviewer[bot] 则总是如此无操作。

无论采用哪种方式,GraphQL reviewRequests 都不会显示待处理的 Copilot 请求,因此只有议题时间线能确认请求是否已注册。两轮之间绝不轮询。如果已安装 /watch-github 技能并且已订阅该 PR,每条新评论或审查都会以消息的形式送达,并注明作者和发布位置。

一轮的实际情况:

  • “空清单也会通过验证”:有效。在同一轮中通过回归测试修复。
  • “脚本未接入文档”:这是有意安排的顺序。回复了计划(合并后统一更新文档,接入所有脚本),随后解决。
  • “跟随符号链接”:拆分处理。经实证确认后,修复了保留叶节点一致性的部分;依据已记录的设计理由,拒绝了可能逃逸的部分。
  • Copilot 提出的“应用清单前先验证其来源”,针对的是一个在 CI 中只有一个调用方、且读取由同一工作流写入的文件的脚本:作为推测性加固予以拒绝。用一句话回复后解决。该轮的“建议更改”结论并未阻止合并。
  • 对于一个本身不产生任何行为的删除变更,有人提出“添加回归测试”:予以拒绝。评论没有指出任何测试可以锁定、而源文件又未说明的事实(这是 /rubber-duck-review 技能的一贯测试问题);正文中的普查就是证明。回复了规则后解决。

配套门槛

  • 每次推送后,PR 都会保持订阅状态,CI 结果会以消息形式送达。 如果已安装,/watch-github 技能会定义订阅方式。MERGE 根据 PR 的合并提交进行判断,该提交会作为主线分支的新顶端报告。执行 gh pr merge 后,git rev-parse HEAD 仍指向主题分支的顶端,而 squash 或 merge 提交只存在于主线上。
  • 在任何内容合入之前,都要进行能够阻止合入的独立审查。 审查范围仅限于确切要合入的内容,绝不审查工作树:提交后审查分支或 PR 差异(base...HEAD),提交前则审查暂存区差异。如果已安装,/rubber-duck-review 技能会定义审查及其收敛方式。
  • 门槛会检查正文中的库说明。 如果差异新增、移动、触及、复用或依赖某个库形类别(由 /code-standards 技能的 references/design.md 负责维护的列表),则会被拒绝,除非 ## How(在测量形态中为 ## Reasoning;没有 PR 时则为合入报告)包含符合 /pr-and-issue-discipline 技能所述格式的 **Library:** 项:列出一个库及其涵盖内容,或者说明搜索过程以及为何没有合适选项。
  • 门槛或审查者确认的库必须成为后续任务(一个 issue 或本次会话的任务列表),或者在 PR 中完成,绝不能只记录为尚未构建的条目。大型库或适用性不明确时,应先向负责人询问,任务则根据其答复安排。
  • 推送前的本地运行应有针对性。 运行类型检查(如果该语言支持),以及变更和验证所涉及的测试;长时间运行时要明确设置超时,并且只能通过你启动的 PID 停止进程,绝不能按模式停止。完整测试套件以及 lint 加构建检查应交由 CI 判断;CI 已会运行时,绝不要在本地重复运行。
  • 提交前钩子只能作为检查门槛: 它能做和不能做的事情,以 /code-standards 技能的“钩子只检查,绝不提交”标准为准。
  • 当 CI 分钟数按量计费或供应紧张时(例如,按量计费套餐中的私有仓库),应改为在推送前于本地运行一次完整检查。不确定属于哪种情况时,询问用户,或者询问向你介绍情况的负责人。

合入前的行数核算

变更合入之前,作者应按文件类型查看新增和删除行数,并核对总数是否符合变更所述目的。目的由标题的类型和正文的首个标题确定;没有 PR 时,则由提交说明的类型确定。

计数只是信号,不是判决。它能发现一种重写:规模与被替换内容一样大,而每位审查者都认为它很干净;计数会促使人们追问原因。

变更是否优秀取决于判断;优秀的变更无论计数如何都会合入。但绝不能在意外情况尚未解释清楚时合入。

git diff --cached --numstat                          # landing a still-uncommitted patch
git diff --numstat "<base-remote>/<base>...HEAD"     # a branch; the remote the PR merges into, never a fork remote
gh api --paginate "repos/<owner>/<repo>/pulls/<n>/files" --jq '.[] | "\(.additions)\t\(.deletions)\t\(.filename)"'   # an open or landed PR

按类型汇总各行。类型由仓库的分类方式决定:源代码、脚本、测试、文档、工作流以及生成文件(锁文件、快照、渲染输出)。

生成文件和二进制文件行(两列均为 -)应注明并从所有总数中排除。重命名行({old => new})只计算修改过的行,因此应保持重命名检测开启。

下方的 TARGET 是目的所作用的类型:库变更对应源代码,脚本重写对应脚本,CI 变更对应工作流。这些行是常见形式,应按审查者的理解方式阅读,而不是当作铁律。若目的不符合任何一种形式,则按其自身情况判断。

声明的目的预期形态何时需要追问
简化、整合、弃用目标代码净减少;测试仅因移除行为而减少目标代码持平或增加
Bug 修复目标代码改动集中在缺陷位置;测试因回归用例而增加目标代码增长远超缺陷位置,或测试增加 0
功能目标代码和测试均增加测试增加 0
保持行为不变的重构现有测试原样通过(可以调整结构或加强测试)现有测试现在预期不同的输出:行为变了,因此目的描述不准确,标题应使用可见的类型,而不是 refactor(/pr-and-issue-discipline 技能的标题规则)
纯移动重命名行,加上仅涉及移动路径的编辑(导入、工作流和清单路径、注释、移动文件中的相对路径);移动前后的导出项统计和测试数量相同(/code-standards 技能的 references/structure.md)移动的文件包含路径调整之外的编辑,或有导出项或测试消失
文档或契约变更仅更改文档和契约文件可执行代码发生更改

如果某一行提到测试增加 0,说明已有哪个测试覆盖了这项变更即可作答:修复让现有断言现在能够锁定问题;功能则由已有的基于数据的测试套件覆盖。对于本身不涉及行为的删除,如果正文给出了统计数据(被删除内容的 grep 计数及前后对比),测试增加 0 无需解释。/code-standards 技能的 references/tests.md 规定了统计方法。

为了让这一行有内容可写而编写的测试,正是这项统计要捕捉的问题,绝不是对问题的回答。

不匹配之处是作者要在合并前回答的问题,而不是阻止合并的理由。要么缩减变更,要么认定变更本身合理,并按便于核查的粒度解释增长(重写按函数,批量处理按文件)。

为了让变更更好而增加代码行数,是合理的解释。仅仅说“更整洁”则不行。

有两种解释反复出现,都需要核实,而不能直接接受。一种是分阶段切换,删除工作放在一个明确标出的同级 PR 中完成(合并时一起查看两个 PR 的总和)。另一种是目的描述中未提及的保护条件或规则(此时标题就错了:先修正标题,再适用它现在所属的那一行)。

如何处理这些统计数据,取决于它们说明了什么。符合对应行预期的计数无需解释。如果要附上计数,应放在 PR 正文的技术细节部分。

与目的相悖的计数属于第一部分内容。读者的预期(整合会缩小代码)是错的,因此应根据 /pr-and-issue-discipline 技能的可读性规则,在用户能看到的说明部分给出计数和原因,并在提出变更时告知用户。若没有 PR,则两者都写入合并报告。

## Line accounting

- **Source +41 -12, tests +30 -0.** The consolidation grew source by 29: the merged entry point carries a guard the three old paths each skipped. The three paths themselves go with the sibling PR #N (-88), so the pair is net -59.

生产环境中的一个案例:重写发布脚本并弃用构建分支调用链时,删掉了 18 个函数,又增加了 18 个,脚本大小保持不变。评审者认为所有替代代码都很简洁。只有计数提出了疑问,答案是:实际上什么都没有弃用。

对其前后的 68 个 PR 进行审查,每个 PR 调用一次 pulls/<n>/files,发现没有其他 PR 大小不变。审查发现,有四个 PR 增加了标题未提及的保护条件,或将删除工作分阶段放到了同级 PR 中。

谁来合并

默认由人来合并。PR 的目的是在主线合并前设置人工关卡:作者负责准备 PR(推送、检查全部通过、提交“可以合并”的报告),再由人来合并。

如果由合并队列负责排序,作者准备好的操作就是将已收敛的 PR 加入队列。入队不等于已合并,因此要持续关注,直到提交真正落地。还有两种固定例外,但都必须经过用户授权:

  • 只是一个简单的机械性修正。 几行改动,不改变任何行为、流程或规程(类型收窄、拼写错误修正、没有语义影响的重命名),只要所有门禁都通过,就直接合并。人工审核环节留给值得人类关注的改动。若不确定是否算“简单”,那就不算。
  • 流水线因等待合并而受阻。 当已收敛的 PR 阻塞了排队中的工作,而人类没有采取行动时,就合并它,并在下一份报告中说明。作者本可自行完成合并却让工作闲置等待,这才是问题;发送通知则能保留人类的监督。

仓库定义的长期批准标签(例如 merge-when-green),对单个 PR 的整个生命周期持续生效。 由批准者(仓库授权其授予批准的人,如所有者或仓库指定的维护者)应用此标签,绝不能由 Agent 应用;之后的任何推送或强制推送都不会使其失效。没有此标签的 PR,或没有定义该标签的仓库,均遵循上述规则。

该标签授予操作者的是合并权限,而不是对内容的认可。批准者认可了 PR 的构想和形式,这种认可可能是在创作时或更早作出的,而且批准者可能并未阅读代码。因此,操作者须针对即将合入的确切 head 代替批准者进行审核:

  • 按上述定义,head 已收敛
  • 根据目的(见上文)核对行数
  • 对照 diff 重新阅读 PR 正文
  • 具有阻止权限的门禁审核已针对这个 head 运行

每个 PR 都要进行相同的检查,但阅读深度有所不同:拼写错误修正只需快速浏览 diff,而重写发布脚本则要从头到尾阅读 diff。本技能中的门禁是用于作出判断的工具箱,并非标签解锁的检查清单。合并前须有两项确认:

label='<approval label>'   # the label the repository defines, e.g. merge-when-green
head="$(gh pr view <n> --json headRefOid --jq .headRefOid)"   # first: a push after this fails the merge below
label="$label" gh api --paginate "repos/<owner>/<repo>/issues/<n>/timeline" --jq '.[]
  | select((.event == "labeled" or .event == "unlabeled") and .label.name == env.label)
  | "\(.created_at) \(.event) \(.actor.login)"'   # jq reads the label from the environment, so any label name is safe
1. the LAST line is a labeled event by an authorised approver
   -> labeled by anyone else: remove the label, report who set it, an approver re-applies it
   -> unlabeled, or no line at all: no approval; the rules above apply
2. the operator's gate review ran on THIS head and its findings are triaged
   -> the head moved since the review: re-read head, re-review the head with the delta named, then merge

折叠 PR 与标签。 批准涵盖的是批准者在该 PR 上认可的构想和形式。将另一个 PR 折叠进带标签的 PR,会引入批准者从未认可的内容。

因此,折叠前就存在的标签,只有在每个被折叠进来的 PR 都按其各自的时间线通过检查 1(即标签由批准者设置,而非仅仅存在某个标签)时才能保留。否则,操作者应移除标签并说明原因,待批准者阅读折叠后的内容后重新应用标签。

折叠后由批准者设置的标签即为该项批准,与被折叠 PR 原先带有什么标签无关。新的合并 PR 与其他 PR 一样,初始时不带标签。

PR 自身的审核轮次(同一分支上的新提交)不算折叠,因此标签会保留。优先选择堆叠 PR 或新建 PR,而非折叠进带标签的 PR。

操作者仅在以下两种情况下移除标签:该标签由非授权批准者设置;或折叠带入了未经批准的内容。检查未通过、有未解决的讨论、门禁发现问题或 head 发生变化,都不会移除标签。它们会暂缓合并,而非撤销批准。

两项确认均通过且 head 已收敛:使用仓库的合并方式(--squash、--merge 或 --rebase)以及 --match-head-commit "$head" 进行合并,这样若合并期间发生并发推送,合并就会失败,而不会将其合入,然后报告结果。

检查 1 未通过:移除标签并应用上述规则。检查 2 或收敛检查未通过:依据 Draft Discipline 对发现的问题进行分类处理(需要提交修复的问题会使 PR 重新变为草稿),保留标签,并在报告中说明原因。

落地操作须以退出状态为条件,绝不串联执行;这既适用于合并 PR,也适用于直接推送。读取门禁自身的结论并停止,只有在门禁本身报告通过后,才通过单独的命令执行落地操作。通过意味着门禁的退出代码和结论都通过;审核门禁只有在其发现的问题均已分类处理后才算通过,而不能仅凭进程以 0 退出就认定通过。

tail gate.log; git merge && git push    # WRONG: the merge runs whatever the log said
tail gate.log && git merge && git push  # WRONG: && conditions on tail printing the log,
                                        # not on the gate's verdict; a red log still merges

评论

支持 Markdown