Run the entire CI workflow before Docker build and tests. Require Nix, native payload smoke tests, install/update E2E and signed-package upgrade acceptance before publishing. Keep Desktop Playwright E2E deferred. Archive tested Docker images and signed bundle candidates with provenance and hashes. Publishers consume those exact artifacts without rebuilding. Advance stable channels only after all required publications succeed. Keep canaries on their separate path and reject direct stable-builder publication that bypasses the gate. Move shared release transport, manifests and gates to Python. Keep native Electron adapters in JS and share feed/MIME facts as JSON. Replace the R2/feed JS implementation and move its protocol tests to Python. Verified targeted Python and JS tests, real loopback transport and CLI execution, temporary Git admission, workflow graph lint, and typechecks. No live stable release was run. Native signing, package upgrades and real registry/Store promotion still need their release-run receipts. Separate services cannot promote atomically. A promotion failure keeps the run red.
55 lines
2.0 KiB
Python
55 lines
2.0 KiB
Python
"""Compare the stable and canary versions accepted by release feeds."""
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from hermes_cli.update_channel import _CANARY_TAG_RE
|
|
|
|
STABLE_TAG = re.compile(r"v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)")
|
|
|
|
|
|
def is_valid_version(version: str) -> bool:
|
|
"""semver.valid restricted to our release grammar (no build metadata,
|
|
no alphanumeric prerelease identifiers — those never appear in
|
|
generated tags)."""
|
|
if not isinstance(version, str):
|
|
return False
|
|
core, sep, tail = version.partition("-")
|
|
if sep and not STABLE_TAG.fullmatch("v" + core):
|
|
return False
|
|
if not sep:
|
|
return bool(STABLE_TAG.fullmatch("v" + version))
|
|
if not _CANARY_TAG_RE.fullmatch("v" + version):
|
|
return False
|
|
# Canary timestamp is a fixed-length 14-digit numeric stamp.
|
|
stamp = tail.split(".", 1)[1]
|
|
return len(stamp) == 14
|
|
|
|
|
|
def _prerelease_key(tail: str) -> list[int]:
|
|
"""Numeric sort key for a canary suffix — 'canary.<14 digits>'."""
|
|
return [int(tail.split(".", 1)[1])]
|
|
|
|
|
|
def compare(a: str, b: str) -> int:
|
|
"""semver.compare for our grammar. Both sides must be valid release
|
|
versions (ValueError otherwise — feed publication fails loudly).
|
|
Semver ordering: stable 0.28.0 > any 0.28.0-canary.<stamp>; stamps
|
|
compare numerically."""
|
|
if not is_valid_version(a) or not is_valid_version(b):
|
|
raise ValueError(f"invalid release version(s): {a!r}, {b!r}")
|
|
a_core, _, a_tail = a.partition("-")
|
|
b_core, _, b_tail = b.partition("-")
|
|
if a_core != b_core:
|
|
ka = [int(p) for p in a_core.split(".")]
|
|
kb = [int(p) for p in b_core.split(".")]
|
|
return -1 if ka < kb else 1
|
|
# Same core: a prerelease (canary) sorts BEFORE the stable release.
|
|
if a_tail and b_tail:
|
|
ka, kb = _prerelease_key(a_tail), _prerelease_key(b_tail)
|
|
return -1 if ka < kb else (1 if ka > kb else 0)
|
|
if a_tail:
|
|
return -1
|
|
if b_tail:
|
|
return 1
|
|
return 0
|