mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 11:46:35 +00:00
feat(images): check declared architectures against the registry, and make a pin say why
Nothing held a role's architectures to reality: test_image_reachable proves a tag exists, test_arm64_images proves four named roles publish arm64, and the closure lint only proves the declarations do not intersect to nothing. An amd64-only bump became a scheduling failure on an arm64 row rather than a red test. A new external test sweeps every pinned image and judges a role by the intersection over its services, because a role runs only where all of its images run; judging per image would demand arm64 from an amd64-only role's nginx and call a narrowing stale because nginx publishes arm64 while the application does not. A role declaring nothing must cover every architecture the matrix rotates; a role that narrows must narrow to exactly its capability, which is what retires its TODO by itself. An unread image downgrades the stale verdict to a warning, since the image a probe could not read may be the one the narrowing exists for. The probe reads the mirror first and caches on disk, because the sweep is ~160 manifests and Docker Hub counts its allowance per IP, shared. A new lint requires every architectures declaration to carry a nocheck marker and a Reason. bigbluebutton and opentalk keep their pins with measured reasons; decidim, pretix and taiga lose theirs, because each builds its own image and their upstream Dockerfiles are arch-neutral. Their source builds are not converted yet, so until that lands the matrix may hand them an arm64 row that fails at FROM. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
03ed2f1ca1
commit
0bb437d9ce
14 files changed
+653
-27
No files matched your search
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
import unittest
|
||||
|
||||
from utils.annotations.suppress import (
|
||||
has_rule_with_reason,
|
||||
is_suppressed_anywhere,
|
||||
is_suppressed_at,
|
||||
is_suppressed_in_head,
|
||||
@@ -152,5 +153,69 @@ class TestSuppressedLineNumbers(unittest.TestCase):
|
||||
self.assertEqual(suppressed_line_numbers(lines, "shared"), {4})
|
||||
|
||||
|
||||
class TestHasRuleWithReason(unittest.TestCase):
|
||||
"""An exemption that has to say why, written above the value it exempts."""
|
||||
|
||||
def test_both_markers_on_the_value_line(self) -> None:
|
||||
lines = [" min_storage: 20GB # nocheck: rule-x Reason: the volume"]
|
||||
|
||||
self.assertTrue(has_rule_with_reason(lines, 1, "rule-x"))
|
||||
|
||||
def test_both_markers_in_the_comment_block_above(self) -> None:
|
||||
lines = [
|
||||
" # nocheck: rule-x",
|
||||
" # Reason: the data volume dominates",
|
||||
" min_storage: 20GB",
|
||||
]
|
||||
|
||||
self.assertTrue(has_rule_with_reason(lines, 3, "rule-x"))
|
||||
|
||||
def test_the_marker_order_does_not_matter(self) -> None:
|
||||
lines = [
|
||||
" # Reason: the data volume dominates",
|
||||
" # nocheck: rule-x",
|
||||
" min_storage: 20GB",
|
||||
]
|
||||
|
||||
self.assertTrue(has_rule_with_reason(lines, 3, "rule-x"))
|
||||
|
||||
def test_a_marker_without_a_reason_is_refused(self) -> None:
|
||||
lines = [" # nocheck: rule-x", " min_storage: 20GB"]
|
||||
|
||||
self.assertFalse(has_rule_with_reason(lines, 2, "rule-x"))
|
||||
|
||||
def test_a_reason_without_the_marker_is_refused(self) -> None:
|
||||
lines = [" # Reason: the data volume dominates", " min_storage: 20GB"]
|
||||
|
||||
self.assertFalse(has_rule_with_reason(lines, 2, "rule-x"))
|
||||
|
||||
def test_an_empty_reason_is_refused(self) -> None:
|
||||
lines = [" # nocheck: rule-x Reason:", " min_storage: 20GB"]
|
||||
|
||||
self.assertFalse(has_rule_with_reason(lines, 2, "rule-x"))
|
||||
|
||||
def test_another_rules_marker_does_not_count(self) -> None:
|
||||
lines = [" # nocheck: rule-y Reason: something else", " min_storage: 20GB"]
|
||||
|
||||
self.assertFalse(has_rule_with_reason(lines, 2, "rule-x"))
|
||||
|
||||
def test_a_blank_line_ends_the_comment_block(self) -> None:
|
||||
lines = [
|
||||
" # nocheck: rule-x Reason: the data volume dominates",
|
||||
"",
|
||||
" min_storage: 20GB",
|
||||
]
|
||||
|
||||
self.assertFalse(
|
||||
has_rule_with_reason(lines, 3, "rule-x"),
|
||||
"a marker separated by a blank line belongs to the construct above it",
|
||||
)
|
||||
|
||||
def test_a_line_number_outside_the_file_is_refused(self) -> None:
|
||||
for line_no in (0, 5):
|
||||
with self.subTest(line_no=line_no):
|
||||
self.assertFalse(has_rule_with_reason(["x"], line_no, "rule-x"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user