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.
Prerequisites
Section titled Prerequisites-
Buildkite is properly set up and is building your projects. See the Buildkite Documentation for help with setting up Buildkite.
-
Buildkite is configured to report build statuses to GitHub.
-
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
Section titled Monorepo CIMonorepo 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.
Declare your scopes
Section titled Declare your scopesThe 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/**/*Pipeline outline
Section titled Pipeline outlineA Buildkite pipeline driven by scopes has two parts:
-
Detect scopes using the
mergifyio/mergify-ciplugin. -
Use a dynamic pipeline to conditionally upload only the steps that match the affected scopes.
Static pipeline entry point
Section titled Static pipeline entry pointsteps: - 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 uploadDynamic pipeline script
Section titled Dynamic pipeline scriptThe script below parses the scope results with
jq, so install it on your Buildkite agents
first.
#!/bin/bashset -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}"YAMLfi
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}"YAMLfi
if echo "$SCOPES" | jq -e '.docs == "true"' > /dev/null 2>&1; then cat <<'YAML' - label: ":books: Docs build" command: make docsYAMLfi
# 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"YAMLfiMake the script executable:
chmod +x .buildkite/dynamic-pipeline.shIn this pipeline:
-
The
detect-scopesstep calls themergifyio/mergify-ciplugin with thescopesaction, which inspects the pull request diff and stores a JSON map of scopes ("true"or"false") as Buildkite meta-data under the keymergify-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-processaction to upload test results to CI Insights.
Triggering separate pipelines
Section titled Triggering separate pipelinesIf your monorepo builds live in separate Buildkite pipelines, use trigger steps instead:
#!/bin/bashset -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}"YAMLfi
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}"YAMLfi
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}"YAMLfi
if [ "$(echo "$SCOPES" | jq '[.[] | select(. == "true")] | length')" = "0" ]; then cat <<'YAML' - label: ":fast_forward: No affected scopes" command: "true"YAMLfiDeclare 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?
Thanks for your feedback!