From c2cef79040e3ae2b21050b1e42ab165e9a1855f4 Mon Sep 17 00:00:00 2001 From: Cody Maffucci <46459665+Maffooch@users.noreply.github.com> Date: Mon, 19 Oct 2026 09:43:23 -0600 Subject: [PATCH] ci: skip the heavy unit-tests suite for docs-only merge groups The docs-only skip only looked at pull_request events, so every merge group ran the full two-platform suite even when nothing but docs/ had changed. The changes job now also diffs the merge group's base against its speculative merge commit and skips the heavy jobs when the whole union is docs-only. Parser doc pages under docs/content/supported_tools/parsers/ no longer count as docs-only on either event, because unittests/test_parsers.py checks them against dojo/tools/. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/unit-tests.yml | 70 ++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index e6dc411d950..97fc7238876 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -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: @@ -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. @@ -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' @@ -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 @@ -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.