1. Resources
  2. /
  3. Plugins
  4. /
  5. mergify-ci-buildkite-plugin

Mergify CI Buildkite Plugin

A Buildkite plugin for integrating with Mergify CI Insights — upload JUnit test reports, detect pull request scopes, and upload scopes to Mergify Merge Queue.

Authentication

Put the Mergify token in MERGIFY_TOKEN in the agent environment. None of the examples below pass it through the plugin’s token config, on purpose.

buildkite-agent pipeline upload interpolates variables at upload time, so token: "${MERGIFY_TOKEN}" writes the plaintext secret into the stored pipeline, where the Buildkite UI renders it and build history keeps it. The agent warns about it on upload, and Buildkite Agent v4 rejects such uploads by default: v3’s opt-in --reject-secrets becomes an opt-out --allow-secrets.

Escaping does not help. $$MERGIFY_TOKEN survives upload as the literal string $MERGIFY_TOKEN, and the plugin prefers its token config over the environment, so that non-empty string shadows the MERGIFY_TOKEN that would have worked and is sent as the token.

The plugin says both out loud at runtime. Its environment hook warns whenever token is set, on every step carrying one whether or not that step goes on to use it: by then the upload has happened, and a baked-in secret is indistinguishable from a deliberate mock token. The warning never fails a step, because a failed environment hook makes the agent skip the checkout and command phases too.

Where the token is actually used, a value starting with $ is rejected outright. That is never a token, only an interpolation that did not happen, and it is rejected whether it arrives through token or through MERGIFY_TOKEN.

Supply MERGIFY_TOKEN the way you supply any other Buildkite secret, so it only ever exists in the job’s environment:

  • an agent environment hook that exports MERGIFY_TOKEN
  • a secrets plugin that exports MERGIFY_TOKEN into the job from its own environment hook, which runs before this plugin resolves the token

A pipeline-level env: block is not one of these: it is stored with the pipeline just like token is.

On a self-hosted Mergify, export MERGIFY_API_URL next to MERGIFY_TOKEN the same way. The plugin uses it when mergify_api_url is not set, so the token goes to your instance and not to https://api.mergify.com.

Unsetting MERGIFY_TOKEN does not necessarily stop a build from authenticating, because mergify-cli falls back to GITHUB_TOKEN and then to gh auth token. With none of those available, scopes still detects scopes and writes the meta-data and skips only the API upload with a warning, scopes-upload warns and skips, and junit-process fails the step.

Plugin version

The examples pin mergifyio/mergify-ci#v8. Newer tags are on the releases page; the pins here are bumped once a tag exists, so they can trail the newest release by a day.

Coming from #v2 or older, drop python_version from the plugin config before you move. v5 removed that property and the schema sets additionalProperties: false, so a step still passing it fails validation. Pin the CLI with mergify_cli_version instead. v5 also replaced the uv install with a prebuilt binary, so the agent needs tar and sha256sum/shasum rather than Python, see Requirements.

Actions

junit-process

Process JUnit XML test reports and upload them to Mergify CI Insights. Detects silent test failures automatically using the step’s exit code.

steps:
  - label: "Run tests"
    command: pytest --junitxml=reports/junit.xml
    plugins:
      - mergifyio/mergify-ci#v8:
          action: junit-process
          report_path: "reports/*.xml"

scopes

Detect which code scopes are affected by a pull request and upload them to the Mergify API. A Buildkite annotation is created to display the detected scopes directly on the build page.

steps:
  - label: "Detect scopes"
    plugins:
      - mergifyio/mergify-ci#v8:
          action: scopes

scopes-git-refs

Return the merge-queue-aware base and head SHAs. Results are stored as Buildkite meta-data (mergify-ci.base, mergify-ci.head) for use by subsequent steps.

steps:
  - label: "Get git refs"
    key: git-refs
    plugins:
      - mergifyio/mergify-ci#v8:
          action: scopes-git-refs

