View as Markdown

Integrating Buildkite with Mergify

Run CI Insights, Monorepo CI, and Merge Queue Scopes on Buildkite.


Buildkite is a CI/CD tool with pipeline orchestration and self-hosted runners. Mergify has native integrations for Buildkite covering CI Insights, Monorepo CI, and Merge Queue Scopes. Any Buildkite status check reported to GitHub can also be referenced from your Mergify conditions via check-success.

  1. Buildkite is properly set up and is building your projects. See the Buildkite Documentation for help with setting up Buildkite.

  2. Buildkite is configured to report build statuses to GitHub.

  3. The Mergify GitHub App is installed in your repository.

Mergify features for Buildkite

Section titled Mergify features for Buildkite
  • CI Insights: collect Buildkite job metrics, detect flaky tests, and get Slack notifications for CI failures.

  • Monorepo CI: skip unaffected steps on pull requests to cut CI spend in monorepos.

  • Merge Queue Scopes: run only the jobs affected by a batch when processing the merge queue. Examples for Bazel, Nx, and Turborepo are available.

Monorepo CI is scopes applied to your pipelines. The same scopes block that lets the merge queue batch by scope also tells your pipeline which steps have 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 step skipping on pull requests. For smarter batching or parallel mode in the merge queue, scopes already do that on their own. See Merge Queue Scopes.

Running GitHub Actions instead? The GitHub Actions integration covers the same ground. Scopes themselves are CI-agnostic, so for any other provider let us know and see Merge Queue Scopes.

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

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

A Buildkite pipeline driven by scopes has two parts:

  1. Detect scopes using the mergifyio/mergify-ci plugin.

  2. Use a dynamic pipeline to conditionally upload only the steps that match the affected scopes.

PR Pull request changes Detect detect-scopes step (mergify-ci plugin) 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
.buildkite/pipeline.yml
steps:
- label: ":mag: Detect scopes"
key: detect-scopes
plugins:
- mergifyio/mergify-ci#v6:
action: scopes
token: "${MERGIFY_TOKEN}"
- label: ":pipeline: Upload conditional steps"
depends_on: detect-scopes
command: .buildkite/dynamic-pipeline.sh | buildkite-agent pipeline upload

The script below parses the scope results with jq, so install it on your Buildkite agents first.

.buildkite/dynamic-pipeline.sh
#!/bin/bash
set -euo pipefail
SCOPES=$(buildkite-agent meta-data get "mergify-ci.scopes" --default '{}')
echo "steps:"
if echo "$SCOPES" | jq -e '.frontend == "true"' > /dev/null 2>&1; then
cat <<'YAML'
- label: ":react: Frontend tests"
command: npm test
plugins:
- mergifyio/mergify-ci#v6:
action: junit-process
report_path: "junit.xml"
token: "${MERGIFY_TOKEN}"
YAML
fi
if echo "$SCOPES" | jq -e '.api == "true"' > /dev/null 2>&1; then
cat <<'YAML'
- label: ":gear: API tests"
command: pytest tests/api/
plugins:
- mergifyio/mergify-ci#v6:
action: junit-process
report_path: "reports/*.xml"
token: "${MERGIFY_TOKEN}"
YAML
fi
if echo "$SCOPES" | jq -e '.docs == "true"' > /dev/null 2>&1; then
cat <<'YAML'
- label: ":books: Docs build"
command: make docs
YAML
fi
# Buildkite rejects a pipeline with no steps, so emit a no-op when a pull
# request touches nothing in any scope.
if [ "$(echo "$SCOPES" | jq '[.[] | select(. == "true")] | length')" = "0" ]; then
cat <<'YAML'
- label: ":fast_forward: No affected scopes"
command: "true"
YAML
fi

Make the script executable:

Terminal window
chmod +x .buildkite/dynamic-pipeline.sh

In this pipeline:

  • The detect-scopes step calls the mergifyio/mergify-ci plugin with the scopes action, which inspects the pull request diff and stores a JSON map of scopes ("true" or "false") as Buildkite meta-data under the key mergify-ci.scopes.

  • The dynamic pipeline script reads the scopes meta-data and only emits the steps for scopes that are "true", so unaffected steps are skipped entirely.

  • Each test step can optionally use the junit-process action to upload test results to CI Insights.

If your monorepo builds live in separate Buildkite pipelines, use trigger steps instead:

.buildkite/dynamic-pipeline.sh
#!/bin/bash
set -euo pipefail
SCOPES=$(buildkite-agent meta-data get "mergify-ci.scopes" --default '{}')
echo "steps:"
if echo "$SCOPES" | jq -e '.frontend == "true"' > /dev/null 2>&1; then
cat <<'YAML'
- trigger: "frontend-pipeline"
label: ":react: Trigger frontend pipeline"
build:
branch: "${BUILDKITE_BRANCH}"
commit: "${BUILDKITE_COMMIT}"
YAML
fi
if echo "$SCOPES" | jq -e '.api == "true"' > /dev/null 2>&1; then
cat <<'YAML'
- trigger: "api-pipeline"
label: ":gear: Trigger API pipeline"
build:
branch: "${BUILDKITE_BRANCH}"
commit: "${BUILDKITE_COMMIT}"
YAML
fi
if echo "$SCOPES" | jq -e '.docs == "true"' > /dev/null 2>&1; then
cat <<'YAML'
- trigger: "docs-pipeline"
label: ":books: Trigger docs pipeline"
build:
branch: "${BUILDKITE_BRANCH}"
commit: "${BUILDKITE_COMMIT}"
YAML
fi
if [ "$(echo "$SCOPES" | jq '[.[] | select(. == "true")] | length')" = "0" ]; then
cat <<'YAML'
- label: ":fast_forward: No affected scopes"
command: "true"
YAML
fi

Declare a trigger branch for every scope you define. A scope with no matching branch produces a pull request that silently runs no checks at all.

Was this page helpful?