"""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"