Source code for gyoza.deployment.builder

"""Docker build logic driven by a gyoza.yml configuration.

Generates a temporary ``docker-compose.yml`` from the gyoza config
and delegates to ``docker compose build``.
"""

from __future__ import annotations

import subprocess
import tempfile
from pathlib import Path
from typing import Any

import yaml

from gyoza.deployment.config import load_config

_SERVICE_NAME = "gyoza_build"
_REQUIRED_KEYS = frozenset({"image", "build"})


def _load_build_config(project_path: Path) -> dict[str, Any]:
    """Read and validate the gyoza config for building.

    Parameters
    ----------
    project_path : Path
        Root directory containing the gyoza config.

    Returns
    -------
    dict[str, Any]
        Parsed YAML configuration.

    Raises
    ------
    FileNotFoundError
        If the config file is not found.
    ValueError
        If ``image`` or ``build`` keys are missing.
    """
    data = load_config(project_path)
    missing = _REQUIRED_KEYS - data.keys()
    if missing:
        msg = f"Missing required keys in gyoza config: {', '.join(sorted(missing))}"
        raise ValueError(msg)
    return data


[docs] def build(project_path: Path | str) -> subprocess.CompletedProcess[str]: """Run ``docker compose build`` for a gyoza project. Reads ``gyoza.yml`` from *project_path*, writes a temporary ``docker-compose.yml`` carrying the ``build`` section verbatim, and invokes ``docker compose build``. Parameters ---------- project_path : Path | str Directory containing ``gyoza.yml`` and the build context. Returns ------- subprocess.CompletedProcess[str] Result of the ``docker compose build`` invocation. Raises ------ FileNotFoundError If ``gyoza.yml`` is missing. ValueError If the config is invalid. subprocess.CalledProcessError If ``docker compose build`` exits with a non-zero code. """ project_path = Path(project_path).resolve() data = _load_build_config(project_path) compose = { "services": { _SERVICE_NAME: { "image": data["image"], "build": data["build"], } } } with tempfile.NamedTemporaryFile( mode="w", suffix=".yml", dir=project_path, prefix=".gyoza-compose-", delete=True, ) as tmp: yaml.safe_dump(compose, tmp, default_flow_style=False) tmp.flush() cmd = ["docker", "compose", "-f", tmp.name, "build", _SERVICE_NAME] return subprocess.run(cmd, check=True, text=True, cwd=project_path) # noqa: S603