Implementing a Two-Step CI with Mergify
Run essential tests on every PR and comprehensive tests before merging, optimizing CI time and resources.
As your codebase grows, so does your test suite. What once took minutes can balloon into hour-long CI runs that slow down development and drain resources. But here’s the insight: not all tests need to run at every stage of the PR lifecycle.
Two-step CI is a strategy that separates your test suite into two phases—fast preliminary checks that run on every commit, and comprehensive validation that runs only before merging. Combined with Mergify’s merge queue, this approach can dramatically reduce CI time and costs while maintaining code quality.
Implementing Two-Step CI
Section titled Implementing Two-Step CIA two-step CI approach separates tests into two phases based on when they need to run and what they validate:
Step 1: Preliminary Tests (Fast Feedback)
Section titled Step 1: Preliminary Tests (Fast Feedback)Run immediately when a PR is opened or updated. These are:
- Fast: Complete in minutes, not hours
- Essential: Code quality checks that catch obvious issues
Examples: Linters, formatters, unit tests, basic compile checks
These give developers rapid feedback and prevent broken code from entering the merge queue.
Step 2: Pre-Merge Tests (Comprehensive Validation)
Section titled Step 2: Pre-Merge Tests (Comprehensive Validation)Run only when a PR enters the merge queue, just before merging. These are:
- Thorough: Full test coverage including edge cases
- Expensive: Integration tests, end-to-end tests, performance benchmarks
- Slower: May take 30+ minutes to complete
These jobs ensure code quality before merging to your main branch, without slowing down every PR update.
How It Works
Section titled How It WorksBatch Processing: Even More Efficient
Section titled Batch Processing: Even More EfficientWhen you enable batching in Mergify’s merge queue, you can achieve even greater efficiency. Instead of running pre-merge tests separately for each PR, Mergify:
- Groups multiple PRs together in a batch
- Runs the expensive pre-merge tests once for the entire batch
- Merges all PRs if the batch passes
Example: If you have 5 PRs ready to merge and your pre-merge tests take 30 minutes:
- Without batching: 5 × 30 minutes = 2.5 hours of CI time
- With batching: 1 × 30 minutes = 30 minutes of CI time
That’s 80% reduction in CI resource usage!
Implementation Guide
Section titled Implementation GuideImplementing two-step CI requires coordinating your CI system with Mergify’s merge queue. Here’s how:
Overview
Section titled Overview-
Configure CI to distinguish test phases: Set up your CI to run preliminary tests on all branches, but pre-merge tests only on merge queue branches
-
Configure Mergify conditions: Define which tests must pass to enter the queue vs. which must pass to merge
Step 1: Configure Your CI System
Section titled Step 1: Configure Your CI SystemThe key is to run pre-merge tests only on merge queue branches. By default,
Mergify creates branches prefixed with mergify/merge-queue/
for queued PRs
(customizable via queue_branch_prefix
in
queue_rules
).
Pattern:
- ✅ Preliminary tests: Run on all branches
- ✅ Pre-merge tests: Run only on branches matching
mergify/merge-queue/*
Example: GitHub Actions
Section titled Example: GitHub Actionsname: CI
on:
pull_request:
branches:
- main
jobs:
# STEP 1: Preliminary tests (runs on every PR)
preliminary-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run linters
run: make lint
- name: Run unit tests
run: make test-unit
# STEP 2: Pre-merge tests (runs only on merge queue branches)
pre-merge-tests:
if: startsWith(github.head_ref, 'mergify/merge-queue/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run integration tests
run: make test-integration
- name: Run E2E tests
run: make test-e2e
- name: Run performance benchmarks
run: make benchmark
How it works:
-
preliminary-tests
runs on every PR (fast feedback) -
pre-merge-tests
runs only when the branch name starts withmergify/merge-queue/
(comprehensive validation before merge)
Example: Other CI Systems
Section titled Example: Other CI SystemsThe same principle applies to any CI system. Just configure the pre-merge job to run only on merge queue branches:
- CircleCI: Use branch filters with regex
/^mergify\/merge-queue\/.*/
- Jenkins: Add conditional execution based on
BRANCH_NAME
- GitLab CI: Use
only: /^mergify\/merge-queue\/.*/
Step 2: Configure Mergify
Section titled Step 2: Configure MergifySet up your queue_rules
to define when PRs can enter the queue and when they
can merge:
queue_rules:
- name: default
# PRs can enter the queue after preliminary tests pass
queue_conditions:
- check-success=preliminary-tests
# PRs can merge only after pre-merge tests pass
merge_conditions:
- check-success=pre-merge-tests
What this means:
-
queue_conditions
: Preliminary tests must pass before a PR can enter the merge queue -
merge_conditions
: Pre-merge tests must pass before the PR actually merges to main