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. 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"