feat(release): promote Docker stable/latest from a published receipt tag

The ordered publication pass moves nousresearch/hermes-agent:stable/latest
only after it reads the R2 stable channel head. When that read fails
(v0.21.6: "Channel repository authority mismatch"), the release publishes
and the aliases never move.

`python -m scripts.releases.docker promote --version X.Y.Z` reads the
published vX.Y.Z receipt tag and runs the existing promote_stable with the
digest it binds (slim checked against the receipt, desktop from its own
attempt tag). docker-promote-stable.yml dispatches it with the Docker Hub
credentials only CI holds.
This commit is contained in:
Teknium
2026-10-08 04:57:52 -07:00
parent 99a45ecc17
commit cbe5e53e28
2 changed files with 70 additions and 2 deletions

View File

@@ -170,6 +170,26 @@ def published_digest(tag: str, run=output) -> str:
return digest
def promote_published(version: str, *, run=output) -> None:
"""Move the aliases from the digest a published ``vX.Y.Z`` receipt binds.
The ordered publication pass normally does this after the R2 channel head
moves; this re-enters only the alias step, from the receipt alone.
"""
from scripts.releases.versioning import tag_record
tag = f"v{version}"
if not STABLE_TAG_RE.fullmatch(tag):
raise DockerReleaseError(f"Not a stable version: {version!r}")
run(["git", "fetch", "origin", f"+refs/tags/{tag}:refs/tags/{tag}"])
if run(["git", "cat-file", "-t", f"refs/tags/{tag}"]) != "tag":
raise DockerReleaseError(f"{tag} is not an annotated receipt tag")
receipt = tag_record(run(["git", "tag", "-l", tag, "--format=%(contents)"]))
if not isinstance(receipt, dict) or receipt.get("version") != version:
raise DockerReleaseError(f"{tag} carries no release receipt")
promote_stable(receipt["claimTag"], receipt["dockerManifestDigest"], run=run)
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
sub = parser.add_subparsers(dest="command", required=True)
@@ -187,9 +207,14 @@ def main(argv: list[str] | None = None) -> int:
p_verify.add_argument("--commit", required=True)
p_verify.add_argument("manifest", help="Path to the downloaded manifest JSON")
p_promote = sub.add_parser("promote", help="Move stable/latest onto a published release's receipt-bound image")
p_promote.add_argument("--version", required=True, help="Published version, for example 0.21.6")
args = parser.parse_args(argv)
try:
if args.command == "manifest":
if args.command == "promote":
promote_published(args.version)
elif args.command == "manifest":
archive_hashes = {}
for arch, path in (("amd64", args.archive_amd64), ("arm64", args.archive_arm64)):
if path:
@@ -206,7 +231,7 @@ def main(argv: list[str] | None = None) -> int:
manifest = parse_manifest(handle.read())
verify_manifest(manifest, args.tag, args.commit)
print(json.dumps(manifest))
except DockerReleaseError as exc:
except (DockerReleaseError, subprocess.CalledProcessError) as exc:
print(f"::error::{exc}", file=sys.stderr)
return 1
return 0