scopes-upload

Upload scopes to the Mergify API. Requires a prior scopes-git-refs step for base/head refs. Scopes can be provided as a comma-separated list in plugin config, or read automatically from the mergify-ci.scopes meta-data set by a prior scopes step (both JSON and CSV formats are supported).

# Option 1: explicit scopes via plugin config
steps:
  - label: "Get git refs"
    key: git-refs
    plugins:
      - mergifyio/mergify-ci#v8:
          action: scopes-git-refs

  - label: "Upload scopes"
    depends_on: git-refs
    plugins:
      - mergifyio/mergify-ci#v8:
          action: scopes-upload
          scopes: "backend,frontend"
# Option 2: scopes from meta-data (set by a prior step)
steps:
  - label: "Generate scopes"
    key: generate-scopes
    command: |
      buildkite-agent meta-data set "mergify-ci.scopes" "backend,frontend"

  - label: "Upload scopes"
    depends_on: generate-scopes
    plugins:
      - mergifyio/mergify-ci#v8:
          action: scopes-upload

Using scopes to conditionally run steps

Detect scopes first, then use the meta-data to skip steps unaffected by the change:

steps:
  - label: "Detect scopes"
    key: scopes
    plugins:
      - mergifyio/mergify-ci#v8:
          action: scopes

  - label: "Backend tests"
    depends_on: scopes
    command: pytest tests/backend/
    if: build.env("BUILDKITE_TRIGGERED_FROM_BUILD_PIPELINE_SLUG") != null || build.pull_request.id != null
    plugins:
      - mergifyio/mergify-ci#v8:
          action: junit-process
          report_path: "reports/*.xml"
    # Use a dynamic pipeline or script to check scopes:
    # SCOPES=$(buildkite-agent meta-data get "mergify-ci.scopes")
    # echo "$SCOPES" | jq -e '.backend == "true"'

  - label: "Frontend tests"
    depends_on: scopes
    command: npm test
    plugins:
      - mergifyio/mergify-ci#v8:
          action: junit-process
          report_path: "reports/*.xml"
    # SCOPES=$(buildkite-agent meta-data get "mergify-ci.scopes")
    # echo "$SCOPES" | jq -e '.frontend == "true"'

For full conditional control, use a dynamic pipeline that reads the scopes meta-data and only uploads the relevant steps:

#!/bin/bash
# .buildkite/dynamic-pipeline.sh
SCOPES=$(buildkite-agent meta-data get "mergify-ci.scopes")

if echo "$SCOPES" | jq -e '.backend == "true"' > /dev/null 2>&1; then
  cat <<'YAML'
  - label: "Backend tests"
    command: pytest tests/backend/
    plugins:
      - mergifyio/mergify-ci#v8:
          action: junit-process
          report_path: "reports/*.xml"
YAML
fi

if echo "$SCOPES" | jq -e '.frontend == "true"' > /dev/null 2>&1; then
  cat <<'YAML'
  - label: "Frontend tests"
    command: npm test
    plugins:
      - mergifyio/mergify-ci#v8:
          action: junit-process
          report_path: "reports/*.xml"
YAML
fi
# pipeline.yml
steps:
  - label: "Detect scopes"
    key: scopes
    plugins:
      - mergifyio/mergify-ci#v8:
          action: scopes

  - label: "Upload pipeline"
    depends_on: scopes
    command: .buildkite/dynamic-pipeline.sh | buildkite-agent pipeline upload

Configuration

Authentication is not one of these properties: the token comes from MERGIFY_TOKEN in the agent environment, see Authentication.

