From 2c9bd555548c062c86257543923202725b03a97a Mon Sep 17 00:00:00 2001 From: Julien Danjou Date: Fri, 21 Nov 2025 16:13:12 +0100 Subject: [PATCH 1/3] docs(mq): add scopes This adds a general documentation page on the scope features for itself. Change-Id: I87095c316b9a9995f59cf54ea29d5d7955dd477e --- src/content/docs/merge-queue.mdx | 16 ++- src/content/docs/merge-queue/monorepo.mdx | 2 + src/content/docs/merge-queue/scopes.mdx | 159 ++++++++++++++++++++++ src/content/navItems.tsx | 2 + 4 files changed, 173 insertions(+), 6 deletions(-) create mode 100644 src/content/docs/merge-queue/scopes.mdx diff --git a/src/content/docs/merge-queue.mdx b/src/content/docs/merge-queue.mdx index dacf5ed61a..bb81cac9ae 100644 --- a/src/content/docs/merge-queue.mdx +++ b/src/content/docs/merge-queue.mdx @@ -110,14 +110,18 @@ quickly consume significant compute resources and budget. **The Solution:** A merge queue enables several cost-saving strategies: - **[Batch merging](/merge-queue/batches)**: Combine multiple PRs into a single - batch and test them together. Instead of running CI separately for 5 PRs, run - it once for all 5 together, drastically reducing CI usage. + batch and test them together. Instead of running CI separately for 5 PRs, run + it once for all 5 together, drastically reducing CI usage. + +- **[Scope-aware batching](/merge-queue/scopes)**: Attach scopes to pull + requests so Mergify groups related changes together. Shared test runs can be + reused, making batching safer and more efficient—especially in monorepos. - **[Two-step CI](/merge-queue/two-step)**: Move expensive tests (like E2E - tests) to run only in the merge queue instead of on every PR update. Since - PRs often get updated multiple times before merging—or may never merge at - all—this can dramatically reduce CI costs while actually improving reliability - by ensuring those critical tests run against the real merge state. + tests) to run only in the merge queue instead of on every PR update. Since + PRs often get updated multiple times before merging—or may never merge at + all—this can dramatically reduce CI costs while actually improving reliability + by ensuring those critical tests run against the real merge state. With these strategies, you can significantly reduce CI costs compared to running all tests on every PR update, while simultaneously gaining better reliability diff --git a/src/content/docs/merge-queue/monorepo.mdx b/src/content/docs/merge-queue/monorepo.mdx index 8ba287c40c..226029c0ad 100644 --- a/src/content/docs/merge-queue/monorepo.mdx +++ b/src/content/docs/merge-queue/monorepo.mdx @@ -8,6 +8,8 @@ every change wastes time and CI resources. Mergify's **scopes** feature allows y batch pull requests based on which parts of your codebase they modify, dramatically improving merge queue efficiency. +For a deep dive into how scopes influence batching, see [Merge Queue Scopes](/merge-queue/scopes). + ## Understanding Scopes Scopes define discrete areas of your monorepo (like packages, services, or components). When a pull diff --git a/src/content/docs/merge-queue/scopes.mdx b/src/content/docs/merge-queue/scopes.mdx new file mode 100644 index 0000000000..aa31fba475 --- /dev/null +++ b/src/content/docs/merge-queue/scopes.mdx @@ -0,0 +1,159 @@ +--- +title: Merge Queue Scopes +description: Understand how Mergify groups pull requests by scope to build the most effective batches. +--- + +Mergify scopes describe the areas of your codebase that a pull request touches. By attaching scopes +to pull requests, the merge queue can build smarter batches, reuse the same CI work, and avoid +mixing unrelated changes. + +## Scope-aware batching at a glance + +When several pull requests are eligible for the next batch, Mergify compares their scopes and +prioritizes the combination that shares the most scopes in common. Pull requests that overlap in +scope are tested together first, while unrelated changes stay in the queue until a compatible batch +is available or Mergify needs them to fill the requested batch size. + +```dot class="graph" +strict digraph { + fontname="sans-serif"; + rankdir="LR"; + label="Scope-aware Batch Selection"; + nodesep=0.9; + ranksep=1.2; + splines=polyline; + + edge [fontname="sans-serif", color="#374151", arrowhead=none, penwidth=1.2]; + + subgraph cluster_preferred { + style="rounded"; + color="#1CB893"; + label="Preferred batch"; + + PR1 [shape=box, style="rounded,filled", fillcolor="#347D39", fontcolor="white", color="#165B33", margin="0.35,0.22", fontname="sans-serif", label="PR #101\nScopes: frontend, api"]; + PR2 [shape=box, style="rounded,filled", fillcolor="#347D39", fontcolor="white", color="#165B33", margin="0.35,0.22", fontname="sans-serif", label="PR #214\nScopes: frontend"]; + PR3 [shape=box, style="rounded,filled", fillcolor="#347D39", fontcolor="white", color="#165B33", margin="0.35,0.22", fontname="sans-serif", label="PR #305\nScopes: api, docs"]; + } + + PR4 [shape=box, style="rounded,filled", fillcolor="#6B7280", fontcolor="white", color="#4B5563", margin="0.35,0.22", fontname="sans-serif", label="PR #412\nScope: tooling"]; + + frontend [shape=oval, style="filled", fillcolor="#1CB893", fontcolor="#063C2C", color="#0B7A5C", margin="0.3,0.18", fontname="sans-serif", label="frontend"]; + api [shape=oval, style="filled", fillcolor="#1CB893", fontcolor="#063C2C", color="#0B7A5C", margin="0.3,0.18", fontname="sans-serif", label="api"]; + docs [shape=oval, style="filled", fillcolor="#1CB893", fontcolor="#063C2C", color="#0B7A5C", margin="0.3,0.18", fontname="sans-serif", label="docs"]; + tooling [shape=oval, style="filled", fillcolor="#1CB893", fontcolor="#063C2C", color="#0B7A5C", margin="0.3,0.18", fontname="sans-serif", label="tooling"]; + + PR1 -> frontend; + PR1 -> api; + PR2 -> frontend; + PR3 -> api; + PR3 -> docs; + PR4 -> tooling; +} +``` + +In the example above, the queue selects the three pull requests that share the `frontend` and `api` +scopes first. The change that only touches the `tooling` scope is kept aside unless Mergify needs it +later to complete the batch. + +## How Mergify builds scope-aware batches + +Scope-aware batching follows these steps: + +1. **Collect candidates:** Mergify looks at the next pull requests in queue order that pass the + queue rules and are ready to batch. + +2. **Score overlap:** It evaluates how many scopes each candidate shares with the already selected + pull requests and prefers combinations with the highest overlap. + +3. **Fill the batch:** If the required `batch_size` is not reached with strongly overlapping scopes, + Mergify expands the selection to the best remaining options so the batch can + still start. + +4. **Fallback safely:** When no scope overlap exists, the queue still batches the oldest pull + requests together to honour throughput, but these situations are the exception rather than the + rule. + +This strategy maximises CI reuse: tests that run for one pull request are likely valid for the other +changes in the batch because they touch the same areas of the codebase. + +## Scope assignment lifecycle + +Scopes can be attached to pull requests automatically or manually: + +- **File pattern detection:** Define scopes directly in your configuration so Mergify infers them + from changed paths once your CI uploads the results via + [`gha-mergify-ci`](https://github.com/Mergifyio/gha-mergify-ci) or an equivalent integration. + See [file-pattern scopes](/merge-queue/monorepo/file-patterns) for a + step-by-step setup. + +- **Manual upload:** Use the [`gha-mergify-ci`](https://github.com/Mergifyio/gha-mergify-ci) + GitHub Action, the REST API, or the `mergify scopes-send` CLI to push scopes computed by your own + tooling (Nx, Bazel, Turborepo, etc.). Examples are available in the build tool guides under + [Monorepo integrations](/merge-queue/monorepo). + +## Configuration schema + +Declare scopes at the top level of your `.mergify.yml` file: + +```yaml +scopes: + source: + files: + frontend: + includes: + - apps/web/**/* + api: + includes: + - services/api/**/*.py + docs: + includes: + - docs/**/* + merge_queue_scope: merge-queue + +queue_rules: + - name: default + batch_size: 3 +``` + +### Top-level keys + +- `scopes.source`: selects how scopes are provided. + - `files`: map scope names to the file patterns that define them. Each entry accepts `includes` + and optional `excludes` lists. + + - `manual`: instructs Mergify to expect scopes from external systems via the API or GitHub + Action. + + - `null`: disables scopes entirely. + +- `scopes.merge_queue_scope`: optional name automatically applied to temporary merge queue pull + requests (defaults to `merge-queue`). Set it to `null` to disable. + +### Manual source example + +```yaml +scopes: + source: + manual: + +queue_rules: + - name: default + batch_size: 5 +``` + +With the configuration above you must push scopes yourself—typically from a CI job that analyses the +pull request and calls `gha-mergify-ci` with the `scopes-upload` action. This is the recommended +approach when build systems such as Bazel, Nx, or Turborepo already know which projects are affected. + +## Best practices + +- Keep scope names stable and small in number so batches stay meaningful. + +- Prefer scopes that align with your CI topology—if a test suite covers a specific service or + package, create a scope with the same boundary. + +- Still configure sane `batch_max_wait_time` values: scopes help Mergify pick the right pull + requests, but you control how long it waits for an ideal batch. + +- Monitor merge queue analytics to verify that scope-aware batching increases success rate and + reduces redundant CI runs; adjust scope definitions when you see batches mixing unrelated changes. diff --git a/src/content/navItems.tsx b/src/content/navItems.tsx index 34e2b45fe1..c295d0b20f 100644 --- a/src/content/navItems.tsx +++ b/src/content/navItems.tsx @@ -18,6 +18,7 @@ import { FaBug, FaCircleXmark, FaGear, + FaLayerGroup, FaMoneyBill1, FaRegCircleCheck, FaRegCirclePause, @@ -143,6 +144,7 @@ const navItems: NavItem[] = [ { title: 'Performance', path: '/merge-queue/performance', icon: SlSpeedometer }, { title: 'Parallel Checks', path: '/merge-queue/parallel-checks', icon: TiFlowParallel }, { title: 'Batches', path: '/merge-queue/batches', icon: TbPackages }, + { title: 'Scopes', path: '/merge-queue/scopes', icon: FaLayerGroup }, { title: 'Two-Step CI', path: '/merge-queue/two-step', icon: FaStairs }, { title: 'Monorepo', From 0fcb02f3e766c0361107b6bbaf85a3c2befde78e Mon Sep 17 00:00:00 2001 From: Julien Danjou Date: Fri, 21 Nov 2025 16:58:47 +0100 Subject: [PATCH 2/3] Update src/content/docs/merge-queue/scopes.mdx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/content/docs/merge-queue/scopes.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/merge-queue/scopes.mdx b/src/content/docs/merge-queue/scopes.mdx index aa31fba475..6abcbe8562 100644 --- a/src/content/docs/merge-queue/scopes.mdx +++ b/src/content/docs/merge-queue/scopes.mdx @@ -70,7 +70,7 @@ Scope-aware batching follows these steps: still start. 4. **Fallback safely:** When no scope overlap exists, the queue still batches the oldest pull - requests together to honour throughput, but these situations are the exception rather than the + requests together to honor throughput, but these situations are the exception rather than the rule. This strategy maximises CI reuse: tests that run for one pull request are likely valid for the other From e415706c77635eae006913daeb89870a9d86353f Mon Sep 17 00:00:00 2001 From: Julien Danjou Date: Fri, 21 Nov 2025 16:58:54 +0100 Subject: [PATCH 3/3] Update src/content/docs/merge-queue/scopes.mdx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/content/docs/merge-queue/scopes.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/merge-queue/scopes.mdx b/src/content/docs/merge-queue/scopes.mdx index 6abcbe8562..91a87e8b74 100644 --- a/src/content/docs/merge-queue/scopes.mdx +++ b/src/content/docs/merge-queue/scopes.mdx @@ -73,7 +73,7 @@ Scope-aware batching follows these steps: requests together to honor throughput, but these situations are the exception rather than the rule. -This strategy maximises CI reuse: tests that run for one pull request are likely valid for the other +This strategy maximizes CI reuse: tests that run for one pull request are likely valid for the other changes in the batch because they touch the same areas of the codebase. ## Scope assignment lifecycle