mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 11:46:35 +00:00
With env.development and env/ci.env both gone, env/default.env was the
last inhabitant of the directory. Hoisting it to the repo root removes
the empty-shell indirection: the SPOT is now [default.env](default.env),
sitting next to .env (its generated companion) and other repo-root
configuration artefacts like compose.yml, Dockerfile, and Makefile.
Path updates land in:
- generator: cli/meta/env/__main__.py (REPO_ROOT / "default.env")
- workflow: .github/workflows/test-install-make.yml (awk reads from
default.env, error message updated in lockstep)
- lint tests: test_used_keys_declared.py and test_infinito_prefix.py
switched from globbing env/*.env to targeting default.env directly;
test_default_env_keys_handled.py points at the new path
- drift test: test_inventory_vars_file_spot.py whitelist + STATIC_ENV
- smoke test: tests/integration/meta/env/test_dotenv_generator.py
STATIC_ENV + every docstring/error message
Doc-string and prose references in utils/env/*, scripts/*,
cli/administration/deploy/development/{common,coredns}.py,
Makefile, Dockerfile, and every doc page that mentioned env/default.env
are rewritten in the same pass so future readers see one path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
52 lines
1.8 KiB
Python
52 lines
1.8 KiB
Python
"""Serialise an EnvBuilder to .env on disk.
|
|
|
|
The output format is the docker-compose `.env`-subset that is also
|
|
sourceable from bash (via `set -a; source .env; set +a`). Values that
|
|
contain only "safe" characters are emitted bare; everything else is
|
|
double-quoted with backslash-escaping of `\\` and `"`.
|
|
|
|
Each key is preceded by its documentation comment when the builder
|
|
carries one, mirroring the convention used in `default.env`.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from pathlib import Path
|
|
|
|
from utils.env.builder import EnvBuilder
|
|
|
|
_SAFE_VALUE_RE = re.compile(r"^[A-Za-z0-9_/.@:%+=-]+$")
|
|
|
|
|
|
def quote_for_dotenv(value: str) -> str:
|
|
if value != "" and _SAFE_VALUE_RE.match(value):
|
|
return value
|
|
if "\n" in value or "\r" in value:
|
|
raise ValueError(
|
|
"Multi-line env values cannot be written to .env "
|
|
"(bash `source` and docker-compose `--env-file` both fail "
|
|
"to round-trip them). Collapse to a single line at the "
|
|
f"producer. Offending value: {value!r}"
|
|
)
|
|
escaped = value.replace("\\", "\\\\").replace('"', '\\"')
|
|
return f'"{escaped}"'
|
|
|
|
|
|
def write_dotenv(eb: EnvBuilder, dest: Path) -> None:
|
|
lines: list[str] = [
|
|
"# Generated by `make dotenv` (python -m cli.meta.env) -- do NOT edit.",
|
|
"# Re-run after changes to default.env or after the runtime context",
|
|
"# shifts (distro, CI vs local, fresh checkout).",
|
|
]
|
|
for key in sorted(eb.values):
|
|
lines.append("") # blank separator for readability
|
|
comment = eb.comments.get(key, "").strip()
|
|
if comment:
|
|
lines.append(f"# {comment}")
|
|
lines.append(f"{key}={quote_for_dotenv(eb.values[key])}")
|
|
dest.write_text("\n".join(lines) + "\n", encoding="utf-8")
|