Files
rune/scripts/semver_bump.py
T
Nick c03c953a45
CI Pipeline / best-practices (push) Successful in 9s
CI Pipeline / documentation (push) Successful in 6s
CI Pipeline / canary (push) Successful in 36s
CI Pipeline / type-check (push) Successful in 31s
CI Pipeline / lint (push) Successful in 49s
Auto-version and Release / release (push) Failing after 54s
CI Pipeline / unit-tests (push) Successful in 2m54s
CI Pipeline / gitea-offline (push) Successful in 16s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / CI Pipeline / notify-event-bridge (push) Successful in 8s
deploy / deploy (push) Successful in 5m48s
fix(release): semver_bump honors synthesized convergence title kind (T20 20B REAL DEFECT)
The push-convergence path synthesizes PR titles as '<kind>: converged
release' where kind comes from version_convergence.highest_warranted_bump
(minor|patch). semver_bump then re-derived the kind from the TITLE TEXT
and 'minor:' matched nothing → patch. Every feature tranche downgraded:
T18/T19/T20 feat ranges bumped 0.66.77-83 instead of 0.67.0. Fixed:
MINOR_PREFIX_RE carries the convergence decision through. Held-out
proves minor-title→0.67.0 and patch-title→0.67.1 (no accidental
re-minor). This is the 0.66.x problem the mandate named, root-caused.
2026-09-16 03:32:53 +01:00

59 lines
2.1 KiB
Python

#!/usr/bin/env python3
"""semver_bump.py — compute the next version from a merged PR title.
Extracted from auto-version.yml after the inline python (pasted into a YAML
double-quoted scalar with unescaped quotes and column-0 lines) made the whole
workflow unparseable — silently disabling every post-merge version bump since
#190 (2026-09-06 15:01). Bug-fix contract: logic in a testable script, not a
heredoc nobody can lint.
Conventions (0.x semver until 1.0.0):
- breaking (feat!:/fix!:/BREAKING CHANGE) -> minor bump (major+1 . 0 . 0 only at 1.x)
- feat:/feat(scope): -> minor bump (x . minor+1 . 0)
- anything else -> patch bump
"""
from __future__ import annotations
import re
import sys
# T20 §20B: push-convergence synthesizes PR titles as "<kind>: converged
# release (push-to-master bump)" where kind comes from
# version_convergence.highest_warranted_bump. The title text must carry
# that decision — "minor:" was re-classified as patch here, downgrading
# every feature tranche (observed: feat ranges bumped 0.66.77-83 not 0.67.0).
MINOR_PREFIX_RE = re.compile(r"^\s*minor\b", re.IGNORECASE)
BREAKING_RE = re.compile(r"^\s*\w+(\([^)]*\))?!:")
FEAT_RE = re.compile(r"^\s*feat(\([^)]*\))?")
def next_version(version: str, title: str) -> str:
if BREAKING_RE.match(title) or "BREAKING CHANGE" in title:
kind = "major"
elif MINOR_PREFIX_RE.match(title):
kind = "minor" # synthesized convergence title carries the kind
elif FEAT_RE.match(title):
kind = "minor"
else:
kind = "patch"
major, minor, patch = (int(x) for x in version.split("."))
if kind == "major":
return f"{major}.{minor + 1}.0" if major == 0 else f"{major + 1}.0.0"
if kind == "minor":
return f"{major}.{minor + 1}.0"
return f"{major}.{minor}.{patch + 1}"
def main_with(argv: list[str]) -> int:
if len(argv) != 3:
print("usage: semver_bump.py <current-version> <pr-title>", file=sys.stderr)
return 2
print(next_version(argv[1], argv[2]))
return 0
if __name__ == "__main__":
sys.exit(main_with(sys.argv))