fix(release-cut): close both ends of the rc-number range the gate compares

The new explicit-rc gate compares with `[[ -le ]]`, i.e. bash machine-width
integers, and the author closed only the low end. Past INTMAX bash saturates,
so `version=1.4.156-rc.99999999999999999999` reads as "above the published
rc.3" and the gate falls open — then the tag it cuts pins
highest_rc_for_base at 1e20 for that base forever, and every later cut wraps
to a lower rc the fleet never updates to. Bound the rc number to nine digits.

Also reject leading zeros on an all-digit prerelease identifier. `npm version`
renormalizes rc.4.01 to rc.4.1 while the tag step keeps the literal input, so
the shipped package.json version and its own release tag name different
releases. The explicit path's embedded identifier now goes through the same
validator the kind path uses instead of only the shape regex.
This commit is contained in:
Brennan Benson
2026-07-25 03:07:02 -07:00
parent cf4100b661
commit 698c5beeaa
+23 -7
View File
@@ -394,8 +394,13 @@ jobs:
# branch builds never outrank the main RC series and cannot # branch builds never outrank the main RC series and cannot
# hijack the update channel; clients find them by matching the # hijack the update channel; clients find them by matching the
# identifier ("perf") in the prerelease components. # identifier ("perf") in the prerelease components.
if [[ ! "$1" =~ ^[0-9A-Za-z]+$ ]]; then # Why the numeric alternation rather than plain [0-9A-Za-z]+:
echo "::error::version_suffix must be alphanumeric, got: $1" >&2 # semver forbids a leading zero on an all-digit identifier, and
# `npm version` silently renormalizes rc.4.01 to rc.4.1 while the
# tag step keeps the literal input — so the shipped package.json
# version and its own release tag would name different releases.
if [[ ! "$1" =~ ^(0|[1-9][0-9]*|[0-9A-Za-z]*[A-Za-z][0-9A-Za-z]*)$ ]]; then
echo "::error::version suffix must be alphanumeric with no leading zero on an all-digit identifier, got: $1" >&2
exit 1 exit 1
fi fi
} }
@@ -513,14 +518,25 @@ jobs:
# Why the optional trailing identifier: the rc path can cut # Why the optional trailing identifier: the rc path can cut
# suffixed side-branch RCs (X.Y.Z-rc.N.perf), and a shape this # suffixed side-branch RCs (X.Y.Z-rc.N.perf), and a shape this
# regex rejects can never be re-cut through the override. # regex rejects can never be re-cut through the override.
# Why rc.(0|[1-9][0-9]*): semver forbids a leading zero on a # Why rc.(0|[1-9][0-9]{0,8}): the `-le` below compares with bash's
# numeric identifier, and rc.08 would otherwise reach the `-le` # machine-width integers, so both ends of that range fall *open* on
# below as an invalid octal literal, whose error makes the test # exactly the RCs this gate must catch. A leading zero (rc.08) is an
# false — falling *open* on exactly the RCs this gate must catch. # invalid octal literal, and the failed test makes the `if` false.
if [[ ! "$explicit" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc\.(0|[1-9][0-9]*)(\.[0-9A-Za-z]+)?)?$ ]]; then # Past INTMAX (rc.99999999999999999999) bash saturates, so the test
# reads as "above the published rc" and the cut lands a tag that
# then pins highest_rc_for_base at 1e20 forever — every later cut
# wraps to a *lower* rc the fleet never updates to. Nine digits is
# far above any real series and exact in bash math either way.
if [[ ! "$explicit" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc\.(0|[1-9][0-9]{0,8})(\.[0-9A-Za-z]+)?)?$ ]]; then
echo "::error::version must be X.Y.Z, X.Y.Z-rc.N, or X.Y.Z-rc.N.suffix, got: $EXPLICIT_VERSION" >&2 echo "::error::version must be X.Y.Z, X.Y.Z-rc.N, or X.Y.Z-rc.N.suffix, got: $EXPLICIT_VERSION" >&2
exit 1 exit 1
fi fi
# Why route the embedded identifier through the same validator the
# kind path uses: the regex above only checks shape, and rc.4.01
# is a shape-valid identifier that is not valid semver.
if [[ "$explicit" == *-rc.*.* ]]; then
require_valid_version_suffix "${explicit##*.}"
fi
# Same updater-safety gate the kind path enforces: stable line must # Same updater-safety gate the kind path enforces: stable line must
# strictly increase over the latest published stable (prerelease # strictly increase over the latest published stable (prerelease
# identifiers ignored for the comparison). # identifiers ignored for the comparison).