Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -365,18 +365,6 @@ download_release_asset() {
return 0
fi

# GitHub normalizes `~` to `.` in release asset names, while checksum files
# can still record package filenames with `~dev` for correct version ordering.
# Download the normalized asset but verify it against the checksum entry for
# the original package filename.
_normalized="$(printf '%s' "$_filename" | tr '~' '.')"
if [ "$_normalized" != "$_filename" ]; then
if download "${GITHUB_URL}/releases/download/${_tag}/${_normalized}" "$_output"; then
info "using GitHub-normalized asset name ${_normalized}"
return 0
fi
fi

return 1
}

Expand Down
12 changes: 6 additions & 6 deletions python/release_tooling_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ def test_exact_tag_versions_are_stable_release_versions() -> None:
def test_dev_versions_share_one_build_identity() -> None:
versions = release._versions_from_parts((0, 0, 37), 108, "152d05940", "v0.0.37")

assert versions.python == "0.0.38.dev108+g152d05940"
assert versions.cargo == "0.0.38-dev.108+g152d05940"
assert versions.docker == "0.0.38-dev.108-g152d05940"
assert versions.deb == "0.0.38~dev.108+g152d05940-1"
assert versions.rpm_version == "0.0.38"
assert versions.rpm_release == "0.dev.108.g152d05940"
assert versions.python == "0.0.37.post108+g152d05940"
assert versions.cargo == "0.0.37+post.108.g152d05940"
assert versions.docker == "0.0.37-post.108.g152d05940"
assert versions.deb == "0.0.37+post.108.g152d05940-1"
assert versions.rpm_version == "0.0.37"
assert versions.rpm_release == "2.dev.108.g152d05940"

@elezar elezar Jun 23, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do the rpm releases not use post? Also: Was the switch from dev to post required?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It maybe deemed it not necessary given

Change RPM release prefix from 0.dev. to 2.dev. so it sorts after stable release 1

I can look into it more.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I explored this with Opus 4.8, and the reasoning seems solid:


Why ~dev → +post

A dev build must sort above the current stable so apt install -y treats it as an upgrade (CI doesn't pass --allow-downgrades). The old ~dev form sorted below its base version (~ sorts before everything), so installing dev on top of stable was rejected as a downgrade. +post sorts above the base, so:

0.0.37-1  <  0.0.37+post.108...-1  <  0.0.38-1
   stable        dev                  next stable

It's also the only form valid across all formats from one derived identity: PEP 440 has no "after X but before next" except .post (its .dev is a pre-release, same downgrade bug), and the Cargo/Docker/Debian strings are all derived from the Python version.

Why RPM doesn't get post

RPM splits version into two fields — Version and Release — and sorts by Version first, then Release. The Release field already does what .post does elsewhere:

Package Version Release
stable 0.0.37 1
dev 0.0.37 2.dev.108...
next stable 0.0.38 1

Bumping Release from 1 to 2.dev. puts dev after stable within the same Version, while 0.0.38 wins on Version. RPM has no .post concept and doesn't need one — the two-field scheme handles ordering natively.



def test_semver_tag_parser_excludes_vm_tags() -> None:
Expand Down
24 changes: 14 additions & 10 deletions tasks/scripts/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,18 @@ def _versions_from_parts(
rpm_version = python_version
rpm_release = "1"
else:
next_version = _format_semver(_next_patch(base_version))
python_version = f"{next_version}.dev{git_distance}+g{git_sha}"
rpm_version = next_version
rpm_release = f"0.dev.{git_distance}.g{git_sha}"

# Convert PEP 440 to a SemVer-ish string for Cargo:
# 0.1.0.dev3+gabcdef -> 0.1.0-dev.3+gabcdef
cargo_version = re.sub(r"\.dev(\d+)", r"-dev.\1", python_version)
latest_version = _format_semver(base_version)
python_version = f"{latest_version}.post{git_distance}+g{git_sha}"
rpm_version = latest_version
rpm_release = f"2.dev.{git_distance}.g{git_sha}"

# Convert PEP 440 to a SemVer-ish string for Cargo. Dev builds are
# post-release package-manager builds, represented as SemVer metadata so
# Cargo accepts the workspace version:
# 0.1.0.post3+gabcdef -> 0.1.0+post.3.gabcdef
cargo_version = re.sub(
r"\.post(\d+)\+g([0-9a-f]+)$", r"+post.\1.g\2", python_version
)

# Docker tags can't contain '+'.
docker_version = cargo_version.replace("+", "-")
Expand All @@ -114,10 +118,10 @@ def _versions_from_parts(
if len(snap_version) > 32:
raise ValueError(f"snap version must be at most 32 characters: {snap_version}")

# Debian versions use '~' so prereleases sort before the eventual release.
# Debian post-release versions use '+' so dev builds sort after the
# matching stable package.
deb_version = cargo_version
deb_version = deb_version[1:] if deb_version.startswith("v") else deb_version
deb_version = deb_version.replace("-dev.", "~dev.", 1)
deb_version = f"{deb_version}-1"

return Versions(
Expand Down
Loading