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.

GitHub’s Two CI Status Systems

GitHub has two APIs for CI status reporting, and MergeGuard supports both:

API Used By How It Works
Checks API GitHub Actions, CircleCI Creates Check Runs with rich UI (annotations, summaries)
Status API Terraform Cloud, Jenkins, older tools Sets commit statuses (pending/success/failure)

Why This Matters:

  • External tools like Terraform Cloud use the Status API, not Checks API
  • MergeGuard queries both APIs and combines them when evaluating require.checks
  • You don’t need to know which API a tool uses—MergeGuard handles it

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. Terraform Cloud Conditional Status Checks

The Problem: GitHub’s native required checks fail when checks are conditional. Terraform Cloud webhooks only create status checks when .tf files change—PRs touching other files never get the check, causing required check requirements to block merge indefinitely.

The Solution: Use MergeGuard to require Terraform Cloud checks only when Terraform files are modified:

- name: "Terraform changes"
  priority: 20
  if:
    paths:
      - "terraform/**"
      - "infra/**/*.tf"
      - "*.tf"
  require:
    approvals: 2
    teams: ["platform-team"]
    checks:
      - "Terraform Cloud/**"  # Matches ANY Terraform Cloud workspace

- name: "Non-infrastructure changes"
  priority: 100
  require:
    approvals: 1
    # TF Cloud check NOT required - won't exist anyway

Why This Works:

  1. TF files changed → Terraform Cloud webhook fires → Creates status check → MergeGuard requires it
  2. Non-TF files changed → No webhook/check created → MergeGuard doesn’t require it → PR can merge

Wildcard Pattern Support:

MergeGuard supports wildcard patterns (using minimatch syntax) for check names, perfect for services that create dynamic check names:

- name: "Infrastructure changes"
  if:
    paths: ["terraform/**", "infra/**"]
  require:
    checks:
      - "Terraform Cloud/**"           # Matches all TF Cloud workspaces
      - "Vercel*"                      # Matches Vercel deployments
      - "deploy-*"                     # Matches deploy-prod, deploy-staging, etc.
      - "integration-tests-shard-*"   # Matches parallelized test shards

Common Patterns:

  • Terraform Cloud/** - Match any project/workspace combination
  • Terraform Cloud/my-project/** - Match specific project, any workspace
  • Vercel* - Match all Vercel deployment checks
  • deploy-{prod,staging} - Match specific deployment environments

All matching checks must pass: If the pattern matches multiple checks (e.g., multiple TF workspaces), ALL of them must complete successfully.

Applies to any conditional webhook:

  • Terraform Cloud (project/workspace combinations)
  • Vercel deployments (only when frontend changes)
  • Database migration tools (only when schema changes)
  • Any external CI that conditionally creates checks

Result: You can’t use GitHub’s native required checks for conditional webhooks, but MergeGuard makes it work.


4. 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.


5. 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.


6. 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.


7. 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 evaluates both Check Runs (Checks API) and Commit Statuses (Status API):

Check Runs (Checks API)

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

Commit Statuses (Status API)

State MergeGuard Treats As Blocks Merge?
success ✅ Passed No
failure ❌ Failed Yes
error ❌ Failed Yes
pending ⏳ In progress Yes
(missing) ❌ Not run 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 →