Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXXXII · Protected BranchesMergeGates

Required approvals and status checks — the merge-block conditions

Advanced⏱ ~24 min🧪 Lab requiredgit

What you'll learn

  • Configure minimum approvals, dismissal of stale reviews, and CODEOWNERS-required reviews as a single rule set
  • Identify the difference between dismiss-stale and require-last-pusher-cannot-approve
  • Wire required status checks so that a failed check blocks the merge, not just the deploy
  • Diagnose why a status check was satisfied at the PR open but stale by the merge time

Prerequisites

Practice

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

Approvals and status checks are the conditions the forge evaluates at merge time. They are what turn a pull request from a feature into a gate: the merge is refused unless the configured number of approving reviews exist, the configured CODEOWNERs have reviewed, and the configured CI checks have passed. This lesson walks the four rules in the merge-gating stack and the failure modes that each one closes.

Approval minimums and dismissal of stale reviews

The minimum approval rule is the simplest of the four: a configured integer N, with the merge refused unless the pull request has at least N approving reviews from eligible reviewers. “Eligible” usually means “not the pusher” - the “last pusher cannot approve” rule that most forges apply by default. The point is to require a second pair of eyes that is not the author’s.

flowchart LR
    A[Pull request opened] --> B[Reviews collected]
    B --> C{Approvals >= minimum?}
    C -->|no| D[Merge refused]
    C -->|yes| E{CODEOWNERS satisfied?}
    E -->|no| D
    E -->|yes| F{Status checks passing?}
    F -->|no| D
    F -->|yes| G[Merge accepted]

Dismissal of stale reviews is the natural partner. A stale review is an approval that was given against an earlier version of the pull request; once the pusher pushes new commits, the approval predates the latest code and should not count. Forges vary:

  • GitHub exposes “Dismiss stale pull request approvals when new commits are pushed” as a toggle per branch. When on, any new commit to the head branch dismisses all prior approvals and the pull request must be re-approved.
  • GitLab exposes the same idea via “Approvals reset on push” or “reset_approvals_on_push” in the approvals API.
  • Bitbucket exposes a similar option in repository settings.

The combination of a minimum N and dismissal of stale reviews turns the approval gate into a current review of the current code. Without dismissal, a pusher can rewrite the pull request after an approval and the approval still applies to the original commits - which are no longer reachable from the head.

Require CODEOWNERS review

The “require CODEOWNERS review” rule adds a layer on top of the minimum approval count. Even if N approvals exist, if the CODEOWNERS file declares that a path must be reviewed by an owner, the merge is refused unless at least one of the listed owners has approved. On GitHub, the toggle is “Require review from Code Owners”; on GitLab, the rule is “require code owner approval”; Bitbucket has a “require default reviewer approval” mechanism that approximates it.

The rule is what couples CODEOWNERS (the routing layer) to branch protection (the enforcement layer). Without the rule, a CODEOWNERS entry is documentation. With the rule, the entry is an enforceable gate. This coupling is the topic of the closing lesson in this part, but the rule itself is configured here.

The failure mode to monitor: the CODEOWNERS file lists no reviewer for a path. In that case, “require CODEOWNERS review” has no requirement to apply to that path. Production discipline is to fill the CODEOWNERS file so no production path is left unowned. A path with no owner and a require-CODEOWNERS rule is a path with no merge block, which is the same as no protection.

Required status checks

A required status check is a named CI check that the forge will not merge without. The check is posted to the forge by the CI system (or by any application with the “statuses” permission) and must be green. The wire-level mechanism is the forge’s statuses API; the merge-blocking mechanism is the protection rule.

The two toggles that often distinguish a brittle rule from a robust one:

  • Require branches to be up to date before merging. The pull request’s head must include the latest from the base branch. If the base moved and the head is behind, the merge is refused until the head re-runs the required checks against the new merge point. Without this toggle, a green check against an outdated head is treated as valid.
  • Require status checks to be re-run on push. When the pusher pushes a new commit, prior checks expire and must re-run. Without this toggle, the forge accepts an already- passing check from a previous commit, and the merge can land against code the checks never saw.

For an infrastructure repository, the canonical required checks are:

  • lint / format check - the static checks that prevent common stylistic errors.
  • terraform plan (or equivalent) - the diff the merge will introduce, run against a fresh reference to the latest base.
  • security / policy check - the OPA/Conftest/Sentinel gate, the secret-scan result, the dependency-review verdict.
  • unit / acceptance tests - the tests that exercise the configurations the merge will affect.

Wiring the stack

The four rules together form one merge gate:

flowchart LR
    A[Pull request] --> B[Approvals >= N]
    B --> C[No stale approvals]
    C --> D[CODEOWNERS satisfied]
    D --> E[Status checks current and green]
    E --> F{Branch up to date?}
    F -->|yes| G[Merge accepted]
    F -->|no| H[Re-run required]

If any link is missing, the gate fails. Production discipline is to enumerate the gate as a single rule set per branch and to review it as a unit when the branch’s role changes (for example, when a release branch becomes the support branch for a fleet).

Production discipline

Four rules:

  1. The default branch has the gate as a unit. Approvals, CODEOWNERS, dismiss-stale, re-run-on-push, up-to-date branch, required checks - all configured, all reviewed together, all changed together.
  2. Required checks are owned. Each required check has a named owner who is accountable for its post-incident review and its maintenance. A check that no one owns is a check that no one updates.
  3. Re-runs on push and up-to-date branches are non-negotiable on production repositories. Skip either and the check becomes a snapshot of older code, not of the merge candidate.
  4. Approvals and status checks are necessary but not sufficient. A passing check does not certify that the change is correct; it certifies only that the change passed the checks. Review is still the operator’s responsibility.

Cross-course references

  • Linux for Production Sysadmins - Part XXXIV (ConfigMgmt) covers the same funnel: changes arrive through a controlled interface and pass through a gate, with the gate’s failure modes mapped to the same four categories.
  • Ansible for Production Sysadmins - Parts XXXVII-XXXVIII (RepoArch / QualGates) walk the merge-gating stack in Ansible-specific terms; the same four rules apply.
  • Terraform for Production Sysadmins - Parts XXXII-XXXIII (RepoGuard / StateGuard) cover the merge-gating stack on Terraform code, including the role of terraform plan as a required status check.

Quiz

Knowledge check · 4 questions

  1. Q1. A pull request has two approvals, but its pusher has pushed three new commits after the approvals. With 'dismiss stale pull request approvals when new commits are pushed' enabled, what is the merge-block state?

  2. Q2. A status check is 'green' so the merge can land even if the pull request head is several commits behind the base branch and a 'require branches to be up to date' rule is not enabled.

  3. Q3. List the four kinds of merge-gating rule that combine into a single protection stack on a production default branch.

  4. Q4. Diagnose why a merge-block status check was bypassed despite being listed as required.

    A Terraform change is opened as a pull request. The required check, 'terraform-plan', was added to the protection rule after the PR was opened. The 'terraform-plan' check exists in the merge UI as required, but the merge proceeds even when the check has never run. The team is asking: how can a required check be bypassed without an actual run?

Passing score: 75%. Answers are checked in this browser.