Context-Aware Checks

← Back to Features

Only require specific GitHub checks when relevant paths are modified. Reduce CI costs by skipping expensive tests when they’re not needed.


The Problem

GitHub’s branch protection lets you mark checks as “required,” but they’re always required—even when irrelevant:

  • ❌ Documentation changes trigger 30-minute E2E test suite
  • ❌ Frontend changes run expensive backend integration tests
  • ❌ Infrastructure changes waste time on unrelated checks

Result: Wasted CI resources, longer PR cycle times, higher costs.


The MergeGuard Solution

Use the checks requirement to conditionally enforce which checks must pass based on PR context:

- name: "Backend changes need full tests"
  if:
    paths: ["src/backend/**"]
  require:
    checks:
      - "unit-tests"
      - "integration-tests"
      - "e2e-tests"  # Expensive - only required when backend changes

Result: E2E tests only required (and only run, if your CI is smart) when backend code changes.


How It Works

Traditional GitHub Checks

  1. Mark checks as “required” in branch protection
  2. All required checks must pass for every PR
  3. No way to conditionally require checks

MergeGuard Context-Aware Checks

  1. Rules specify which checks are required when they match
  2. Only specified checks are enforced for that rule
  3. Other checks are ignored (not required for merge)

Key Insight: MergeGuard’s Check Run blocks merge, not the individual CI checks. So you control which CI checks matter per rule.


Configuration

Basic Check Requirement

require:
  checks:
    - "unit-tests"
    - "security-scan"

Behavior:

  • unit-tests must be completed with success, neutral, or skipped
  • security-scan must be completed with success, neutral, or skipped
  • ❌ If checks are missing, pending, or failed → MergeGuard check fails

Path-Based Checks

Only require expensive checks when certain paths change:

rules:
  - name: "Backend changes"
    if:
      paths:
        - "src/backend/**"
        - "api/**"
    require:
      checks:
        - "unit-tests"
        - "integration-tests"
        - "e2e-tests"

  - name: "Frontend changes"
    if:
      paths:
        - "web/**"
        - "src/frontend/**"
    require:
      checks:
        - "frontend-tests"
        - "visual-regression"

  - name: "Documentation only"
    if:
      paths: ["docs/**"]
      excludePaths: ["src/**"]
    require:
      approvals: 1
    # No checks required!

Result:

  • Backend changes → run backend tests only
  • Frontend changes → run frontend tests only
  • Docs changes → skip all tests (just need approval)

Use Cases with Examples

1. Skip CI for Documentation

Save $$$ by not running tests on docs:

- name: "Docs only"
  priority: 10
  if:
    paths: ["docs/**", "README.md"]
    excludePaths: ["src/**"]
  require:
    approvals: 1
  # No checks required - merge with approval only

Impact: If 20% of PRs are docs-only, this saves 20% of CI costs.


2. Expensive E2E Tests for Backend Only

Run slow tests only when backend changes:

- name: "Backend needs E2E"
  if:
    paths:
      - "src/backend/**"
      - "src/api/**"
      - "db/migrations/**"
  require:
    approvals: 1
    checks:
      - "unit-tests"
      - "integration-tests"
      - "e2e-tests"  # 30min suite - only when needed

Impact: If 50% of PRs don’t touch backend, you skip E2E tests half the time.


3. Security Scans for Infrastructure Only

Only run Terraform/Trivy scans when infra changes:

- name: "Infrastructure"
  if:
    paths:
      - "infra/**"
      - "terraform/**"
      - "k8s/**"
  require:
    approvals: 2
    teams: ["platform-team"]
    checks:
      - "terraform-validate"
      - "terraform-plan"
      - "trivy-scan"
      - "checkov-scan"

Result: Security scans only run when infrastructure files change.


4. Tiered Checks by PR Size

Run expensive checks only for large PRs:

rules:
  - name: "Small changes"
    priority: 50
    if:
      maxLocChanged: 50
    require:
      approvals: 1
      checks:
        - "lint"
        - "unit-tests"

  - name: "Large changes"
    priority: 60
    if:
      maxLocChanged: 999999  # everything
    require:
      approvals: 2
      checks:
        - "lint"
        - "unit-tests"
        - "integration-tests"
        - "e2e-tests"

Result: Small PRs skip expensive integration/E2E tests.


5. Label-Triggered Checks

Use labels to require specific checks:

