mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-10 20:56:39 +00:00
Fork CI run 41147980556 pgAdmin#0 failed in sys-ctl-hlth-runtime with
"stalwart_webmail: PHP 8.4.26 is below the security floor 8.4.27".
The deploy ran the fork's GHCR mirror of roundcube/roundcubemail:1.7.5-
apache, copied on 10-19 with PHP 8.4.26, while upstream re-pushed the
same tag on 10-27 with PHP 8.4.27. Branch CI runs the sync with
--only-missing, which skipped the tag because it existed ("destination
exists, skipping") without comparing digests, and the full refresh only
runs on the fork's default branch, which still pins 1.7.4.
- utils/docker/registry.py: manifest_digest() reads the
Docker-Content-Digest of a reference with a HEAD request, which does
not count against Docker Hub's pull allowance.
- GHCRProvider.is_stale() reports a mirrored tag as stale only when both
registries answer with a digest and the two differ.
- Under --only-missing an existing tag is re-copied when it is stale; a
failed re-copy is reported on stderr with the image and keeps the
existing copy, so one refresh cannot block every deploy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
112 lines
3.6 KiB
Python
112 lines
3.6 KiB
Python
from __future__ import annotations
|
|
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
from cli.contributing.mirror.model import ImageRef
|
|
from cli.contributing.mirror.providers import GHCRProvider
|
|
|
|
|
|
class TestGHCRProviderImageBase(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.image = ImageRef(
|
|
role="web-app-nextcloud",
|
|
service="app",
|
|
name="nextcloud",
|
|
version="31.0.0",
|
|
source="docker.io/library/nextcloud:31.0.0",
|
|
registry="docker.io",
|
|
)
|
|
|
|
def test_image_base_simple(self) -> None:
|
|
provider = GHCRProvider("acme", "myrepo")
|
|
self.assertEqual(
|
|
provider.image_base(self.image),
|
|
"ghcr.io/acme/myrepo/mirror/docker.io/nextcloud",
|
|
)
|
|
|
|
def test_image_base_with_slash_in_name(self) -> None:
|
|
image = ImageRef(
|
|
role="web-app-foo",
|
|
service="svc",
|
|
name="foo/bar",
|
|
version="1.0",
|
|
source="docker.io/foo/bar:1.0",
|
|
registry="docker.io",
|
|
)
|
|
provider = GHCRProvider("acme", "myrepo")
|
|
self.assertEqual(
|
|
provider.image_base(image),
|
|
"ghcr.io/acme/myrepo/mirror/docker.io/foo/bar",
|
|
)
|
|
|
|
def test_image_base_quay(self) -> None:
|
|
image = ImageRef(
|
|
role="web-app-keycloak",
|
|
service="app",
|
|
name="keycloak/keycloak",
|
|
version="latest",
|
|
source="quay.io/keycloak/keycloak:latest",
|
|
registry="quay.io",
|
|
)
|
|
provider = GHCRProvider("acme", "myrepo")
|
|
self.assertEqual(
|
|
provider.image_base(image),
|
|
"ghcr.io/acme/myrepo/mirror/quay.io/keycloak/keycloak",
|
|
)
|
|
|
|
def test_namespace_and_repository_are_lowercased(self) -> None:
|
|
provider = GHCRProvider("ACME", "MyRepo")
|
|
self.assertEqual(provider.namespace, "acme")
|
|
self.assertEqual(provider.repository, "myrepo")
|
|
|
|
|
|
class TestGHCRProviderIsStale(unittest.TestCase):
|
|
UPSTREAM = "docker.io/roundcube/roundcubemail"
|
|
MIRROR = "ghcr.io/acme/myrepo/mirror/docker.io/roundcube/roundcubemail"
|
|
|
|
def setUp(self) -> None:
|
|
self.image = ImageRef(
|
|
role="web-app-stalwart",
|
|
service="webmail",
|
|
name="roundcube/roundcubemail",
|
|
version="1.7.5-apache",
|
|
source="docker.io/roundcube/roundcubemail:1.7.5-apache",
|
|
registry="docker.io",
|
|
)
|
|
self.provider = GHCRProvider("acme", "myrepo")
|
|
|
|
def _is_stale(self, digests: dict) -> bool:
|
|
def fake(image: str, reference: str) -> str | None:
|
|
self.assertEqual(reference, "1.7.5-apache")
|
|
return digests[image]
|
|
|
|
with patch(
|
|
"cli.contributing.mirror.providers.manifest_digest", side_effect=fake
|
|
):
|
|
return self.provider.is_stale(self.image)
|
|
|
|
def test_upstream_repushed_tag_makes_the_copy_stale(self) -> None:
|
|
self.assertTrue(
|
|
self._is_stale({self.UPSTREAM: "sha256:new", self.MIRROR: "sha256:old"})
|
|
)
|
|
|
|
def test_matching_digests_are_current(self) -> None:
|
|
self.assertFalse(
|
|
self._is_stale({self.UPSTREAM: "sha256:same", self.MIRROR: "sha256:same"})
|
|
)
|
|
|
|
def test_indeterminate_upstream_counts_as_current(self) -> None:
|
|
self.assertFalse(
|
|
self._is_stale({self.UPSTREAM: None, self.MIRROR: "sha256:old"})
|
|
)
|
|
|
|
def test_indeterminate_mirror_counts_as_current(self) -> None:
|
|
self.assertFalse(
|
|
self._is_stale({self.UPSTREAM: "sha256:new", self.MIRROR: None})
|
|
)
|
|
|
|
|
|
if __name__ == "__main__": # pragma: no cover
|
|
unittest.main()
|