Files
computer-playbook/utils/docker/image/pin.py
kevinveenbirkenbachandClaude Opus 5 e14e73fba0 refactor(docker): let a moving tag declare itself with the marker it already has
A pin outside semver was given its own 'docker-ref' marker on the theory
that every class needs one. That holds for a digest and not for a tag. A
digest pin declares something about the registry, that bytes move under a
standing tag, which no other choice says. A tag outside semver declares
that upstream publishes nothing orderable, which is exactly what
'docker-version' already means and what minio and the act runner already
carry on their version lines.

Keeping both would have put '# nocheck: docker-version, docker-ref' on
every such line: two keys, two checks, one sentence said twice. 'docker-ref'
is dropped instead. It lived in this one mapping, was never entered in the
suppression catalog, and no check ever read it.

The semver entry goes with it, because a semver pin never needs a
declaration. The test that asserted three distinct rules is replaced by two
that pin the separation where it is real: a digest marker differs from a
tag marker, and a semver pin has no marker at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 17:47:02 +02:00

59 lines
1.5 KiB
Python

from utils.update.base import version_depth
DIGEST_PREFIX = "sha256:"
SEMVER = "semver"
DIGEST = "digest"
REF = "ref"
RULE_BY_CLASS = {
DIGEST: "docker-digest",
REF: "docker-version",
}
def is_digest(version: str) -> bool:
"""Whether ``version`` pins by content rather than by name.
Args:
version: the ``version:`` value a role declares for an image.
"""
return str(version).strip().startswith(DIGEST_PREFIX)
def pin_class(version: str) -> str:
"""Classify a pin as ``semver``, ``digest`` or ``ref``.
Args:
version: the ``version:`` value a role declares for an image.
Returns:
The class name. ``ref`` is the residue: a tag that names no version
the registry can order, so nothing can tell whether it moved.
"""
stripped = str(version).strip()
if is_digest(stripped):
return DIGEST
if version_depth(stripped) > 0:
return SEMVER
return REF
def reference_separator(version: str) -> str:
"""The character that joins an image name to ``version`` in a pull ref.
Args:
version: the ``version:`` value a role declares for an image.
"""
return "@" if is_digest(version) else ":"
def pull_reference(name: str, version: str) -> str:
"""Join an image name and a pin into the reference a registry accepts.
Args:
name: image name, with or without a registry prefix.
version: the ``version:`` value a role declares for an image.
"""
return f"{name}{reference_separator(version)}{str(version).strip()}"