View as Markdown

Scopes with Bazel

Configure merge queue scopes using Bazel's dependency graph for precise batching.


If you’re using monorepo tools like Bazel that have built-in dependency graph analysis, you can use their affected project detection instead of file patterns. This approach is often more accurate because these tools understand your project’s dependency relationships.

To use the manual scopes mechanism, configure Mergify to expect scopes from your CI system:

scopes:
source:
manual:
queue_rules:
- name: default
batch_size: 5

Use bazel query to determine affected projects and upload them to Mergify.

name: Detect Scopes
on:
pull_request:
jobs:
detect-scopes:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
with:
# The queue-aware base commit must exist locally for `git diff`.
fetch-depth: 0
- name: Get git refs
id: refs
uses: Mergifyio/gha-mergify-ci@v25
with:
action: scopes-git-refs
- name: Get scopes
id: scopes
env:
HEAD: ${{ steps.refs.outputs.head }}
BASE: ${{ steps.refs.outputs.base }}
run: |
changed=$(git diff --name-only --diff-filter=ACMR "$BASE" "$HEAD" | tr '\n' ' ')
if [ -z "$changed" ]; then
echo "scopes=" >> "$GITHUB_OUTPUT"
exit 0
fi
set +e
packages=$(bazel query --keep_going --noshow_progress --output=package \
"buildfiles(set($changed))" 2>query.err)
rc=$?
set -e
# --keep_going exits 3 when it skipped files outside any Bazel package
# (README.md, .github/**). Anything else is a real failure and must not
# be swallowed into an empty scope list.
if [ "$rc" -ne 0 ] && [ "$rc" -ne 3 ]; then
cat query.err >&2
exit "$rc"
fi
scopes=$(printf '%s\n' "$packages" | sed '/^$/d' | sort -u | paste -sd, -)
echo "Detected scopes: ${scopes:-<none>}"
echo "scopes=$scopes" >> "$GITHUB_OUTPUT"
- name: Scopes upload
uses: Mergifyio/gha-mergify-ci@v25
with:
action: scopes-upload
token: ${{ secrets.MERGIFY_TOKEN }}
scopes: ${{ steps.scopes.outputs.scopes }}

Using the mergifyio/mergify-ci Buildkite plugin, a first step resolves the merge-queue-aware base and head SHAs and exposes them as meta-data, while a second step computes the affected projects with bazel query and uploads them to Mergify:

steps:
- label: ":mag: Get git refs"
key: git-refs
plugins:
- mergifyio/mergify-ci#v7:
action: scopes-git-refs
- label: ":mag: Detect and upload scopes"
depends_on: git-refs
command: |
BASE=$(buildkite-agent meta-data get "mergify-ci.base")
HEAD=$(buildkite-agent meta-data get "mergify-ci.head")
CHANGED=$(git diff --name-only --diff-filter=ACMR "$BASE" "$HEAD" | tr '\n' ' ')
SCOPES=""
if [ -n "$CHANGED" ]; then
set +e
PACKAGES=$(bazel query --keep_going --noshow_progress --output=package \
"buildfiles(set($CHANGED))" 2>query.err)
RC=$?
set -e
# --keep_going exits 3 when it skipped files outside any Bazel package.
# Anything else is a real failure, not an empty scope list.
if [ "$RC" -ne 0 ] && [ "$RC" -ne 3 ]; then
cat query.err >&2
exit "$RC"
fi
SCOPES=$(printf '%s\n' "$PACKAGES" | sed '/^$/d' | sort -u | paste -sd, -)
fi
buildkite-agent meta-data set "mergify-ci.scopes" "$SCOPES"
plugins:
- mergifyio/mergify-ci#v7:
action: scopes-upload
token: "${MERGIFY_TOKEN}"

Install the Mergify CLI in your pipeline and export MERGIFY_TOKEN. Use mergify ci git-refs to get the merge-queue-aware base and head SHAs and mergify ci scopes-send to upload the detected scopes:

Terminal window
REFS=$(mergify ci git-refs --format json)
BASE=$(printf '%s' "$REFS" | jq -r '.base')
HEAD=$(printf '%s' "$REFS" | jq -r '.head')
CHANGED=$(git diff --name-only --diff-filter=ACMR "$BASE" "$HEAD" | tr '\n' ' ')
if [ -z "$CHANGED" ]; then
echo '{"scopes": []}' > scopes.json
else
# Capture the exit code rather than piping straight into jq: a failed query
# piped into jq still writes a well-formed, empty scopes.json and returns 0.
if PACKAGES=$(bazel query --keep_going --noshow_progress --output=package \
"buildfiles(set($CHANGED))" 2>query.err); then
RC=0
else
RC=$?
fi
# --keep_going exits 3 when it skipped files outside any Bazel package
# (README.md, .github/**). Anything else is a real failure.
if [ "$RC" -ne 0 ] && [ "$RC" -ne 3 ]; then
cat query.err >&2
exit "$RC"
fi
printf '%s\n' "$PACKAGES" | sort -u \
| jq -R -s '{scopes: split("\n") | map(select(length > 0))}' > scopes.json
fi
mergify ci scopes-send --scopes-json scopes.json

Was this page helpful?