mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
The env generator runs on the bare bootstrap python, before any dependency is installed. utils/distros.py reached PyYAML through utils.cache.yaml, so `python -m cli.meta.env` died with ModuleNotFoundError on every dev-runtime image that ships python3 without PyYAML. utils/yaml_bootstrap.load_block reads the block-YAML subset the SPOT files use with stdlib only. It raises BootstrapYamlError on anything it cannot parse identically to yaml.safe_load, so meta/distros.yml fails loud instead of drifting. The per-line regex lint meant to guard this is gone: one hop of indirection defeated it and it stayed green while the invariant was broken. It is replaced by a test that runs the real generator with every non-stdlib import blocked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3.9 KiB
3.9 KiB
utils/env/handlers 🧩
Per-variable mapping logic for the .env generator.
For the package's overall role see README.md.
For general README conventions see documentation.md.
Purpose 🎯
Each module in this directory owns exactly one variable, or one tightly-coupled group resolved by a single helper call.
The orchestrator in builder.py iterates the registry in __init__.py and calls each handler's apply(eb, ctx) in order.
File Naming 🏷️
- A handler module name MUST match the lowercase of the variable it owns:
INFINITO_CONTAINER->infinito/container.pyINFINITO_GHCR_MIRROR_PREFIX->infinito/ghcr_mirror_prefix.py
- The
infinito_prefix in the filename MUST be present iff the owned key carries theINFINITO_prefix. - Aggregate handlers (one helper call resolves several keys at once) MUST take the singular topic name without a key suffix.
Example:
infinito/inventory.pyresolvesINFINITO_INVENTORY_DIR,INFINITO_INVENTORY_FILE, andINFINITO_INVENTORY_HOST_VARS_FILEfrom onescripts/inventory/resolve.shinvocation. - The trivial static passthroughs live in two collector files:
passthrough.py(always emitted) andgha_passthrough.py(emitted only whenctx.on_ghais true).
Module Shape 📐
Every handler module MUST expose the same surface so the registry can call it uniformly:
"""<one-line docstring: what variable, what it derives from>."""
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from utils.env.builder import BuildContext, EnvBuilder
KEY = "INFINITO_..."
COMMENT = "<single-line per-key comment, mirrors default.env style>"
def apply(eb: EnvBuilder, ctx: BuildContext) -> None: ...
KEYMUST be the owned variable's name.COMMENTMUST be a single line, no em dashes, that ends up above the key in the generated.env.applyMUST take(eb, ctx)and returnNone.- A handler that targets a conditional branch (GHA-only, NIX-only, ...) MUST self-check the relevant
ctxflag and return early. The registry calls every handler unconditionally.
Import Rules 🔗
- A handler MUST NOT import another handler.
Cross-handler data flows through
eb.get(...)/eb.set(...)only. - A handler MAY import stdlib, parser.py, runtime.py, and any repo module whose own import closure is stdlib-only.
The whole closure MUST import and run on the bare bootstrap python, before any dependency is installed.
utils.cache.filesand yaml_bootstrap.py qualify;utils.cache.yamldoes not, because it imports PyYAML. Read SPOT files withutils.cache.files.read_textplusutils.yaml_bootstrap.load_blockor a stdlib line parse.load_blockaccepts block mappings, block lists and string scalars only, and raisesBootstrapYamlErroron everything else, so a SPOT file read this way MUST stay inside that subset. test_bare_bootstrap.py enforces this by running the generator with every non-stdlib import blocked. - A handler MUST NOT import from writer.py or builder.py at runtime.
The
EnvBuilderandBuildContexttypes MAY be referenced underTYPE_CHECKINGfor annotations only.
Registry 📋
- __init__.py MUST list every handler module in
ORDERED_HANDLERS. - The order in
ORDERED_HANDLERSMUST respect data dependencies. Example:infinito_containerruns afterpassthroughso it can read the resolvedINFINITO_DISTRO. - Adding a handler MUST also bump the relevant baseline test in test_dotenv_generator.py when the new key is a static-default in default.env, so the drift test stays honest.