PropertyRequiredDefaultDescription
actionyes—junit-process, scopes, scopes-git-refs, or scopes-upload
tokenno—Mergify token taken from plugin config. Plugin config is stored with the pipeline, so only set this to a value that is not a secret, such as a mock token. Setting it warns on every build, and a value starting with $ is rejected. See Authentication
report_pathfor junit-process—Glob path to JUnit XML files
scopesno—Comma-separated list of scopes. If not set, scopes-upload reads from mergify-ci.scopes meta-data
mergify_api_urlnoMERGIFY_API_URL from the environment, else https://api.mergify.comMergify API endpoint
job_namenoStep labelOverride job name (useful for matrix builds)
mergify_config_pathno—Path to .mergify.yml configuration file
mergify_cli_versionnopinned defaultVersion of mergify-cli to install. Leave unset to use the plugin’s pinned default (kept current by Renovate, see hooks/environment), pin an exact release (e.g. 2026.6.16.1, must be >= 2026.6.15.1), or use latest to always install the newest released version.

Meta-data

The plugin stores the following values via buildkite-agent meta-data:

KeySet byDescription
mergify-ci.basescopes, scopes-git-refsMerge-queue-aware base SHA
mergify-ci.headscopes, scopes-git-refsMerge-queue-aware head SHA
mergify-ci.sourcescopes, scopes-git-refsReference source (e.g. buildkite_pull_request, merge_queue)
mergify-ci.scopesscopesJSON mapping of scope names to “true”/“false”

Annotations

The mergify-cli writes Buildkite annotations (context: mergify-ci-scopes) showing the detected scopes as a table. Annotations are written at both build scope (visible on the build page) and job scope (visible on the job card).

Requirements

mergify-cli is installed as a prebuilt binary (no Python required), downloaded and checksum-verified by the upstream install.sh. Supported platforms: Linux (x86_64, aarch64) and macOS (x86_64, aarch64).

  • curl (to download the installer and the binary)
  • tar (to extract the binary)
  • sha256sum or shasum (to verify the binary checksum)
  • jq (for JSON processing in scopes-upload)

Development

Running tests

The suite drives the plugin for real, so it needs the same jq listed under Requirements. Without it the scopes-upload tests fail with exit 127.

bats tests/

CI runs them in GitHub Actions (.github/workflows/ci.yaml), which reports the ci-gate check that merges are gated on.

License

Apache 2.0 — see LICENSE.

The plugins listed on this webpage are provided for informational purposes only. They have not undergone any formal security review or assessment. While we strive to provide useful resources, we cannot guarantee the safety, reliability, or integrity of these plugins. Users are strongly advised to conduct their own security evaluations before downloading, installing, or using any plugin. By using these plugins, you acknowledge and accept any risks associated with their use. We disclaim any liability for any harm or damages arising from the use of the plugins listed.

You deserve better CI.

Get started for free with a full month of our all-access trial.

Buildkite Pipelines

Platform

  1. Pipelines
  2. Public pipelines
  3. Test Engine
  4. Package Registries
  5. Mobile Delivery Cloud
  6. Pricing

Hosting options

  1. Self-hosted agents
  2. Mac hosted agents
  3. Linux hosted agents

Resources

  1. Docs
  2. Blog
  3. Changelog
  4. Example pipelines
  5. Plugins
  6. Webinars
  7. Case studies
  8. Events
  9. Migration Services

Company

  1. About
  2. Careers
  3. Press
  4. Security
  5. Brand assets
  6. Contact

Solutions

  1. Replace Jenkins
  2. Workflows for MLOps
  3. Testing at scale
  4. Monorepo mojo
  5. Bazel orchestration

Compare

  1. Buildkite vs GitHub Actions
  2. Buildkite vs Jenkins
  3. Buildkite vs CircleCI
  4. Buildkite vs Harness
  5. Buildkite vs CloudBees
  6. Buildkite vs Blacksmith
  7. Buildkite vs Depot

Legal

  1. Terms of Service
  2. Acceptable Use Policy
  3. Privacy Policy
  4. Subprocessors
  5. Service Level Agreement
  6. Supplier Code of Conduct
  7. Modern Slavery Statement

Support

  1. System status
  2. Forum
© Buildkite Pty Ltd 2026