"""Data types for the development inventory matrix. `DevInventorySpec` is the value type `build_dev_inventory` consumes — it carries everything `infinito administration inventory provision` needs to materialise one development inventory folder. `PlanEntry` is the per-round tuple `plan_dev_inventory_matrix` emits and the deploy wrapper iterates. """ from __future__ import annotations from dataclasses import dataclass from typing import TYPE_CHECKING, Any if TYPE_CHECKING: from collections.abc import Mapping PlanEntry = tuple[int, str, dict[str, int], tuple[str, ...], tuple[str, ...]] @dataclass(frozen=True) class DevInventorySpec: """Everything `infinito administration inventory provision` needs to materialise one development inventory folder. `extra_vars` wins over the implicit `STORAGE_CONSTRAINED` / `RUNTIME` overrides so callers (and tests) keep the documented "user-provided vars always win" behaviour without re-implementing the merge. `active_variants` is the per-app variant index for the round this folder represents. Apps absent from the mapping (or with an out-of- range index) fall back to variant 0. The resolved variant payload for every app in `include` is baked into the inventory as an `applications.` override, so the deploy stage no longer needs a runtime variant selector — the inventory itself is variant-resolved. """ inventory_dir: str include: tuple[str, ...] storage_constrained: bool runtime: str extra_vars: Mapping[str, Any] | None = None services_disabled: str = "" active_variants: Mapping[str, int] | None = None def __post_init__(self) -> None: if not self.include: raise ValueError("DevInventorySpec.include must not be empty") if not isinstance(self.include, tuple): object.__setattr__(self, "include", tuple(self.include)) def overrides(self) -> dict[str, Any]: merged: dict[str, Any] = { "STORAGE_CONSTRAINED": bool(self.storage_constrained), "RUNTIME": self.runtime, } if self.extra_vars: merged.update(self.extra_vars) return merged def variant_selectors(self) -> dict[str, int]: return dict(self.active_variants or {}) def inventory_root(self) -> str: return str(self.inventory_dir).rstrip("/")