import logging import os import re from pathlib import Path logger = logging.getLogger(__name__) _ENV_VAR_RE = re.compile(r"\$\{([^}]+)\}") def resolve_env_vars(value: str) -> str: """Replace ${VAR} patterns with environment variable values. Non-recursive: if ${A} expands to a literal ${B}, ${B} is NOT resolved. Unknown variables are replaced with empty string and a warning is logged. """ if not isinstance(value, str): return value def _replacer(match: re.Match) -> str: var_name = match.group(1) env_value = os.environ.get(var_name) if env_value is None: logger.warning("Environment variable %s is not set, using empty string", var_name) return "" return env_value return _ENV_VAR_RE.sub(_replacer, value) def ensure_config_dir(path: Path | None = None) -> Path: """Create ~/.config/redmine-reporter/ with 0o700 permissions. Returns the path to the config directory. No-op if the directory already exists. """ if path is None: path = Path.home() / ".config" / "redmine-reporter" path.mkdir(mode=0o700, parents=True, exist_ok=True) return path def check_file_permissions(path: Path) -> list[str]: """Check that a config file has safe permissions (0o600 or stricter). Returns a list of warning messages. Empty list means no issues. """ warnings: list[str] = [] if not path.exists(): return warnings mode = path.stat().st_mode & 0o777 if mode > 0o600: warnings.append( f"Config file permissions are too open ({mode:04o}). " f"Expected 0600. Fix with: chmod 600 {path}" ) return warnings def expand_filename_template( template: str, *, author: str = "", from_date: str = "", to_date: str = "", ext: str = "", ) -> str: """Expand placeholders in a filename template. Supported placeholders: {author} — author name {from} — start date (YYYY-MM-DD) {to} — end date (YYYY-MM-DD) {date} — end date formatted as DD_MM_YYYY {ext} — file extension without dot Unknown placeholders are left as-is. """ date_dd_mm_yyyy = "" if to_date: try: from datetime import datetime dt = datetime.strptime(to_date, "%Y-%m-%d") date_dd_mm_yyyy = dt.strftime("%d_%m_%Y") except ValueError: date_dd_mm_yyyy = to_date replacements = { "author": author.replace(" ", "_"), "from": from_date, "to": to_date, "date": date_dd_mm_yyyy, "ext": ext, } result = template for key, value in replacements.items(): result = result.replace("{" + key + "}", value) return result