Sanitized single-commit public mirror of recipe-maintainer. - Removed test-ssh/.testenv (live creds); added test-ssh/.testenv.example placeholders. - Removed plans/ and planned-updates/ (deployment-planning docs) so no client/ deployment domains appear in the public repo. - All other secret stores were already gitignored. - docs.coopcloud.tech retained as a submodule (public upstream).
84 lines
2.5 KiB
Python
84 lines
2.5 KiB
Python
"""Read and write abra .env files and recipe-info config files.
|
|
|
|
Handles the specific format of abra .env files: KEY=VALUE lines,
|
|
with support for comments (#) and uncommenting keys.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
def read_env_file(path: str | Path) -> dict[str, str]:
|
|
"""Read a KEY=VALUE env file into a dict.
|
|
|
|
Ignores empty lines and comments (#).
|
|
"""
|
|
result = {}
|
|
p = Path(path)
|
|
if not p.exists():
|
|
return result
|
|
with open(p) as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if not line or line.startswith("#"):
|
|
continue
|
|
if "=" in line:
|
|
key, value = line.split("=", 1)
|
|
result[key.strip()] = value.strip()
|
|
return result
|
|
|
|
|
|
def write_env_file(path: str | Path, data: dict[str, str]) -> None:
|
|
"""Write a dict as a KEY=VALUE env file."""
|
|
p = Path(path)
|
|
p.parent.mkdir(parents=True, exist_ok=True)
|
|
with open(p, "w") as f:
|
|
for key, value in data.items():
|
|
f.write(f"{key}={value}\n")
|
|
|
|
|
|
def apply_env_overrides(env_path: str | Path,
|
|
overrides: dict[str, str]) -> None:
|
|
"""Set or uncomment values in an abra .env file.
|
|
|
|
For each key in overrides:
|
|
- If the key exists (even commented out), replace the line
|
|
- If the key doesn't exist, append it
|
|
"""
|
|
p = Path(env_path)
|
|
if not p.exists():
|
|
write_env_file(p, overrides)
|
|
return
|
|
|
|
with open(p) as f:
|
|
lines = f.readlines()
|
|
|
|
remaining = dict(overrides)
|
|
new_lines = []
|
|
for line in lines:
|
|
stripped = line.strip()
|
|
matched = False
|
|
for key, value in list(remaining.items()):
|
|
# Match "KEY=...", "#KEY=...", or "# KEY=..."
|
|
if stripped.lstrip("#").strip().startswith(f"{key}="):
|
|
new_lines.append(f"{key}={value}\n")
|
|
remaining.pop(key)
|
|
matched = True
|
|
print(f" env: {key}={value}", flush=True)
|
|
break
|
|
if not matched:
|
|
new_lines.append(line)
|
|
|
|
# Append any keys that weren't found in the file
|
|
for key, value in remaining.items():
|
|
new_lines.append(f"{key}={value}\n")
|
|
print(f" env: {key}={value} (appended)", flush=True)
|
|
|
|
with open(p, "w") as f:
|
|
f.writelines(new_lines)
|
|
|
|
|
|
def get_abra_env_path(server: str, domain: str) -> Path:
|
|
"""Return the path to an abra app's .env file."""
|
|
from lib.config import paths
|
|
return paths.ABRA_SERVERS_DIR / server / f"{domain}.env"
|