mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-13 14:16:46 +00:00
- Move iter_role_images + all helpers + ImageRef from cli/mirror/util.py and
cli/mirror/model.py into utils/docker/image_discovery.py; add source_file field
to ImageRef to distinguish config/main.yml vs vars/main.yml entries
- Delete cli/mirror/util.py; update all callers to import from utils.docker.image_discovery
- Move utils into subpackages: utils/docker/ (image_ref, paths_utils, hadolint) and
utils/roles/ (dependency_resolver, resource_validation); update all imports
- Reorganise tests into subpackages: tests/lint/docker/dockerfile/, tests/integration/docker/,
tests/unit/utils/docker/, tests/unit/utils/roles/; fix parents[N] offsets after relocation
- tests/lint/docker/test_image_versions.py now uses iter_role_images (same SPOT as mirror)
- Add vars/main.yml images.<name>.{image,version} support for non-application roles
(test-e2e-playwright, sys-ctl-hlth-csp); eliminate hardcoded duplicates in defaults/tasks
- Add web-app-matrix matrix-chatgpt-bot and web-app-mini-qr mini-qr entries in config/main.yml;
replace hardcoded image strings in vars/templates with lookup('config', ...) references
- Fix sync/__main__.py log line to use correct registry prefix for non-Docker-Hub images
- Update docs/contributing/code/role/dockerfile.md with image declaration rules and SPOT reference
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
97 lines
3.5 KiB
Python
97 lines
3.5 KiB
Python
"""Warn about templated Dockerfiles that should become plain files.
|
|
|
|
Roles that still ship ``templates/Dockerfile.j2`` are reported as warnings so
|
|
that maintainers can migrate them to ``files/Dockerfile`` over time without
|
|
blocking CI.
|
|
|
|
Two distinct warnings are emitted depending on the file content:
|
|
|
|
- **No Jinja2 logic** (only ``{{ variable }}`` substitutions or plain text):
|
|
The file can be migrated to a plain ``files/Dockerfile`` by replacing
|
|
variables with Docker ``ARG`` declarations and passing them via the compose
|
|
build ``args`` block.
|
|
- **Jinja2 control-flow logic** (``{% if %}``, ``{% for %}``, etc.):
|
|
The conditional logic prevents a direct one-to-one migration. The warning
|
|
remains because maintainers should still consider whether the logic can be
|
|
eliminated or split into separate Dockerfiles.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from utils.annotations.message import warning
|
|
|
|
_REPO_ROOT = Path(__file__).resolve().parents[4]
|
|
_ROLES_ROOT = _REPO_ROOT / "roles"
|
|
|
|
# Matches any Jinja2 control-flow or structural tag: {% ... %}
|
|
_J2_LOGIC_RE = re.compile(r"\{%-?\s*\w")
|
|
|
|
|
|
def _has_j2_logic(dockerfile_j2: Path) -> bool:
|
|
"""Return True if the file contains at least one Jinja2 block tag."""
|
|
return bool(_J2_LOGIC_RE.search(dockerfile_j2.read_text(encoding="utf-8")))
|
|
|
|
|
|
def _collect_templated_dockerfiles() -> list[Path]:
|
|
templated: list[Path] = []
|
|
for dockerfile_j2 in sorted(_ROLES_ROOT.glob("*/templates/Dockerfile.j2")):
|
|
plain_dockerfile = dockerfile_j2.parents[1] / "files" / "Dockerfile"
|
|
if plain_dockerfile.is_file():
|
|
continue
|
|
templated.append(dockerfile_j2)
|
|
return templated
|
|
|
|
|
|
def _warning_message(relative_path: str, has_logic: bool) -> str:
|
|
if has_logic:
|
|
return (
|
|
f"{relative_path} uses Dockerfile.j2 with Jinja2 control-flow logic. "
|
|
"Consider whether the conditional logic can be eliminated or split into "
|
|
"separate Dockerfiles to allow migration to a plain files/Dockerfile."
|
|
)
|
|
return (
|
|
f"{relative_path} uses Dockerfile.j2 instead of files/Dockerfile. "
|
|
"Prefer a plain Dockerfile because Dockerfiles are easier to test and "
|
|
"maintain and provide better separation of concerns. "
|
|
"Variables can be passed as Docker ARG declarations via the compose build args block."
|
|
)
|
|
|
|
|
|
def _emit_warning(dockerfile_j2: Path) -> None:
|
|
relative_path = dockerfile_j2.relative_to(_REPO_ROOT).as_posix()
|
|
has_logic = _has_j2_logic(dockerfile_j2)
|
|
message = _warning_message(relative_path, has_logic)
|
|
warning(message, title="Templated Dockerfile", file=relative_path)
|
|
|
|
|
|
class TestDockerfileTemplates(unittest.TestCase):
|
|
"""Warn when roles still use templates/Dockerfile.j2."""
|
|
|
|
def test_dockerfile_j2_warns_only(self) -> None:
|
|
self.assertTrue(
|
|
_ROLES_ROOT.is_dir(), f"'roles' directory not found at: {_ROLES_ROOT}"
|
|
)
|
|
|
|
templated = _collect_templated_dockerfiles()
|
|
|
|
failures: list[Path] = []
|
|
for dockerfile_j2 in templated:
|
|
_emit_warning(dockerfile_j2)
|
|
if not _has_j2_logic(dockerfile_j2):
|
|
failures.append(dockerfile_j2)
|
|
|
|
self.assertFalse(
|
|
failures,
|
|
"The following Dockerfile.j2 files contain no Jinja2 control-flow logic "
|
|
"and MUST be migrated to plain files/Dockerfile:\n"
|
|
+ "\n".join(f" {p.relative_to(_REPO_ROOT).as_posix()}" for p in failures),
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|