- name: "Performance label"
  if:
    labels: ["performance"]
  require:
    checks:
      - "performance-benchmarks"
      - "load-tests"

- name: "Security label"
  if:
    labels: ["security"]
  require:
    checks:
      - "security-scan"
      - "dependency-audit"

Result: Opt-in to expensive checks via labels.


6. Dependabot with Security Scan Only

Skip most checks for Dependabot, but keep security:

- name: "Dependabot updates"
  priority: 10
  if:
    author: "dependabot[bot]"
    semverLevel: ["patch", "minor"]
  require:
    checks:
      - "security-scan"  # Still verify no vulnerabilities
  action:
    autoApprove: true
    autoMerge:
      requireChecks: true
      mergeMethod: "squash"

Result: Dependabot PRs auto-merge after security scan, skipping other tests.


Combining with CI Workflow Filters

For maximum savings, combine MergeGuard rules with GitHub Actions paths filters:

GitHub Actions Workflow:

name: E2E Tests
on:
  pull_request:
    paths:
      - 'src/backend/**'
      - 'src/api/**'
jobs:
  e2e:
    runs-on: ubuntu-latest
    steps:
      - run: yarn test:e2e

MergeGuard Config:

- name: "Backend changes"
  if:
    paths:
      - "src/backend/**"
      - "src/api/**"
  require:
    checks:
      - "E2E Tests"

Result:

  1. E2E workflow only runs when backend changes (saves compute)
  2. E2E check only required when backend changes (enforced by MergeGuard)

Check Status Behavior

MergeGuard accepts these check conclusions:

Conclusion MergeGuard Treats As Blocks Merge?
success ✅ Passed No
neutral ✅ Passed No
skipped ✅ Passed No
failure ❌ Failed Yes
timed_out ❌ Failed Yes
cancelled ❌ Failed Yes
action_required ❌ Failed Yes
(missing) ❌ Not run Yes
(pending) ⏳ In progress Yes

Note: neutral and skipped are treated as passing, so you can use workflow if conditions to skip checks conditionally.


Best Practices

1. Start with All Checks, Then Carve Out Exceptions

- name: "Docs skip CI"
  priority: 10
  if:
    paths: ["docs/**"]
    excludePaths: ["src/**"]
  require:
    approvals: 1
  # No checks

- name: "Default policy"
  priority: 100
  require:
    approvals: 2
    checks:
      - "unit-tests"
      - "integration-tests"

2. Use Exclusions to Protect Critical Paths

- name: "Fast-track small changes"
  if:
    maxLocChanged: 10
    excludePaths:
      - "infra/**"
      - ".github/workflows/**"
  require:
    approvals: 1
  # No expensive checks for small, safe changes

3. Align CI Triggers with MergeGuard Rules

Keep GitHub Actions paths filters in sync with MergeGuard if.paths for maximum savings.

4. Monitor Check Failures

Watch for patterns:

  • Checks failing often? Maybe they’re flaky.
  • Check never runs? Workflow paths filter might be wrong.

Troubleshooting

Check Not Running

Possible Causes:

  1. GitHub Actions paths filter excludes the changed files
  2. Workflow disabled or not triggered on pull_request events
  3. Check name in MergeGuard doesn’t match actual check name

Debug:

  • Check PR “Checks” tab—is the check listed?
  • Review workflow YAML for paths and on triggers
  • Verify check name matches exactly (case-sensitive)

Check Required But Shouldn’t Be

Check:

  1. Review rule conditions—does the rule actually match?
  2. Check rule priority—is a higher-priority rule being enforced?
  3. Look at MergeGuard Check Run details to see which rule matched

All Checks Required Despite Rules

Check:

  1. Ensure checks aren’t marked as required in branch protection settings (conflicts with MergeGuard)
  2. Verify MergeGuard Check Run is present and passing

Cost Savings Calculator

Example scenario:

  • 100 PRs/month
  • E2E tests cost $5/run (30min on beefy runners)
  • 40% of PRs are docs/frontend-only (no backend changes)

Without MergeGuard:

  • 100 PRs × $5 = $500/month

With MergeGuard:

  • 60 backend PRs × $5 = $300
  • 40 other PRs × $0 = $0
  • Total: $300/month (40% savings)

Adjust for your team’s PR volume and CI costs.



← Back to Features Next: Auto-Approval & Auto-Merge →