From c8e4a859e4206f2489f586aa3a099d09eb09e06e Mon Sep 17 00:00:00 2001 From: Thomas Jung Date: Tue, 1 Sep 2026 15:23:01 -0400 Subject: [PATCH] fix(#2097): scope QA notify workflow to default branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QA notify template (notify-qa.yml, copied into every *-Contribution repo) triggered on push to ANY branch (no branches: filter). A push to a feature branch — e.g. the devtoberfest validation-tutorial bot's validation-tutorial/* branches — dispatched a QA rebuild for a slug that existed only on that branch. QA discovery reads the default branch, so the slug was a phantom and the rebuild hard-failed 'unknown slug in filter'. Bring the QA template to parity with the PROD template (docs/authors/tutorial-repo-dispatch.yml): - add branches: [master, main] (both, since Tutorials-Contribution defaults to master while the rest default to main) - add the strict slug-charset guard on the inferred slug Add a QA-template branch-trigger regression guard mirroring the PROD one. All 23 -Contribution repos currently ship the unfiltered template; propagate via scripts/install-notify-workflows.ts --only qa --execute after merge. --- .github/workflows/notify-qa.yml.template | 19 ++++++++++++++- test/unit/install-notify-workflows.test.ts | 27 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/.github/workflows/notify-qa.yml.template b/.github/workflows/notify-qa.yml.template index 6556984e6..7c05c2063 100644 --- a/.github/workflows/notify-qa.yml.template +++ b/.github/workflows/notify-qa.yml.template @@ -2,6 +2,16 @@ name: Notify tutorials-qa on: push: + # -Contribution repos are a MIXTURE of default branches: most default to + # `main`, but Tutorials-Contribution still defaults to `master`. List BOTH + # so this fires ONLY on the repo's default branch — and NEVER on feature + # branches. Without this filter a push to any branch (e.g. the devtoberfest + # validation-tutorial bot's `validation-tutorial/*` branches) dispatches a + # QA rebuild for a slug that exists only on that branch; QA discovery reads + # the default branch, so the slug is a phantom and the rebuild hard-fails + # "unknown slug in filter" (tutorials-ims#2097). Mirrors the branch filter + # in docs/authors/tutorial-repo-dispatch.yml (the PROD notify template). + branches: [master, main] paths: - 'tutorials/**' @@ -17,8 +27,15 @@ jobs: changed=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} \ | awk -F/ '/^tutorials\//{print $2}' | sort -u) count=$(echo "$changed" | wc -l) + # Only emit a slug when EXACTLY one tutorial changed AND it matches the + # strict slug charset (whole-value POSIX case match); anything else → + # empty slug → rebuild-content-qa.yml runs a full rebuild. Mirrors + # docs/authors/tutorial-repo-dispatch.yml. if [ "$count" = "1" ] && [ -n "$changed" ]; then - echo "slug=$changed" >> "$GITHUB_OUTPUT" + case "$changed" in + *[!a-z0-9-]* | -* ) echo "slug=" >> "$GITHUB_OUTPUT" ;; + *) echo "slug=$changed" >> "$GITHUB_OUTPUT" ;; + esac else echo "slug=" >> "$GITHUB_OUTPUT" fi diff --git a/test/unit/install-notify-workflows.test.ts b/test/unit/install-notify-workflows.test.ts index 84a98228c..7b234aed9 100644 --- a/test/unit/install-notify-workflows.test.ts +++ b/test/unit/install-notify-workflows.test.ts @@ -392,3 +392,30 @@ describe('PROD notify template branch trigger (regression guard)', () => { expect(template).not.toMatch(/branches:\s*\[\s*main\s*\]/) }) }) + +describe('QA notify template branch trigger (regression guard)', () => { + // -Contribution repos are ALSO a mixture of `main`- and `master`-default + // branches (Tutorials-Contribution defaults to `master`). The QA template + // MUST scope the push trigger to the default branch(es) — WITHOUT a branches: + // filter it fires on EVERY branch push (e.g. the devtoberfest validation- + // tutorial bot's `validation-tutorial/*` branches), dispatching a QA rebuild + // for a slug that exists only on that branch; QA discovery reads the default + // branch, so the slug is a phantom and the rebuild hard-fails "unknown slug + // in filter" (tutorials-ims#2097). Keep this in parity with the PROD guard. + const template = readFileSync( + join(__dirname, '..', '..', '.github', 'workflows', 'notify-qa.yml.template'), + 'utf8', + ) + + it('fires on both master and main', () => { + expect(template).toMatch(/branches:\s*\[\s*master\s*,\s*main\s*\]/) + }) + + it('does not ship a main-only trigger', () => { + expect(template).not.toMatch(/branches:\s*\[\s*main\s*\]/) + }) + + it('ships a branches filter (never an unfiltered push trigger)', () => { + expect(template).toMatch(/branches:/) + }) +})