From 9b6a166382bdce525c1dcfb077c7a152267ee69e Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Fri, 15 May 2026 00:25:44 +0300 Subject: T8782: Block merge while backport-conflict label is present via merge_protections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a merge_protections rule requiring `label != backport-conflict` for merge. The backport action above auto-applies the label on cherry-pick conflicts, so this closes the loop: the conflicted PR exists (workspace preserved), is labeled and assigned, AND cannot be merged until the operator pushes a resolution and removes the label manually. Per Mergify docs, merge_protections from `extends:` merges across parent and child with same-name override semantics; consumer repos can opt out by defining a same-named rule locally with weaker conditions. `dequeue` is not exposed as a YAML action under pull_request_rules — only as a user-facing slash command and a key under commands_restrictions. merge_protections is the declarative equivalent. Manual `@Mergifyio dequeue` remains available to Maintainers via commands_restrictions. Active ejection of an already-queued PR when the label arrives vs. holding the PR indefinitely in queue is not explicitly documented by Mergify; the comment marks this as TBD on first real backport conflict. --- .mergify.yml | 57 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/.mergify.yml b/.mergify.yml index 01b818e..74e9439 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -39,14 +39,22 @@ defaults: # accountability loop — they wrote the change, they know what the # backport should look like. # - # Operational caveat — the label DOES NOT block merge by itself. The - # 2026-05-12 incident below shipped because branch protection did not - # require a check that catches conflict markers. Repos consuming - # `extends: mergify` need either (a) a required CI status that fails - # on `<<<<<<<` in tracked files, or (b) a branch-protection ruleset - # that forbids merging while `backport-conflict` is present. Both - # belong to fleet-wide governance, tracked under T8850 (Cat 1 branch - # governance rulesets) rather than this central-config repo. + # Merge is blocked while the label is present — see the + # `merge_protections` block at the bottom of this file. Mergify + # documents that merge_protections are "safe to edit live; + # re-evaluations happen automatically", so a PR that gains the + # label after queueing will fail the merge check at the next + # evaluation. Active ejection of a queued PR vs. holding it + # indefinitely is not explicitly documented; manual fallback is + # `@Mergifyio dequeue` (allowed to Maintainers per + # commands_restrictions below). + # + # This does NOT replace branch protection. A determined operator can + # still bypass via the GitHub "Merge" button if branch protection + # doesn't require Mergify checks. The defense-in-depth piece (required + # CI status that fails on `<<<<<<<` in tracked files, or branch- + # protection ruleset on the label) is tracked under T8850 (Cat 1 + # branch governance rulesets). # # Incident history (2026-05-12): # vyos/vyos-documentation#1994 was backported via `@Mergifyio backport @@ -158,3 +166,36 @@ commands_restrictions: requeue: *allowed squash: *allowed update: *allowed + +merge_protections: + # Block merge while the backport-conflict label is set on a PR. The + # `backport` action above adds this label whenever a backport cherry-pick + # produces conflict markers; this rule ensures the conflicted PR cannot + # proceed to merge until a human removes the label (which they should + # only do after pushing a resolution commit that overwrites the markers). + # + # Why merge_protections and not a pull_request_rules `dequeue` action: + # Mergify exposes `dequeue` as a user-facing slash command and as a key + # under `commands_restrictions`, but NOT as a YAML action in + # pull_request_rules. `merge_protections` is the declarative equivalent — + # a PR with the label fails the success_conditions and is held back from + # merge; manual `@Mergifyio dequeue` remains available as the escape + # hatch (allowed to Maintainers per commands_restrictions above). + # + # Inheritance: per Mergify docs, merge_protections from `extends:` + # merges across parent and child, with same-named rules in the child + # overriding the parent. Consumer repos can disable this rule by + # defining a same-named rule locally with weaker conditions. + # + # Caveat: merge_protections only governs Mergify-driven merges. A + # determined operator can still click GitHub's merge button if branch + # protection doesn't require Mergify checks. Defense-in-depth (required + # CI status, branch-protection ruleset on the label) is tracked under + # T8850 Cat 1 branch governance. + - name: backport-conflict label must be absent to merge + description: > + Block merge while the backport-conflict label is present. The label + is auto-applied by the backport action on cherry-pick conflicts and + must be manually removed after the conflict is resolved in the PR. + success_conditions: + - label != backport-conflict -- cgit v1.2.3