---
title: Integrating GitHub Actions with Mergify
description: Run CI Insights, Job Selection, and Merge Queue Scopes on GitHub Actions.
---

<IntegrationLogo src={ghaLogo} alt="GitHub Actions logo"/>

[GitHub Actions](https://github.com/features/actions) is the CI/CD platform
provided by GitHub. Mergify has native integrations for GitHub Actions covering
CI Insights, Job Selection, and Merge Queue Scopes. Any GitHub Actions job status
can also be referenced from your Mergify
[conditions](/configuration/conditions) via `check-success`.

## Prerequisites

1. You've set up GitHub Actions in your repository. If you're new to GitHub
   Actions, see their
   [official documentation](https://docs.github.com/en/actions).

2. GitHub Actions is already configured to report job statuses to your pull
   requests.

3. The [Mergify GitHub App](/integrations/github) is installed in your repository.

## Mergify features for GitHub Actions

- **[CI Insights](/ci-insights/setup/github-actions)**: collect job metrics,
  detect flaky tests, and get Slack notifications for CI failures.

- **[Job Selection](#job-selection)**: skip unaffected jobs on pull requests to cut
  CI spend in monorepos.

- **[Merge Queue Scopes](/merge-queue/scopes)**: run only the jobs
  affected by a batch when processing the merge queue. Examples for
  [Bazel](/merge-queue/scopes/bazel),
  [Nx](/merge-queue/scopes/nx), and
  [Turborepo](/merge-queue/scopes/turborepo) are available.

- **[`github_actions` action](/workflow/actions/github_actions)** (deprecated):
  trigger a workflow directly from a Mergify rule.

:::caution
  To match a GitHub Actions status check with `check-success`, use the job
  name only, not the workflow name.
:::

{/* Keeps links to the section's former name, Monorepo CI, landing here. */}
<span id="monorepo-ci"></span>

## Job Selection

Job Selection is [scopes](/merge-queue/scopes) applied to your workflows. The same
`scopes` block that lets the merge queue batch by scope also tells each job
whether it has work to do, so a pull request that only touches the frontend
does not pay for the API and docs test suites.

This section covers job skipping on pull requests. For smarter batching or
[parallel mode](/merge-queue/queue-modes#parallel-mode) in the merge queue,
scopes already do that on their own. See
[Merge Queue Scopes](/merge-queue/scopes).

Running Buildkite instead? The
[Buildkite integration](/integrations/buildkite#job-selection) covers the same
ground. Scopes themselves are CI-agnostic, so for any other provider
[let us know](mailto:support@mergify.com) and see
[other build tools](/merge-queue/scopes/others).

### Declare your scopes

The action reads the `scopes` block of your `.mergify.yml` to know which areas
of the repository map to each scope name:

```yaml
scopes:
  source:
    files:
      frontend:
        include:
          - apps/web/**/*
      api:
        include:
          - services/api/**/*
      docs:
        include:
          - docs/**/*
```

### Workflow outline

A GitHub Actions workflow driven by scopes has three parts:

1. **Detect scopes** using the
   [`gha-mergify-ci`](https://github.com/Mergifyio/gha-mergify-ci) action. Its
   inputs and outputs are listed in the
   [action reference](/integrations/gha/reference).

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.

```dot class="queue" title="detect-scopes runs first, and each test job runs only when its scope changed"
digraph {
  PR [label="Pull request\nchanges", class="external"];
  Config [label="Scopes config\n(.mergify.yml)", class="config"];
  Detect [label="detect-scopes job\n(gha-mergify-ci)", class="pending"];

  Frontend [label="frontend-tests\n(run)", class="pending"];
  Docs [label="docs-tests\n(run)", class="pending"];
  API [label="api-tests\n(skipped)", class="muted"];

  PR -> Detect;
  Config -> Detect;
  Detect -> Frontend [label="scope: frontend"];
  Detect -> Docs [label="scope: docs"];
  Detect -> API [label="scope: api (false)", class="muted", style=dashed];

  Gate [label="ci-gate\n(optional)", class="external"];
  Frontend -> Gate;
  Docs -> Gate;
}
```

### Example workflow

:::caution
  On a `pull_request` event the `scopes` action works out the base and head to
  diff on its own. Any other event has no pull request to take them from, so
  either pass an explicit [`base`](/integrations/gha/reference#input-base), or
  guard the job with `if: ${{ github.event_name == 'pull_request' }}` and skip
  it. Sending the scopes to Mergify is a no-op outside a pull request either
  way: on other events the action only writes its `scopes` output.
:::

```yaml
name: 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@@@GHA_MERGIFY_CI_VERSION@@
        with:
          action: scopes
          token: ${{ secrets.MERGIFY_TOKEN }}

  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:
      - detect-scopes
      - frontend-tests
      - api-tests
      - docs-tests
    runs-on: ubuntu-24.04
    steps:
      - name: Report status
        uses: Mergifyio/gha-mergify-ci@@@GHA_MERGIFY_CI_VERSION@@
        with:
          action: wait-jobs
          jobs: ${{ toJSON(needs) }}
```

In this workflow:

- `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, reducing redundant
  builds.

- The final `ci-gate` job ensures that the aggregated status reflects the actual
  CI coverage, even if some jobs were skipped. Keep `detect-scopes` in its
  `needs`: without it, a failed scope detection skips every test job and
  `ci-gate` still reports success.

The `token` on `detect-scopes` is a Mergify application key with the [`ci`
scope](/api/usage#application-key-scopes), stored as a repository secret; an
`admin` key is rejected. It lets the action send the scopes it detected back to
Mergify, which is how [Merge Queue Scopes](/merge-queue/scopes) know what a
queued pull request affects. `wait-jobs` needs no token. For pull requests
opened by Dependabot, add the same secret to your [Dependabot
secrets](/integrations/dependabot#configuring-mergify_token-for-dependabot) as
well.

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

<Image src={ghaSummaryScreenshot} alt="GitHub Actions job summary listing the scopes detected for a pull request" />

### 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.
