Conditional Approvals

← Back to Features

Require different reviewers based on PR context: files changed, lines of code, author, labels, and more.


Overview

GitHub’s native CODEOWNERS provides static file-to-owner mapping, but lacks flexibility:

  • Can’t require “at least one from team A AND team B”
  • Can’t scale approvals based on change size
  • Can’t adjust requirements by label or author

MergeGuard’s conditional approvals solve this with dynamic, context-aware rules.


Configuration Options

Approval Count

Require a specific number of approvals:

require:
  approvals: 2

Team-Based Approvals

Require at least one member from each listed team to approve:

require:
  teams:
    - "platform-team"
    - "security-team"

Behavior:

  • ✅ At least one member from platform-team must approve
  • ✅ At least one member from security-team must approve
  • ✅ Team membership checked automatically via GitHub API

User-Based Approvals

Require all listed users to approve:

require:
  users:
    - "tech-lead"
    - "security-expert"

Behavior:

  • tech-lead must approve
  • security-expert must approve
  • ✅ All users in the list are required

Combined Requirements

You can combine approval types:

require:
  approvals: 3              # Total approvals needed
  teams: ["platform-team"]  # Including one from platform
  users: ["tech-lead"]      # And tech-lead specifically

Behavior:

  • ✅ At least 3 total approvals
  • ✅ At least one from platform-team
  • tech-lead must approve (counts toward the 3)

Use Cases with Examples

1. Two Teams Must Both Approve

Require cross-functional review for critical areas:

- name: "Infrastructure changes"
  if:
    paths:
      - "infra/**"
      - "k8s/**"
      - ".github/workflows/**"
  require:
    approvals: 2
    teams:
      - "platform-team"   # one member must approve
      - "security-team"   # one member must approve

Result: Infrastructure changes need platform AND security team eyes, plus 2 total approvals.


2. Scale Approvals by PR Size

Require more reviewers for larger changes:

rules:
  - name: "Tiny changes"
    priority: 50
    if:
      maxLocChanged: 10
    require:
      approvals: 1

  - name: "Medium changes"
    priority: 60
    if:
      maxLocChanged: 300
    require:
      approvals: 2

  - name: "Large changes"
    priority: 70
    require:
      approvals: 3

Result:

  • ≤10 LOC → 1 approval
  • ≤300 LOC → 2 approvals
  • >300 LOC → 3 approvals

3. Emergency Label Triggers On-Call Team

Use labels to route to specific teams:

- name: "Emergency on-call gate"
  priority: 5
  if:
    labels: ["emergency"]
  require:
    approvals: 1
    teams:
      - "on-call-team"
    users:
      - "incident-commander"

Result: PRs with emergency label need on-call team + incident commander approval, bypassing normal 2-approval default.


4. Path-Based Team Routing

Different teams for different code areas:

rules:
  - name: "Frontend changes"
    if:
      paths: ["web/**", "src/frontend/**"]
    require:
      approvals: 1
      teams: ["frontend-team"]

  - name: "Backend changes"
    if:
      paths: ["api/**", "src/backend/**"]
    require:
      approvals: 2
      teams: ["backend-team"]

  - name: "Database migrations"
    if:
      paths: ["db/migrations/**"]
    require:
      approvals: 2
      teams: ["backend-team", "platform-team"]
      users: ["dba-lead"]

Result: Different teams review different areas, with database migrations requiring extra oversight.


5. Author-Based Requirements

Different rules for bots vs. humans:

rules:
  - name: "Bot PRs"
    priority: 10
    if:
      author: "renovate[bot]"
    require:
      approvals: 1
    action:
      autoApprove: true

  - name: "Human PRs"
    priority: 100
    require:
      approvals: 2

Result: Bot PRs auto-approved with 1 approval; human PRs need 2.


6. Specific User for Releases

Require release manager for version bumps:

- name: "Release PRs"
  if:
    labels: ["release"]
    paths: ["package.json", "VERSION"]
  require:
    approvals: 2
    users: ["release-manager"]
    teams: ["engineering-leads"]

Result: Release PRs need release manager + one engineering lead.


7. Exclude Paths for Fast-Track

Require fewer approvals when sensitive paths aren’t touched:

- name: "Fast-track safe changes"
  priority: 20
  if:
    maxLocChanged: 20
    excludePaths:
      - "infra/**"
      - "secrets/**"
      - ".github/workflows/**"
  require:
    approvals: 1
  action:
    autoApprove: true

Result: Small changes outside critical paths get auto-approved.


How It Works

1. PR Context Built

When a PR is opened/updated, MergeGuard collects:

  • Existing approvals (who has approved)
  • Team memberships (via GitHub API)
  • PR metadata (files, LOC, labels, author)

2. Rules Evaluated

For each rule that matches:

  • Check if required users have approved
  • Check if at least one member from each required team has approved
  • Count total approvals

3. Check Run Updated

GitHub Check shows:

  • ✅ Teams satisfied (e.g., “platform-team: @alice approved”)
  • ❌ Teams unsatisfied (e.g., “security-team: awaiting approval”)
  • ✅ Users satisfied (e.g., “tech-lead: approved”)
  • ❌ Users unsatisfied (e.g., “dba-lead: awaiting approval”)
  • Approval count (e.g., “2/3 approvals received”)

4. Merge Blocked or Allowed

  • ❌ If any requirement unmet → Check fails, merge blocked
  • ✅ If all requirements met → Check passes, merge allowed

Best Practices

Start Conservative

Begin with higher approval counts and relax over time:

- name: "Default"
  require:
    approvals: 2

Use Exclusions for Fast-Track

Combine positive and negative conditions:

- name: "Fast-track"
  priority: 10
  if:
    maxLocChanged: 10
    excludePaths: ["infra/**"]
  require:
    approvals: 1

Layer by Specificity

High-priority rules for specific cases, lower priority for defaults:

- name: "Critical paths"
  priority: 10
  if:
    paths: ["infra/**"]
  require:
    approvals: 3

- name: "Default"
  priority: 100
  require:
    approvals: 1

Document Intent

Use descriptions to explain complex rules:

- name: "Database migrations"
  description: "Migrations affect production data and need DBA review"
  require:
    users: ["dba-lead"]

Troubleshooting

Team Approvals Not Satisfied

Check:

  1. Reviewer is actually a member of the required team (GitHub API sync may lag)
  2. Review is “Approved” not just “Commented”
  3. All required teams have at least one approval

Debug: Check the MergeGuard Check Run details—it shows which teams are satisfied/unsatisfied.

User Approvals Not Counted

Check:

  1. Username matches exactly (case-sensitive)
  2. User has actually submitted an approval (not just commented)
  3. Approval wasn’t dismissed after submission


← Back to Features Next: Context-Aware Checks →