Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 54 additions & 16 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ on:
#
# - Under the two-tier matrices below, pull requests run the light tier and
# the queue re-runs the full tier on the speculative merge commit, so a
# queued PR's checks taking longer than its own PR run is expected.
# queued PR's checks taking longer than its own PR run is expected. The
# exception is a docs-only merge group, which skips the heavy jobs (see
# the `changes` job).
merge_group:

jobs:
Expand All @@ -49,11 +51,22 @@ jobs:
uses: ./.github/workflows/ruff.yml
secrets: inherit

# Detect a docs-only pull request so the heavy jobs below can skip themselves
# while `Unit Tests Complete` still reports. Only the pull_request event is
# inspected: on merge_group and workflow_dispatch the step is skipped, the
# output stays empty, and every `!= 'true'` guard runs the full suite -- the
# queue always tests the complete matrix on the speculative merge commit.
# Detect a docs-only change so the heavy jobs below can skip themselves while
# `Unit Tests Complete` still reports.
#
# - pull_request: the files of the PR.
# - merge_group: the diff between the queue's base and the speculative merge
# commit. That commit carries every entry queued ahead of this one, so the
# suite is skipped only when the whole union is docs-only. The code in that
# commit is then identical to the base, which already landed through this
# queue, so there is nothing new for the suite to test.
# - workflow_dispatch: the step is skipped, the output stays empty, and every
# `!= 'true'` guard runs the full suite.
#
# The parser docs under docs/content/supported_tools/parsers/ do NOT count as
# docs-only: unittests/test_parsers.py checks that every parser has a doc page
# and every doc page has a parser, so a rename or delete there can break the
# suite.
changes:
# Release PRs and merge-backs are merged without waiting for CI. Skip when the
# head is release/merge-* AND the PR has release-management.
Expand All @@ -62,26 +75,50 @@ jobs:
outputs:
docs_only: ${{ steps.filter.outputs.docs_only }}
steps:
- name: Detect a docs-only pull request
- name: Detect a docs-only change
id: filter
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request' || github.event_name == 'merge_group'
env:
GH_TOKEN: ${{ github.token }}
EVENT_NAME: ${{ github.event_name }}
PR_NUMBER: ${{ github.event.pull_request.number }}
MG_BASE_SHA: ${{ github.event.merge_group.base_sha }}
MG_HEAD_SHA: ${{ github.event.merge_group.head_sha }}
# Fail open: if the file listing errors, docs_only stays false and the
# full suite runs. This step must never fail the job -- a red `changes`
# job would skip the heavy chain and fail the gate for every PR.
run: |
docs_only=false
if files=$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${{ github.event.pull_request.number }}/files" --paginate --jq '.[].filename'); then
if [ -n "${files}" ] && ! echo "${files}" | grep -qv '^docs/'; then
docs_only=true
files=""
listed=false
if [ "${EVENT_NAME}" = "pull_request" ]; then
if files=$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/files" --paginate --jq '.[].filename'); then
listed=true
fi
else
# git rather than the compare API: compare truncates its file list
# at 300 entries, git diff never does. Two depth-1 fetches are enough
# for a tree-to-tree diff.
git init -q mg
if git -C mg fetch -q --depth=1 --no-tags \
"https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \
"${MG_BASE_SHA}" "${MG_HEAD_SHA}" \
&& files=$(git -C mg diff --name-only "${MG_BASE_SHA}" "${MG_HEAD_SHA}"); then
listed=true
fi
fi
if [ "${listed}" = "true" ] && [ -n "${files}" ] \
&& ! echo "${files}" | grep -qv '^docs/' \
&& ! echo "${files}" | grep -q '^docs/content/supported_tools/parsers/'; then
docs_only=true
fi
echo "changed files:"
echo "${files}"
echo "docs_only=${docs_only}" >> "${GITHUB_OUTPUT}"

build-docker-containers:
needs: [ ruff, changes ]
# Docs-only PRs skip the whole heavy chain (the test-* jobs skip with this
# Docs-only PRs and merge groups skip the whole heavy chain (the test-* jobs skip with this
# job via their needs). The gate below only accepts a skipped dependency
# when docs_only is true, so this can never silently under-test code.
if: needs.changes.outputs.docs_only != 'true'
Expand All @@ -96,7 +133,7 @@ jobs:
#
# Tiering shrinks MATRICES, it does not skip jobs: the
# unit-tests-complete gate treats a skipped dependency as a failure
# (except on a docs-only PR, the one skip it explicitly allows).
# (except on a docs-only change, the one skip it explicitly allows).
# Same jobs, fewer cells.
platform: ${{ github.event_name == 'pull_request' && fromJSON('["linux/amd64"]') || fromJSON('["linux/amd64", "linux/arm64"]') }}
fail-fast: false
Expand Down Expand Up @@ -175,9 +212,10 @@ jobs:
steps:
- name: Verify every job succeeded
# A skipped or cancelled dependency is not a pass, with one exception:
# on a docs-only pull request the heavy jobs skip by design, and this
# gate must still report success or the PR can never enter the merge
# queue (a required check that never reports blocks queue entry).
# on a docs-only pull request or merge group the heavy jobs skip by
# design, and this gate must still report success or the PR can never
# enter (or leave) the merge queue (a required check that never reports
# blocks it).
# Checking explicitly rather than relying on this job's own conclusion,
# because a job whose needs were skipped can otherwise report success
# by omission.
Expand Down
Loading