gr pr merge refuses with checks still running for a check that has
completed with conclusion: skipped.
Measured on a real PR in this repository (#917):
check run: sync-status status=completed conclusion=skipped
GitHub: mergeable=MERGEABLE mergeStateStatus=CLEAN
Control: the base branch's head commit carries 18 check runs through the same
API path, so the query finds checks when checks exist. GitHub itself
considered the PR clean and mergeable.
Why this is worse than a nuisance. A skipped check never becomes green, so
the gate can never be satisfied by waiting — the only exits are --force or
--skip-gate checks. A gate that cannot pass trains people to reach past it,
and --force waives mergeable along with everything else. So a
misclassification in the least important gate quietly disarms the most
important one. This is the same shape as a gate whose underlying quantity is
structurally always zero: it fails identically on a healthy PR and a broken
one, which carries the same information as always passing.
Fix: treat a check run as blocking only when status is not completed.
A completed run with conclusion in {skipped, neutral} is not running and
must not be reported as running; {failure, timed_out, cancelled, action_required} is a real block and should say so with the conclusion named,
not with "still running".
Also worth doing: print the check name and its status/conclusion in the
refusal. checks still running gave no way to tell a queued suite from a
skipped no-op without going to the API by hand.
Not a regression from #917; found while merging it.
gr pr mergerefuses withchecks still runningfor a check that hascompleted with
conclusion: skipped.Measured on a real PR in this repository (#917):
Control: the base branch's head commit carries 18 check runs through the same
API path, so the query finds checks when checks exist. GitHub itself
considered the PR clean and mergeable.
Why this is worse than a nuisance. A skipped check never becomes green, so
the gate can never be satisfied by waiting — the only exits are
--forceor--skip-gate checks. A gate that cannot pass trains people to reach past it,and
--forcewaivesmergeablealong with everything else. So amisclassification in the least important gate quietly disarms the most
important one. This is the same shape as a gate whose underlying quantity is
structurally always zero: it fails identically on a healthy PR and a broken
one, which carries the same information as always passing.
Fix: treat a check run as blocking only when
statusis notcompleted.A completed run with
conclusionin{skipped, neutral}is not running andmust not be reported as running;
{failure, timed_out, cancelled, action_required}is a real block and should say so with the conclusion named,not with "still running".
Also worth doing: print the check name and its status/conclusion in the
refusal.
checks still runninggave no way to tell a queued suite from askipped no-op without going to the API by hand.
Not a regression from #917; found while merging it.