From a4b8650faf5baac00f7602199a22cab93ae1c190 Mon Sep 17 00:00:00 2001 From: Yun Wang Date: Mon, 22 Jun 2026 15:55:38 +0200 Subject: [PATCH] feat: derive release bump only from the title bang marker The release workflow scanned the PR title and body for "BREAKING CHANGE(S)" and forced a major bump on any match. Prose that merely mentioned the phrase, including a sentence stating there was no breaking change, tripped it. This is the same footgun that mis-cut a major in getstream-ruby. Treat the `\!` marker in the conventional-commits title (e.g. `feat\!:`) as the sole breaking-change signal and stop reading the PR body entirely. Drop the now-unused --body / --body-file options and the body plumbing in release.yml. --- .github/workflows/release.yml | 4 ---- scripts/release/bump_version.py | 20 +++++--------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 812319cf..8b6df328 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,13 +64,9 @@ jobs: if: github.event_name == 'pull_request' && steps.already_released.outputs.value != 'true' env: PR_TITLE: ${{ github.event.pull_request.title }} - PR_BODY: ${{ github.event.pull_request.body }} run: | - PR_BODY_FILE=$(mktemp) - printf '%s' "$PR_BODY" > "$PR_BODY_FILE" python3 scripts/release/bump_version.py \ --title "$PR_TITLE" \ - --body-file "$PR_BODY_FILE" \ --output "$GITHUB_OUTPUT" - name: Determine version bump (manual) diff --git a/scripts/release/bump_version.py b/scripts/release/bump_version.py index 269dc3a0..bdc0d40e 100755 --- a/scripts/release/bump_version.py +++ b/scripts/release/bump_version.py @@ -65,12 +65,11 @@ def find_latest_semver_tag() -> str: return "{}.{}.{}".format(*versions[-1]) -def determine_bump_type(title: str, body: str) -> str: +def determine_bump_type(title: str) -> str: + # Breaking changes are signalled only by the `!` marker in the title + # (e.g. `feat!:`). Free-text body/title prose is not trusted: a PR that + # merely mentions "BREAKING CHANGE" must not force a major bump. title = title.strip() - body = body.strip() - breaking_re = re.compile(r"BREAKING[ -]CHANGES?", re.IGNORECASE) - if breaking_re.search(title) or breaking_re.search(body): - return "major" match = re.match(r"^([a-zA-Z]+)(\([^)]+\))?(!)?:", title) if not match: return "none" @@ -119,12 +118,6 @@ def parse_bool(value: str) -> bool: return value.strip().lower() == "true" -def resolve_body(args: argparse.Namespace) -> str: - if args.body_file: - return Path(args.body_file).read_text(encoding="utf-8") - return args.body or "" - - def write_outputs(output_path: str, entries: dict[str, str]) -> None: if not output_path: for key, value in entries.items(): @@ -138,8 +131,6 @@ def write_outputs(output_path: str, entries: dict[str, str]) -> None: def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--title", default="") - parser.add_argument("--body", default="") - parser.add_argument("--body-file", dest="body_file", default="") parser.add_argument("--output", default="") parser.add_argument("--manual-bump", dest="manual_bump", default="") parser.add_argument( @@ -174,8 +165,7 @@ def main() -> int: ) return 0 - body = resolve_body(args) - bump = determine_bump_type(args.title, body) + bump = determine_bump_type(args.title) if bump == "none": write_outputs( args.output,