mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
The runtime image pins lived in meta/distros.yml, which neither the docker updater, the image mirror nor the vulnerability scan reads, so a pin would only ever move by hand. The new role sys-os carries them in its meta/services.yml under the distro id; utils/distros.py renders the workspace matrix from there with the stdlib block reader, and meta/distros.yml keeps the distro attributes only. A new release now arrives as an updater pull request. follow: latest holds debian, ubuntu and fedora to the release their latest tag points at, by Docker Hub digest: Ubuntu moves between LTS releases only (26.10 shares the digest of rolling, not latest) and Fedora skips rawhide (46 shares the digest of rawhide, latest is 45). Arch stays rolling; CentOS stream tags carry no number the updater can order. dockerhub_repo and is_dockerhub move to utils/docker/image/ref.py beside split_registry_and_name, so utils/update/follow.py can use them without an import cycle and utils/update/docker.py stays under the size cap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
1.9 KiB
Python
71 lines
1.9 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import Any
|
|
|
|
DOCKER_HUB_REGISTRIES = frozenset(
|
|
{"docker.io", "registry-1.docker.io", "index.docker.io"}
|
|
)
|
|
GHCR_REGISTRY = "ghcr.io"
|
|
MCR_REGISTRY = "mcr.microsoft.com"
|
|
|
|
IMAGE_NAME_RE = re.compile(
|
|
r"^"
|
|
r"("
|
|
r"(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)"
|
|
r"(?:\.(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?))*"
|
|
r"(?:\:[0-9]{1,5})?"
|
|
r"/"
|
|
r")?"
|
|
r"[a-z0-9]+(?:[._-][a-z0-9]+)*"
|
|
r"(?:/[a-z0-9]+(?:[._-][a-z0-9]+)*)*"
|
|
r"$"
|
|
)
|
|
|
|
|
|
def is_valid_image_name(image: Any) -> bool:
|
|
if not isinstance(image, str):
|
|
return False
|
|
image = image.strip()
|
|
if not image or " " in image or "@" in image:
|
|
return False
|
|
|
|
if image.rfind(":") > image.rfind("/"):
|
|
return False
|
|
|
|
return IMAGE_NAME_RE.fullmatch(image) is not None
|
|
|
|
|
|
def split_registry_and_name(image: str) -> tuple[str | None, str] | None:
|
|
"""Return (registry, repository_name) for a validated Docker image name."""
|
|
image = image.strip()
|
|
if not is_valid_image_name(image):
|
|
return None
|
|
|
|
first, sep, rest = image.partition("/")
|
|
if not sep:
|
|
return None, image
|
|
|
|
if "." in first or ":" in first or first == "localhost":
|
|
return first, rest
|
|
|
|
return None, image
|
|
|
|
|
|
def is_dockerhub(image: str) -> bool:
|
|
parsed = split_registry_and_name(image)
|
|
if parsed is None:
|
|
return False
|
|
registry, _name = parsed
|
|
return registry is None or registry in DOCKER_HUB_REGISTRIES
|
|
|
|
|
|
def dockerhub_repo(image: str) -> str:
|
|
parsed = split_registry_and_name(image)
|
|
if parsed is None:
|
|
raise ValueError(f"Invalid Docker image reference: {image!r}")
|
|
registry, name = parsed
|
|
if registry is not None and registry not in DOCKER_HUB_REGISTRIES:
|
|
raise ValueError(f"Image is not a Docker Hub reference: {image!r}")
|
|
return name if "/" in name else f"library/{name}"
|