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.
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-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. 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:
- TF files changed → Terraform Cloud webhook fires → Creates status check → MergeGuard requires it
- 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 combinationTerraform Cloud/my-project/**- Match specific project, any workspaceVercel*- Match all Vercel deployment checksdeploy-{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:
- E2E workflow only runs when backend changes (saves compute)
- 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
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 → |