From ba221c5c2fd2004766fc3ce0b05b8c487d12dd6e Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Tue, 1 Sep 2026 03:40:03 -0700 Subject: [PATCH 1/2] version: base master nightlies on next unreleased codename (3009.0~nbN) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Master nightlies were producing versions like ``3008.2+697.g621251a737`` because ``git describe --match "v3008.*"`` (constraint inherited from a 3007.x forward-merge) hijacked the detected version to Argon's line even though master is developing toward Potassium. That mis-labels the code, and once 3008.3 releases, the master nightly RPM/DEB sort *below* it — so consumers of a nightly mirror wouldn't auto-move to a real stable 3008.3 fix. Fix master's ``salt/version.py`` in two related places: 1. Swap the ``--match`` constraint from ``v3008.*`` to ``v3009.*``. No ``v3009.*`` tag exists yet, so describe falls through to just the raw SHA on this branch. 2. Extend the existing SHA-only handler to lift the baseline to ``SaltVersionsInfo.next_release()`` (Potassium/3009 on master) using ``git rev-list --count v3008.0..HEAD`` for the dev-cycle commit count. Emits a ``pre_type="nb"`` (nightly build) version like ``3009.0nb1292+1292.g621251a737``. PEP 440 sort: 3008.2 < 3008.99 < 3009.0.dev* < 3009.0nb1 < 3009.0nb1292 < 3009.0a1 < 3009.0rc1 < 3009.0 Also guard the module-level ``SaltVersionsInfo._current_release`` override at file bottom against pre-release versions — a pre_release codename reflects the *next* codename, not the last released one, and would corrupt ``SaltVersionsInfo.current_release()`` for callers that expect "last released codename". In ``tools/changelog.py``, add ``_to_distro_version()`` and use it in both ``update_rpm`` (extending the pre-existing ``rc`` -> ``~rc`` translation to also cover ``a``/``b``/``nb``) and ``update_deb`` (which previously had no translation at all). rpmvercmp and dpkg --compare both treat an extra alphanumeric segment as *greater* than nothing (``3009.0nb1292`` > ``3009.0``); the ``~`` form sorts *less than nothing* (``3009.0~nb1292`` < ``3009.0``) — required so nightlies sort below the eventual final release. Verified with rpm.labelCompare and dpkg --compare-versions: 3008.2 < 3009.0~nb1292 3009.0~nb1292 < 3009.0~nb1293 3009.0~nb1292 < 3009.0~rc1 3009.0~rc1 < 3009.0 3008.99 < 3009.0~nb1 3009.0~nb1292 < 3009.0 Maintenance branches (3006.x, 3007.x, 3008.x) are unaffected: they keep their own hardcoded ``--match v.*`` (rebased at branch cut). --- salt/version.py | 65 +++++++++++++++++++++++++++++++++++++++------- tools/changelog.py | 43 ++++++++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 11 deletions(-) diff --git a/salt/version.py b/salt/version.py index 6f875f39bbb..9fddda884da 100644 --- a/salt/version.py +++ b/salt/version.py @@ -639,19 +639,23 @@ def __discover_version(saltstack_version): # Let's not import `salt.utils` for the above check kwargs["close_fds"] = True + # Constrain describe to the next-unreleased codename's major + # (Potassium / 3009 on master). No ``v3009.*`` tag exists yet so + # describe falls through to just the raw SHA, which the SHA-only + # branch below lifts to a ``3009.0nb+.g`` version so + # master nightlies sort above every 3008.x release and below any + # future 3009 pre-release. Maintenance branches (e.g. 3008.x) keep + # their own hardcoded ``v.*`` constraint from their branch's + # version.py (rebased on branch-cut), which is why this value is + # tied to the *next* codename here. process = subprocess.Popen( [ "git", "describe", "--tags", "--long", - # Constrain to the branch's own major (3008.x) so tags - # from other majors reachable in the git graph do not hijack - # the detected version. Merged forward from 3007.x's - # v3007.* constraint (see git log for f3ffc8f9c9ea) and - # rebased to this branch's major. "--match", - "v3008.*", + "v3009.*", "--always", "--candidates=150", ], @@ -667,7 +671,46 @@ def __discover_version(saltstack_version): return saltstack_version if SaltStackVersion.git_sha_regex.match(out): - # We only define the parsed SHA and set NOC as ??? (unknown) + # Describe fell through to just the raw SHA — no ``v3009.*`` + # tag was reachable. This is the normal state on ``master`` + # while the next codename is still unreleased. Lift the + # baseline to the next unreleased codename and count commits + # since the previous major's first tag, so the emitted version + # sorts above every released version of the previous major and + # below any pre-release/final of the next. + next_rel = SaltVersionsInfo.next_release() + cur_rel = SaltVersionsInfo.current_release() + if next_rel and cur_rel and next_rel.info[0] != cur_rel.info[0]: + anchor = f"v{cur_rel.info[0]}.0" + try: + rev_list = subprocess.check_output( + ["git", "rev-list", "--count", f"{anchor}..HEAD"], + cwd=cwd, + stderr=subprocess.DEVNULL, + ) + count = int(rev_list.decode().strip()) + except (subprocess.CalledProcessError, ValueError): + count = -1 + sha = out.strip() + if not sha.startswith("g"): + sha = f"g{sha}" + # ``nb`` = nightly build. Uses the existing pre_type slot on + # SaltStackVersion; the ``__str__`` formatter renders it as + # ``.nb+.``. The RPM/DEB + # changelog helpers rewrite ``nb`` to ``~nb`` so distro + # version comparison places nightly builds below the + # unadorned final release (see tools/changelog.py). + return SaltStackVersion( + next_rel.info[0], + 0, + 0, + pre_type="nb", + pre_num=count if count >= 0 else 0, + noc=count, + sha=sha, + ) + # No unreleased codename available (or same as current) — fall + # back to the historical behaviour of just recording the SHA. saltstack_version.sha = out.strip() saltstack_version.noc = -1 return saltstack_version @@ -730,8 +773,12 @@ def __get_version(saltstack_version): # Get additional version information if available __saltstack_version__ = __get_version(__saltstack_version__) -if __saltstack_version__.name: - # Set SaltVersionsInfo._current_release to avoid lookups when finding previous and next releases +if __saltstack_version__.name and not __saltstack_version__.pre_type: + # Populate SaltVersionsInfo._current_release only for released / stable + # versions. A pre-release (e.g. ``3009.0nb1292`` on master) reflects the + # tree's *next* codename, not the last released one — treating it as + # ``current`` would corrupt SaltVersionsInfo.current_release() for + # downstream callers that rely on it meaning "last released codename". SaltVersionsInfo._current_release = getattr( SaltVersionsInfo, __saltstack_version__.name.upper() ) diff --git a/tools/changelog.py b/tools/changelog.py index f99eeb19d2b..37860117662 100644 --- a/tools/changelog.py +++ b/tools/changelog.py @@ -9,6 +9,7 @@ import logging import os import pathlib +import re import sys import textwrap @@ -39,6 +40,35 @@ ) +# PEP 440 pre-release markers (a, b, rc) plus Salt's ``nb`` (nightly +# build). We anchor on ```` on both sides so ``rc1`` matches but +# random letters embedded in numbers don't. rpm/deb version comparison +# treats extra alphanumeric segments as *greater* than nothing +# (``3009.0nb1292`` > ``3009.0``), while any segment starting with ``~`` +# is *less than nothing* (``3009.0~nb1292`` < ``3009.0``) — which is what +# we want for pre-releases. +_DISTRO_PRERELEASE_RE = re.compile(r"(?<=\d)(a|b|rc|nb)(?=\d)") + + +def _to_distro_version(pep440_version: str) -> str: + """ + Rewrite PEP 440 pre-release markers in ``pep440_version`` to the ``~`` + form used by rpmvercmp / dpkg-vercmp so pre-releases sort below the + final release. + + Only the public-version segment (before ``+``) is rewritten — the + local-version identifier can contain hex SHAs whose letters would + false-match (e.g. ``621251a737`` looks like ``1a7`` = digit-a-digit). + + Examples: + ``3009.0rc1`` -> ``3009.0~rc1`` + ``3009.0nb1292+1292.g…`` -> ``3009.0~nb1292+1292.g…`` + ``3008.2`` -> ``3008.2`` (unchanged) + """ + public, sep, local = pep440_version.partition("+") + return _DISTRO_PRERELEASE_RE.sub(r"~\1", public) + sep + local + + def _get_changelog_contents(ctx: Context, version: Version): """ Return the full changelog generated by towncrier. @@ -108,7 +138,12 @@ def update_rpm(ctx: Context, salt_version: Version, draft: bool = False): rpm_release = str(salt_version.post) str_salt_version = f"{rpm_version}-{rpm_release}" else: - rpm_version = str(salt_version).replace("rc", "~rc") + # Rewrite PEP 440 pre-release markers to the ``~`` distro form so + # rpmvercmp sorts pre-releases *below* the unadorned final version. + # rpmvercmp treats extra alphanumeric segments as *greater* (so + # ``3009.0nb1292`` > ``3009.0``), while any segment starting with + # ``~`` sorts *less than nothing* (``3009.0~nb1292`` < ``3009.0``). + rpm_version = _to_distro_version(str(salt_version)) rpm_release = "0" str_salt_version = rpm_version @@ -172,7 +207,11 @@ def update_deb(ctx: Context, salt_version: Version, draft: bool = False): debian_changelog_path = "pkg/debian/changelog" tmp_debian_changelog_path = f"{debian_changelog_path}.1" with open(tmp_debian_changelog_path, "w", encoding="utf-8") as wfp: - wfp.write(f"salt ({salt_version}) stable; urgency=medium\n\n") + # See _to_distro_version() for rationale — dpkg-vercmp has the same + # "extra segment sorts higher" quirk as rpmvercmp, so pre-release + # markers need to be rewritten to the ``~`` form here too. + deb_version = _to_distro_version(str(salt_version)) + wfp.write(f"salt ({deb_version}) stable; urgency=medium\n\n") wfp.write(formated) wfp.write( f"\n -- Salt Project Packaging {date}\n\n" From 6773f882d01e054bf54fff6573b97684fb54d036 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Tue, 1 Sep 2026 03:40:51 -0700 Subject: [PATCH 2/2] changelog: add fragment for master nightly version fix --- changelog/70200.fixed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/70200.fixed.md diff --git a/changelog/70200.fixed.md b/changelog/70200.fixed.md new file mode 100644 index 00000000000..457b1ae5294 --- /dev/null +++ b/changelog/70200.fixed.md @@ -0,0 +1 @@ +Fixed master nightly build version to reflect the next unreleased codename (Potassium/3009) instead of the previous released major (Argon/3008). Master nightlies now emit `3009.0nb+.g` (rendered as `3009.0~nb+...` in RPM/DEB names) so they sort above every 3008.x release and below any future 3009 pre-release.