Skip to content

Commit 629fbae

Browse files
os-zhuangclaude
andauthored
ci: exempt Changesets pre-release (RC) mode from the no-major guard (#3342)
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-<tag>.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: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 7607a7a commit 629fbae

1 file changed

Lines changed: 31 additions & 2 deletions

File tree

scripts/check-changeset-no-major.mjs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,17 @@
2222
* Exits with code 1 (and a clear list of offenders) if any changeset frontmatter
2323
* bumps a package `major`.
2424
*
25-
* ESCAPE HATCH: when a major release is genuinely intended, gate this check off
26-
* in CI with the `allow-major` PR label (see `.github/workflows/pr-automation.yml`).
25+
* RC EXEMPTION: when Changesets is in pre-release mode (`.changeset/pre.json`
26+
* with `"mode": "pre"`, entered via `changeset pre enter <tag>`), a `major`
27+
* bump only ever produces a `X.0.0-<tag>.N` PRE-RELEASE version — nothing final
28+
* publishes until `changeset pre exit`. Accumulating the next major's breaking
29+
* changes is precisely what an RC window is FOR, so this guard stands aside for
30+
* the duration and re-arms automatically once pre-mode is exited. The pending
31+
* majors are still printed (informationally) so the RC curator can eyeball them.
32+
*
33+
* ESCAPE HATCH: outside pre-mode, when a major release is genuinely intended,
34+
* gate this check off in CI with the `allow-major` PR label (see
35+
* `.github/workflows/pr-automation.yml`).
2736
*
2837
* The script intentionally has zero third-party dependencies so it can run in
2938
* minimal CI environments before `pnpm install`.
@@ -82,6 +91,26 @@ if (offenders.length === 0) {
8291
process.exit(0);
8392
}
8493

94+
// RC exemption: in Changesets pre-release mode a `major` only yields a
95+
// `X.0.0-<tag>.N` pre-release — the intended product of an RC window — and
96+
// nothing final ships until `changeset pre exit`. Surface the pending majors
97+
// for the RC curator, but do not fail. The guard re-arms once pre-mode exits.
98+
try {
99+
const pre = JSON.parse(readFileSync(join(changesetDir, 'pre.json'), 'utf8'));
100+
if (pre?.mode === 'pre') {
101+
console.log(
102+
`✓ Changesets is in pre-release mode (tag: ${pre.tag ?? 'unknown'}) — ` +
103+
'`major` bumps are the expected product of an RC window; skipping the no-major guard.',
104+
);
105+
for (const { file, majors } of offenders) {
106+
console.log(`::notice file=${file}::pending major in ${file}: ${majors.join(', ')}`);
107+
}
108+
process.exit(0);
109+
}
110+
} catch {
111+
// No readable `.changeset/pre.json` → not in pre-mode → fall through to the guard.
112+
}
113+
85114
console.error('⛔ Changeset(s) declare a `major` bump.\n');
86115
for (const { file, majors } of offenders) {
87116
console.error(` ${file}`);

0 commit comments

Comments
 (0)