mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
The GHCR mirror covered role images only. The images the runner pulls on its own went straight to Docker Hub: coredns from compose.yml, the cache stack in compose/cache.override.yml and alpine:latest, the init image of scripts/docker/cache/package-frontend/certs.sh. With the anonymous githubactions quota spent, the guide step of Postmarks#0 (main run 37954203718) and Shopware#2 (#872) died on "toomanyrequests" while pulling alpine:latest, after coredns had hit the same limit. iter_runner_images (utils/docker/image/discovery.py) finds those images, and the mirror sync and the fork-PR wait now cover them next to the role images. On a real GitHub runner compose_file_args layers a generated build/compose/runner-mirror.override.yml that points only the services of the layered compose files at their mirror, and the certificate generator gets the mirrored init image. Local runs and act are unchanged, and the coredns literal stays in compose.yml for Dependabot. test-dns now waits for images-mirror-missing like the deploy and workspace jobs, since it brings up the stack too. Verified with docker compose config on a simulated GitHub runner: the service set is unchanged and only the runner images point at ghcr.io/infinito-nexus/core/mirror/docker.io/..., and an existing role mirror under that scheme pulls anonymously. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
69 lines
2.1 KiB
Python
69 lines
2.1 KiB
Python
"""Point the images the CI runner pulls itself at their GHCR mirror."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from typing import TYPE_CHECKING
|
|
|
|
from cli.contributing.mirror.providers import GHCRProvider
|
|
from utils.cache.yaml import dump_yaml
|
|
from utils.docker.image.discovery import iter_runner_images
|
|
|
|
if TYPE_CHECKING:
|
|
from pathlib import Path
|
|
|
|
OVERRIDE = "build/compose/runner-mirror.override.yml"
|
|
|
|
|
|
def mirrored_images(repo_root: Path) -> dict[tuple[str, str], str]:
|
|
"""Return ``{(source_file, service): mirror reference}`` for every runner image.
|
|
|
|
Args:
|
|
repo_root: repository root the runner images are declared under.
|
|
"""
|
|
provider = GHCRProvider(
|
|
os.environ["GITHUB_REPOSITORY_OWNER"],
|
|
os.environ["GITHUB_REPOSITORY"].split("/", 1)[-1],
|
|
os.environ["INFINITO_GHCR_MIRROR_PREFIX"],
|
|
)
|
|
return {
|
|
(
|
|
image.source_file,
|
|
image.service,
|
|
): f"{provider.image_base(image)}:{image.version}"
|
|
for image in iter_runner_images(repo_root)
|
|
}
|
|
|
|
|
|
def write_override(repo_root: Path, layered: list[str]) -> str:
|
|
"""Write the compose override that swaps the runner's images for mirrors.
|
|
|
|
Args:
|
|
repo_root: repository root the compose files are relative to.
|
|
layered: compose files the stack is built from. Only their services
|
|
are overridden, so the override never adds a service of its own.
|
|
|
|
Returns:
|
|
The override path relative to ``repo_root``.
|
|
"""
|
|
services = {
|
|
service: {"image": reference}
|
|
for (source_file, service), reference in mirrored_images(repo_root).items()
|
|
if source_file in layered
|
|
}
|
|
target = repo_root / OVERRIDE
|
|
staging = target.with_name(f"{target.name}.{os.getpid()}")
|
|
dump_yaml(staging, {"services": services})
|
|
staging.replace(target)
|
|
return OVERRIDE
|
|
|
|
|
|
def env_image(repo_root: Path, key: str) -> str:
|
|
"""Return the mirror reference for an image ``default.env`` declares.
|
|
|
|
Args:
|
|
repo_root: repository root holding ``default.env``.
|
|
key: the ``default.env`` key naming the image.
|
|
"""
|
|
return mirrored_images(repo_root)[("default.env", key)]
|