From 9e4f088caaaf25543ad58905a09b675e76c5bae3 Mon Sep 17 00:00:00 2001 From: Dusten Hubbard Date: Tue, 7 Jul 2026 01:00:05 -0500 Subject: [PATCH] feat(release): support X.Y.Z-beta-N pre-release tags --- PyReconstruct/modules/gui/main/first_launch.py | 11 ++++++++++- scripts/prune_prereleases.py | 5 +++-- tests/test_first_launch.py | 10 ++++++++++ tests/test_prune_prereleases.py | 6 +++++- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/PyReconstruct/modules/gui/main/first_launch.py b/PyReconstruct/modules/gui/main/first_launch.py index cccc6da0..61004f98 100644 --- a/PyReconstruct/modules/gui/main/first_launch.py +++ b/PyReconstruct/modules/gui/main/first_launch.py @@ -194,8 +194,17 @@ def parse_changelog_section(text, version): target = _normalize_version(version) if not text or not target: return None + # Match by parsed PEP 440 version so any equivalent spelling of the header + # lines up with the runtime version -- e.g. a [1.21.0-beta-1] header (which + # setuptools-scm bakes into the app as 1.21.0b1) matches a 1.21.0b1 runtime; + # fall back to a normalized string compare for non-version headers + # ([Unreleased]). Mirrors whats_new_content's matching. + target_v = _safe_version(target) for section in parse_all_sections(text): - if _normalize_version(section["version"]).lower() == target.lower(): + sv = section["version"] + if target_v is not None and _safe_version(sv) == target_v: + return section["body"] or None + if _normalize_version(sv).lower() == target.lower(): return section["body"] or None return None diff --git a/scripts/prune_prereleases.py b/scripts/prune_prereleases.py index 4eee8ed0..b33e71ac 100644 --- a/scripts/prune_prereleases.py +++ b/scripts/prune_prereleases.py @@ -27,8 +27,9 @@ # Pre-release suffixes appended to the exact X.Y.Z: # PEP 440 compact: a / b / rc -# dashed semver: -alpha[.N] / -beta[.N] / -rc[.N] -PRERELEASE_SUFFIX = r"(?:(?:a|b|rc)\d+|-(?:alpha|beta|rc)(?:\.\d+)?)" +# dashed semver: -alpha / -beta / -rc, optionally followed by a number with +# either a '.' or '-' separator (-beta.2 or -beta-2). +PRERELEASE_SUFFIX = r"(?:(?:a|b|rc)\d+|-(?:alpha|beta|rc)(?:[-.]\d+)?)" def select_superseded(stable_tag: str, tags: list[str]) -> list[str]: diff --git a/tests/test_first_launch.py b/tests/test_first_launch.py index a5c1c1fc..c0d4658a 100644 --- a/tests/test_first_launch.py +++ b/tests/test_first_launch.py @@ -117,6 +117,16 @@ def test_parse_changelog_section_missing_returns_none(): assert F.parse_changelog_section(SAMPLE, "") is None +def test_parse_changelog_section_matches_beta_by_version(): + # setuptools-scm bakes a v1.21.0-beta-1 tag into the app as 1.21.0b1, so the + # [1.21.0-beta-1] header must match a 1.21.0b1 runtime by PARSED version, not + # raw string; the reverse spelling matches too. + dashed = "## [1.21.0-beta-1] — 2026-07-07\n\n- Beta bullet.\n" + assert "Beta bullet." in F.parse_changelog_section(dashed, "1.21.0b1") + compact = "## [1.21.0b1] — 2026-07-07\n\n- Beta bullet.\n" + assert "Beta bullet." in F.parse_changelog_section(compact, "1.21.0-beta-1") + + # ---- friendly dates --------------------------------------------------------- def test_friendly_date_formats_iso_without_leading_zero(): assert F.friendly_date("2026-06-29") == "June 29, 2026" diff --git a/tests/test_prune_prereleases.py b/tests/test_prune_prereleases.py index 2cfe0eb6..7475d5fc 100644 --- a/tests/test_prune_prereleases.py +++ b/tests/test_prune_prereleases.py @@ -21,13 +21,16 @@ _spec.loader.exec_module(prune) # Every same-line pre-release form the repo has used or planned: -# PEP 440 compact (rcN / aN / bN) and dashed semver (-rc.N / -alpha.N / -beta.N). +# PEP 440 compact (rcN / aN / bN) and dashed semver (-rc.N / -alpha.N / -beta.N), +# including the hyphen-number spelling -beta-N (the fork's chosen beta scheme). SUPERSEDED = [ "v1.21.0rc1", "v1.21.0rc2", "v1.21.0-rc.1", "v1.21.0-alpha.1", "v1.21.0-beta.2", + "v1.21.0-beta-1", + "v1.21.0-beta-3", "v1.21.0a1", "v1.21.0b2", ] @@ -40,6 +43,7 @@ "v1.20.4", "v1.20.4rc2", "v1.22.0rc1", + "v1.22.0-beta-1", "v1.21.10rc1", ]