#!/usr/bin/env python3 """ generate-compose.py Reads .env and generates docker-compose.override.yml with NAS volumes. No external dependencies required. """ import os import sys import re from pathlib import Path ENV_FILE = Path(".env") OUTPUT_FILE = Path("docker-compose.override.yml") OVERRIDE_HEADER = """\ # Auto-generated by generate-compose.py — do not edit manually. # Re-run the script after changing NAS_* entries in .env. services: backend: volumes: """ def load_env(path: Path) -> dict: env = {} if not path.exists(): print(f"[ERROR] {path} not found. Copy .env.example to .env first.") sys.exit(1) with path.open(encoding="utf-8") as fh: for line in fh: line = line.strip() if not line or line.startswith("#"): continue if "=" not in line: continue key, _, value = line.partition("=") key = key.strip() # Strip inline comments value = value.split("#")[0].strip() env[key] = value return env def parse_nas_entries(env: dict) -> list[tuple[str, str, str]]: """Return list of (key, name, host_path) sorted by key.""" nas_pattern = re.compile(r"^NAS_(\d+)$") entries = [] for key, value in env.items(): match = nas_pattern.match(key) if not match: continue if "|" not in value: print(f"[WARN] {key}={value!r} — expected format 'Name|/host/path', skipping.") continue name, _, host_path = value.partition("|") entries.append((key, name.strip(), host_path.strip())) entries.sort(key=lambda t: int(t[0].split("_")[1])) return entries def validate_paths(entries: list[tuple[str, str, str]]) -> None: for key, name, host_path in entries: if not Path(host_path).exists(): print(f"[WARN] {key} — host path does not exist: {host_path!r}") else: print(f"[OK] {key} — {name!r} → {host_path}") def generate_override(entries: list[tuple[str, str, str]]) -> str: lines = [OVERRIDE_HEADER] for idx, (key, name, host_path) in enumerate(entries, start=1): container_path = f"/nas/nas{idx}" lines.append(f" - {host_path}:{container_path}:ro # {name}") # Pass NAS_X env vars into the container so config.py can read the names lines.append(" environment:") for idx, (key, name, host_path) in enumerate(entries, start=1): lines.append(f" - {key}={name}|/nas/nas{idx}") lines.append("") return "\n".join(lines) def main() -> None: print("=" * 55) print(" MediaTracker — generate-compose.py") print("=" * 55) env = load_env(ENV_FILE) entries = parse_nas_entries(env) if not entries: print("[ERROR] No NAS_* entries found in .env. Add at least one NAS.") sys.exit(1) print(f"\nFound {len(entries)} NAS source(s):\n") validate_paths(entries) content = generate_override(entries) OUTPUT_FILE.write_text(content, encoding="utf-8") print(f"\n[DONE] {OUTPUT_FILE} generated with {len(entries)} volume(s).") print("\nNext step:") print(" docker compose -f docker-compose.yml -f docker-compose.override.yml up -d --build\n") if __name__ == "__main__": main()