Monorepo CI with GitHub Actions

Use `gha-mergify-ci` to run only the workflows impacted by a pull request.


Mergify’s GitHub Actions integration makes scopes actionable in your CI. This guide shows how to wire scopes into your workflows so that each pull request runs only the jobs it truly needs.

Before you start, declare scopes in your .mergify.yml so the action knows which areas of the repo map to each scope name:

scopes:
  source:
    files:
      frontend:
        includes:
          - apps/web/**/*
      api:
        includes:
          - services/api/**/*
      docs:
        includes:
          - docs/**/*

A typical GitHub Actions pipeline with scopes consists of three parts:

  1. Detect scopes using the gha-mergify-ci action.

  2. Reuse the scope outputs to conditionally run jobs.

  3. Publish a final status (for example with a ci-gate job) if you want one check that reflects all the jobs that ran.

%0 PR Pull request changes Detect detect-scopes job (gha-mergify-ci) PR->Detect Config Scopes config (.mergify.yml) Config->Detect Frontend frontend-tests (run) Detect->Frontend scope: frontend API api-tests (skipped) Detect->API scope: api (false) Docs docs-tests (run) Detect->Docs scope: docs Gate ci-gate (optional) Frontend->Gate Docs->Gate
name: Monorepo CI
on:
  pull_request:

jobs:
  detect-scopes:
    runs-on: ubuntu-24.04
    outputs:
      frontend: ${{ fromJSON(steps.scopes.outputs.scopes).frontend }}
      api: ${{ fromJSON(steps.scopes.outputs.scopes).api }}
      docs: ${{ fromJSON(steps.scopes.outputs.scopes).docs }}
    steps:
      - uses: actions/checkout@v5

      - name: Detect scopes
        id: scopes
        uses: Mergifyio/gha-mergify-ci@v11
        with:
          action: scopes

  frontend-tests:
    needs: detect-scopes
    if: ${{ needs.detect-scopes.outputs.frontend == 'true' }}
    uses: ./.github/workflows/frontend-tests.yaml
    secrets: inherit

  api-tests:
    needs: detect-scopes
    if: ${{ needs.detect-scopes.outputs.api == 'true' }}
    uses: ./.github/workflows/api-tests.yaml
    secrets: inherit

  docs-tests:
    needs: detect-scopes
    if: ${{ needs.detect-scopes.outputs.docs == 'true' }}
    uses: ./.github/workflows/docs-tests.yaml
    secrets: inherit

  ci-gate:
    if: ${{ !cancelled() }}
    needs:
      - frontend-tests
      - api-tests
      - docs-tests
    runs-on: ubuntu-24.04
    steps:
      - name: Report status
        uses: Mergifyio/gha-mergify-ci@v11
        with:
          action: wait-jobs
          jobs: ${{ toJSON(needs) }}
  • detect-scopes calls gha-mergify-ci with the scopes action, which inspects the pull request diff and returns a JSON map of scopes set to true or false.

  • Each job checks the scope it cares about before running, dramatically reducing redundant builds.

  • The final ci-gate job ensures that the aggregated status reflects the actual CI coverage, even if some jobs were skipped.

Mergify also publishes annotations that can be seen in your GitHub Actions jobs summary.

GitHub Actions summary showing detected scopes and ci-gate result

Protecting the branch with ci-gate

Section titled Protecting the branch with ci-gate

Once ci-gate publishes a single status, add it as a required check in your GitHub branch ruleset so that only pull requests with the relevant jobs executed can merge.

Ready to reuse the same scopes for batching? Head over to Merge Queue Scopes to see how they power smarter batches.