Files
computer-playbook/utils/env/README.md
T

50 lines
3.6 KiB
Markdown

# utils/env 📐
This directory hosts the Python implementation behind `make dotenv`.
For the general rules that govern READMEs in code directories see [documentation.md](../../docs/contributing/documentation.md).
For the per-variable defaults that this code consumes see [default.env](../../default.env).
## Purpose 🎯
The package converts the committed [default.env](../../default.env) plus a small runtime context into the gitignored `.env` at the repo root.
Every module here belongs to one of four roles: parse, resolve, orchestrate, write.
## Structure 🗂️
| File | Role |
|---|---|
| `parser.py` | Read [default.env](../../default.env) into `(values, comments)` tuples. |
| `runtime.py` | Resolve host-context lookups (disk, RAM, hostname, GHA/Act flags, `/proc/version`, helper-script invocation). |
| `builder.py` | Thin orchestrator that defines `EnvBuilder`, `BuildContext`, and `build_env()`. Walks the handler registry. |
| `writer.py` | Serialise an `EnvBuilder` into a docker-compose-compatible `.env` on disk. |
| [`handlers/`](handlers/) | One module per dynamically-computed variable. See [handlers README](handlers/README.md). |
The CLI entry point lives at [__main__.py](../../cli/meta/env/__main__.py) under `cli/meta/env/`.
Shell consumers source [load.sh](../../scripts/meta/env/load.sh) under `scripts/meta/env/`.
## File Naming 🏷️
- Module names MUST stay lowercase snake_case.
- Pure passthrough or simple parser/writer helpers MUST live directly under `utils/env/`.
- Per-variable computation MUST live under [`handlers/`](handlers/) (see [handlers README](handlers/README.md) for the naming convention used there).
- Test files live in `tests/unit/python/utils/env/` and mirror the module name with a `test_` prefix.
## Machine Facts vs Run Facts 🧭
The writer a handler picks decides how long a value lives. Both kinds are registered in `default.env` and both are visible to the lints; they differ only in whether the generated `.env` remembers what the environment said.
| Writer | Meaning | On the next `make dotenv` |
|---|---|---|
| `eb.setdefault` | machine fact | a non-empty value in the process env is adopted and written back |
| `eb.set` | run fact | recomputed from `BuildContext`; whatever the process env said is discarded |
`setdefault` is the operator-override contract: exporting a key changes it and the change persists. That persistence is why it MUST NOT carry a value an environment declares for a single run. [Makefile](../../Makefile) exports `BASH_ENV`, so every make recipe sources `.env` — a value adopted once is re-exported into every later recipe and into every container that mounts the repo, and a per-run decision has silently become a property of the machine.
A key that some environment declares per run therefore MUST be written by a dedicated handler through `eb.set`. The exporting shell still wins for its own run, because [load.sh](../../scripts/meta/env/load.sh) preserves non-empty caller values when it sources `.env`; the file keeps the derived default. `INFINITO_RUNNING_ON_GITHUB`, `INFINITO_CACHE_STACK` and `INFINITO_GHCR_MIRROR` are written this way. Note the consequence: for those keys `.env` and the running shell can legitimately disagree, and `.env` is not the authority.
## Import Rules 🔗
- `parser.py`, `runtime.py`, and `writer.py` MUST NOT import from `builder.py` or [`handlers/`](handlers/).
- `builder.py` MUST NOT import individual handler modules; it MUST go through the `ORDERED_HANDLERS` list exposed by `handlers/__init__.py`.
- Handler modules MUST NOT import each other (see [handlers README](handlers/README.md)).