Context-Aware Checks
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
- Mark checks as “required” in branch protection
- All required checks must pass for every PR
- No way to conditionally require checks
MergeGuard Context-Aware Checks
- Rules specify which checks are required when they match
- Only specified checks are enforced for that rule
- 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-testsmust be completed withsuccess,neutral, orskipped - ✅
security-scanmust be completed withsuccess,neutral, orskipped - ❌ 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:
- E2E workflow only runs when backend changes (saves compute)
- 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
pathsfilter might be wrong.
Troubleshooting
Check Not Running
Possible Causes:
- GitHub Actions
pathsfilter excludes the changed files - Workflow disabled or not triggered on
pull_requestevents - Check name in MergeGuard doesn’t match actual check name
Debug:
- Check PR “Checks” tab—is the check listed?
- Review workflow YAML for
pathsandontriggers - Verify check name matches exactly (case-sensitive)
Check Required But Shouldn’t Be
Check:
- Review rule conditions—does the rule actually match?
- Check rule priority—is a higher-priority rule being enforced?
- Look at MergeGuard Check Run details to see which rule matched
All Checks Required Despite Rules
Check:
- Ensure checks aren’t marked as required in branch protection settings (conflicts with MergeGuard)
- 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.
Related Features
- Conditional Approvals - Dynamic reviewer requirements
- Auto-Merge - Auto-merge when checks pass
- Priority Rules - Override check requirements
| ← Back to Features | Next: Auto-Approval & Auto-Merge → |