Files
hermes-agent/tests/scripts/test_release_docker_cli.py
ethernet 8e0a256f49 feat(release): key the stable archive by the attempt ref
desktop preparation parses its claim ref and carries the attempt ref as
archive_tag beside the plain payload version. Stable admission accepts
only attempt refs and returns them; accepted candidates, bootstrap and
advance_stable read the attempt-scoped archive, and docker manifests may
carry the attempt-ref image tag while stable/latest aliases stay put
until publish.
2026-09-23 09:56:49 -04:00

89 lines
3.7 KiB
Python

"""Docker receipt CLI validates identity and hashes actual local archive bytes."""
import copy
import hashlib
import json
from pathlib import Path
import subprocess
import sys
import pytest
ROOT = Path(__file__).resolve().parents[2]
def test_cli_manifest_and_verify(tmp_path):
identity = ['--tag', 'v1.2.3', '--commit', 'a' * 40]
digests = ['--digest-amd64', 'b' * 64, '--digest-arm64', 'c' * 64]
archives = []
expected = {}
for arch, data in [('amd64', b'first archive'), ('arm64', b'second archive')]:
path = tmp_path / f'{arch}.tar'
path.write_bytes(data)
archives.extend([f'--archive-{arch}', str(path)])
expected[arch] = hashlib.sha256(data).hexdigest()
def cli(*args):
return subprocess.run([sys.executable, '-m', 'scripts.releases.docker', *args],
cwd=ROOT, capture_output=True, text=True, encoding='utf-8', timeout=30)
for extra in ([], archives):
result = cli('manifest', *identity, *digests, *extra)
assert result.returncode == 0, result.stderr
manifest = json.loads(result.stdout)
assert manifest['digests'] == {'amd64': 'b' * 64, 'arm64': 'c' * 64}
assert manifest.get('archives') == (expected if extra else None)
out = tmp_path / 'manifest.json'
out.write_text(result.stdout, encoding='utf-8')
assert cli('verify', *identity, str(out)).returncode == 0
for extra in (archives[:2], ['--digest-arm64', 'z' * 64]):
result = cli('manifest', *identity, *digests, *extra)
assert result.returncode == 1 and '::error::' in result.stderr
for change in [
{'tag': 'v1.2.4'}, {'commit': 'b' * 40}, {'schema': 2},
{'digests': {'amd64': 'b' * 64}}, {'digests': {'amd64': 'b' * 64, 'arm64': 'z' * 64}},
{'archives': {'amd64': 'd' * 64}}, {'archives': {'amd64': 'd' * 64, 'riscv64': 'e' * 64}},
{'list-digest': 'sha256:wrong'}, None,
]:
bad = copy.deepcopy(manifest)
if change:
bad.update(change)
out.write_text(json.dumps(bad) if change else 'not json', encoding='utf-8')
result = cli('verify', *identity, str(out))
assert result.returncode == 1 and '::error::' in result.stderr
def test_manifest_admits_the_attempt_ref_image_tag():
from scripts.releases.docker import DockerReleaseError, build_manifest, parse_manifest
manifest = build_manifest("rc.1-v1.2.3", "a" * 40, {"amd64": "b" * 64, "arm64": "c" * 64})
assert parse_manifest(json.dumps(manifest).encode())["tag"] == "rc.1-v1.2.3"
# The old suffix shape is dead; a recut reuses no image tag.
with pytest.raises(DockerReleaseError):
build_manifest("v1.2.3-rc", "a" * 40, {"amd64": "b" * 64, "arm64": "c" * 64})
with pytest.raises(DockerReleaseError):
parse_manifest(json.dumps(dict(manifest, tag="not-a-tag")).encode())
def test_promotion_reuses_the_receipt_digest_without_rebuilding():
from scripts.releases.docker import DockerReleaseError, promote_stable
digest = 'sha256:' + 'd' * 64
calls = []
def run(argv):
calls.append(argv)
if argv[:4] == ['docker', 'buildx', 'imagetools', 'inspect']:
return digest
if argv[:4] == ['docker', 'buildx', 'imagetools', 'create']:
return ''
raise AssertionError(argv)
promote_stable('v1.2.3', digest, run=run)
create = next(argv for argv in calls if argv[3] == 'create')
assert create[-1] == f'nousresearch/hermes-agent@{digest}'
assert all('build' not in argv for argv in calls)
with pytest.raises(DockerReleaseError, match='versioned tag'):
promote_stable('v1.2.3', digest, run=lambda _argv: 'sha256:' + 'e' * 64)