#!/usr/bin/env python3 from __future__ import annotations import argparse import re from pathlib import Path ENV_ASSIGNMENT_RE = re.compile( r"^(\s*(?:export\s+)?)([A-Za-z_][A-Za-z0-9_]*)(\s*=\s*)(.*)$" ) def split_inline_comment(rhs: str) -> tuple[str, str]: in_single = False in_double = False escaped = False for i, char in enumerate(rhs): if escaped: escaped = False continue if char == "\\": escaped = True continue if char == "'" and not in_double: in_single = not in_single continue if char == '"' and not in_single: in_double = not in_double continue if char == "#" and not in_single and not in_double: if i == 0 or rhs[i - 1].isspace(): return rhs[:i], rhs[i:] return rhs, "" def transform_line(line: str) -> str: stripped = line.strip() if stripped == "" or stripped.startswith("#"): return line newline = "" raw = line if line.endswith("\r\n"): newline = "\r\n" raw = line[:-2] elif line.endswith("\n"): newline = "\n" raw = line[:-1] match = ENV_ASSIGNMENT_RE.match(raw) if not match: return line prefix, key, delimiter, rhs = match.groups() value_part, comment_part = split_inline_comment(rhs) trailing_ws = value_part[len(value_part.rstrip()) :] placeholder = f"${{{{project.{key}}}}}" return f"{prefix}{key}{delimiter}{placeholder}{trailing_ws}{comment_part}{newline}" def generate_example_env(source_path: Path, target_path: Path) -> None: lines = source_path.read_text(encoding="utf-8").splitlines(keepends=True) transformed = [transform_line(line) for line in lines] target_path.write_text("".join(transformed), encoding="utf-8") def main() -> None: parser = argparse.ArgumentParser( description="Generate .env.example using ${{project.KEY}} placeholders." ) parser.add_argument("--source", default=".env", help="Path to source .env file") parser.add_argument( "--target", default=".env.example", help="Path to output .env.example file" ) args = parser.parse_args() source_path = Path(args.source) target_path = Path(args.target) if not source_path.exists(): raise FileNotFoundError(f"Source env file not found: {source_path}") generate_example_env(source_path, target_path) if __name__ == "__main__": main()