A GitHub Action that detects which parts of your monorepo are affected by a pull request, so you can run only the relevant tests.
In monorepos, running all tests for every pull request wastes time and resources. While GitHub Actions offers path filtering:
on:
  pull_request:
    paths:
      - 'js/**'This approach has a critical flaw: when a job doesn't run, you can't tell if it was skipped due to the filter or if CI failed to start. This becomes especially problematic with merge queues, where you don't want to skip tests on the second PR just because the first PR in the queue modified different files.
This action analyzes changed files and returns "scopes" indicating which parts of your codebase are affected. It's merge queue-aware, ignoring changes from PRs ahead in the queue to ensure accurate test selection.
When running in a merge queue context, the action automatically adds a merge-queue scope for integration tests.
The example bellow demonstrate how to setup a monorepo containing a Python and a Javascript project.
The Python jobs will ran only if *.py are modified, same the Javascript jobs and *.js files.
An additional job called integration will run only when on Mergify merge-queue branches.
Create a .mergify.yml file in your repository root:
scopes:
  source:
    files:
      python:
        includes:
          - **/*.py
      js:
        includes:
          - **/*.js
queue_rules:
    - name: default
      autoqueue: true
      queue_conditions:
        - check-success: alls-green
      merge_conditions:
        - check-success: alls-greenname: Continuous Integration
on:
  pull_request_target:
    types:
      - opened
jobs:
  scopes:
    runs-on: ubuntu-22.04
    outputs:
      js: ${{ fromJSON(steps.scopes.outputs.scopes).js }}
      python: ${{ fromJSON(steps.scopes.outputs.scopes).python }}
      merge-queue: ${{ fromJSON(steps.scopes.outputs.scopes).merge-queue }}
    steps:
      - uses: actions/checkout@v5
      - name: Get PR scopes
        id: scopes
        uses: Mergifyio/gha-mergify-ci
        with:
          action: scopes
          token: ${{ secrets.MERGIFY_TOKEN }}
  python:
    if: ${{ needs.scopes.outputs.python == 'true' }}
    needs: scopes
    uses: ./.github/workflows/python.yaml
    secrets: inherit
  js:
    if: ${{ needs.scopes.outputs.js == 'true' }}
    needs: scopes
    uses: ./.github/workflows/js.yaml
    secrets: inherit
  integration:
    if: ${{ needs.scopes.outputs.merge-queue == 'true' }}
    needs: scopes
    uses: ./.github/workflows/integration.yaml
    secrets: inherit
  alls-green:
    if: ${{ !cancelled() }}
    needs:
      - python
      - js
      - integration
    runs-on: ubuntu-latest
    steps:
      - name: Verify all jobs succeeded
        uses: re-actors/alls-green@release/v1
        with:
          allowed-skips: ${{ toJSON(needs) }}
          jobs: ${{ toJSON(needs) }}Each scope defined in .mergify.yml becomes an output:
scopes.<scope-name>: Returnstrueif the scope is affected,falseotherwisescopes.merge-queue: Automatically set totruewhen running in a merge queue
- Python >= 3.10 (available in the runner environment)