From 9b1d6f47f1e22592c82bb75177e1fccfa37cae07 Mon Sep 17 00:00:00 2001 From: bircni Date: Wed, 7 Oct 2026 03:08:08 +0200 Subject: [PATCH] ci: reject breaking marker on non-breaking PR types (#39584) Reject the breaking marker `!` on `build`, `chore`, `ci`, `docs`, `style` and `test` PR titles, since these types never change user-facing behavior. The check runs in `lint-pr-title`, so it fails before labels are synced and such PRs never get `pr/breaking`. Co-authored-by: silverwind --- CONTRIBUTING.md | 2 +- tools/ci-tools.ts | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6dd8fc06859..fef7ded677d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -175,7 +175,7 @@ PR titles must follow the [Conventional Commits](https://www.conventionalcommits type(scope)!: subject ``` -The scope in parentheses is optional. A `!` immediately before the colon marks a [breaking change](https://www.conventionalcommits.org/en/v1.0.0/#summary): either `type!:` or `type(scope)!:` (not `type!(scope):`). +The scope in parentheses is optional. A `!` immediately before the colon marks a [breaking change](https://www.conventionalcommits.org/en/v1.0.0/#summary): either `type!:` or `type(scope)!:` (not `type!(scope):`). Do not mark `build`, `chore`, `ci`, `docs`, `style`, or `test` as breaking. Use one of these types: diff --git a/tools/ci-tools.ts b/tools/ci-tools.ts index 6927dddd3cd..5752ded4641 100644 --- a/tools/ci-tools.ts +++ b/tools/ci-tools.ts @@ -18,6 +18,7 @@ const allowedTypes = [ type CommitType = typeof allowedTypes[number]; const allowedTypesList = allowedTypes.join(', '); +const nonBreakingTypes = new Set(['build', 'chore', 'ci', 'docs', 'style', 'test']); const titlePattern = new RegExp(`^(${allowedTypes.join('|')})(\\([\\w/.-]+\\))?(!)?: .+$`); function parsePrTitle(title: string): {type: CommitType, scope: string, breaking: boolean} | null { @@ -86,12 +87,17 @@ function lintPrTitle(): void { console.error('Missing PR_TITLE'); exit(1); } - if (!parsePrTitle(env.PR_TITLE)) { + const parsed = parsePrTitle(env.PR_TITLE); + if (!parsed) { console.error(`Invalid PR title: ${env.PR_TITLE}`); console.error('Expected format: type(scope): subject (scope optional, append "!" for breaking changes)'); console.error(`Allowed types: ${allowedTypesList}`); exit(1); } + if (parsed.breaking && nonBreakingTypes.has(parsed.type)) { + console.error(`Type "${parsed.type}" cannot be marked as breaking (remove "!" from the title)`); + exit(1); + } } // Command: sync the title-derived labels onto the PR via the GitHub API.