From 57514458f5419a37df998459f6860db9cf7080df Mon Sep 17 00:00:00 2001 From: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Date: Mon, 20 Jul 2026 10:29:50 +0800 Subject: [PATCH] ci: exempt Changesets pre-release (RC) mode from the no-major guard check-changeset-no-major.mjs scans every pending changeset (not the PR diff), so a single `major` sitting on main reds the "Check Changeset" job on every subsequent PR. During an RC window that major is intentional: in Changesets pre-release mode a `major` only yields a `X.0.0-.N` pre-release, and nothing final ships until `changeset pre exit`. Detect `.changeset/pre.json` (mode: "pre") and stand the guard aside for the duration, emitting the pending majors as `::notice::` annotations so the RC curator can still eyeball them. The guard re-arms automatically once pre-mode exits, so it keeps protecting the lockstep `fixed` group the rest of the time. Co-Authored-By: Claude Opus 4.8 --- scripts/check-changeset-no-major.mjs | 33 ++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/scripts/check-changeset-no-major.mjs b/scripts/check-changeset-no-major.mjs index fad55f2e60..6eeaa8e070 100644 --- a/scripts/check-changeset-no-major.mjs +++ b/scripts/check-changeset-no-major.mjs @@ -22,8 +22,17 @@ * Exits with code 1 (and a clear list of offenders) if any changeset frontmatter * bumps a package `major`. * - * ESCAPE HATCH: when a major release is genuinely intended, gate this check off - * in CI with the `allow-major` PR label (see `.github/workflows/pr-automation.yml`). + * RC EXEMPTION: when Changesets is in pre-release mode (`.changeset/pre.json` + * with `"mode": "pre"`, entered via `changeset pre enter `), a `major` + * bump only ever produces a `X.0.0-.N` PRE-RELEASE version — nothing final + * publishes until `changeset pre exit`. Accumulating the next major's breaking + * changes is precisely what an RC window is FOR, so this guard stands aside for + * the duration and re-arms automatically once pre-mode is exited. The pending + * majors are still printed (informationally) so the RC curator can eyeball them. + * + * ESCAPE HATCH: outside pre-mode, when a major release is genuinely intended, + * gate this check off in CI with the `allow-major` PR label (see + * `.github/workflows/pr-automation.yml`). * * The script intentionally has zero third-party dependencies so it can run in * minimal CI environments before `pnpm install`. @@ -82,6 +91,26 @@ if (offenders.length === 0) { process.exit(0); } +// RC exemption: in Changesets pre-release mode a `major` only yields a +// `X.0.0-.N` pre-release — the intended product of an RC window — and +// nothing final ships until `changeset pre exit`. Surface the pending majors +// for the RC curator, but do not fail. The guard re-arms once pre-mode exits. +try { + const pre = JSON.parse(readFileSync(join(changesetDir, 'pre.json'), 'utf8')); + if (pre?.mode === 'pre') { + console.log( + `✓ Changesets is in pre-release mode (tag: ${pre.tag ?? 'unknown'}) — ` + + '`major` bumps are the expected product of an RC window; skipping the no-major guard.', + ); + for (const { file, majors } of offenders) { + console.log(`::notice file=${file}::pending major in ${file}: ${majors.join(', ')}`); + } + process.exit(0); + } +} catch { + // No readable `.changeset/pre.json` → not in pre-mode → fall through to the guard. +} + console.error('⛔ Changeset(s) declare a `major` bump.\n'); for (const { file, majors } of offenders) { console.error(` ${file}`);