What happened
A consumer pins shellcheck-py/shellcheck-py at v0.11.0.1-1. Upstream
also has v0.11.0.1, and -1 is that project's later post-release: the
tag dates are 2025-08-09 and 2026-08-20. uphold guard --stage manual
refuses the pin under no-stale-hook-pins:
shellcheck-py/shellcheck-py pins v0.11.0.1-1, and v0.11.0.1 is newer
Reproduced under 1.18.0 and 1.19.0. The pin is the newest tag the remote
has, and the guard says to move backwards.
Why
version_key in src/pins.rs does trimmed.split_once('-') and treats
everything after the first hyphen as a semver prerelease: rank is 0
for a tag with a suffix and 1 without, and rank sits in the key before
the suffix text. remote_refs sorts tags by that key and stale takes
tags.last() as newest. So v0.11.0.1-1 becomes release 0.11.0.1,
prerelease 1, rank 0, and sorts under v0.11.0.1, rank 1. That is the
right answer for v1.0.0-rc1 -- the case the test
a_release_candidate_does_not_outrank_its_release pins down -- and the
wrong one for a suffix that is a bare revision counter, which Debian,
Python (.post1), Alpine (-r1) and this upstream all use to mean
"after". The comment above version_key says why this matters: a guard
that reports a current pin as stale is how a check earns a blanket
opt-out.
What would close it
Read a suffix that is purely numeric after - (and the .postN, -rN,
_N counter forms) as ordering AFTER the bare version, so rank has
three values: prerelease, release, post-release. Or, when two tags differ
only by such a suffix, fall back to the forge's tag date. A unit test
beside the existing one asserting v0.11.0.1-1 sorts above v0.11.0.1,
with v1.0.0-rc1 < v1.0.0 and v10 > v9 unchanged.
What happened
A consumer pins
shellcheck-py/shellcheck-pyatv0.11.0.1-1. Upstreamalso has
v0.11.0.1, and-1is that project's later post-release: thetag dates are 2025-08-09 and 2026-08-20.
uphold guard --stage manualrefuses the pin under
no-stale-hook-pins:Reproduced under 1.18.0 and 1.19.0. The pin is the newest tag the remote
has, and the guard says to move backwards.
Why
version_keyinsrc/pins.rsdoestrimmed.split_once('-')and treatseverything after the first hyphen as a semver prerelease:
rankis 0for a tag with a suffix and 1 without, and
ranksits in the key beforethe suffix text.
remote_refssorts tags by that key andstaletakestags.last()as newest. Sov0.11.0.1-1becomes release0.11.0.1,prerelease
1, rank 0, and sorts underv0.11.0.1, rank 1. That is theright answer for
v1.0.0-rc1-- the case the testa_release_candidate_does_not_outrank_its_releasepins down -- and thewrong one for a suffix that is a bare revision counter, which Debian,
Python (
.post1), Alpine (-r1) and this upstream all use to mean"after". The comment above
version_keysays why this matters: a guardthat reports a current pin as stale is how a check earns a blanket
opt-out.
What would close it
Read a suffix that is purely numeric after
-(and the.postN,-rN,_Ncounter forms) as ordering AFTER the bare version, sorankhasthree values: prerelease, release, post-release. Or, when two tags differ
only by such a suffix, fall back to the forge's tag date. A unit test
beside the existing one asserting
v0.11.0.1-1sorts abovev0.11.0.1,with
v1.0.0-rc1 < v1.0.0andv10 > v9unchanged.