#!/usr/bin/env python3 """Shared Gitea configuration helpers for issue scripts.""" from __future__ import annotations import os import re import shlex import subprocess from pathlib import Path from urllib.parse import urlparse DEFAULT_ENV_FILE = Path("~/.config/gitea/gitea.env").expanduser() SECRETS_ENV_FILE = Path("~/.config/secrets/gitea.env").expanduser() LEGACY_ENV_FILE = Path("~/.config/gitea/data-analysis-agent.env").expanduser() REQUIRED_ENV = ("GITEA_BASE_URL", "GITEA_OWNER", "GITEA_REPO", "GITEA_TOKEN") def load_env_file(path: Path) -> dict[str, str]: values: dict[str, str] = {} if not path.exists(): return values for raw_line in path.read_text(encoding="utf-8").splitlines(): line = raw_line.strip() if not line or line.startswith("#"): continue if line.startswith("export "): line = line[len("export ") :] if "=" not in line: continue key, raw_value = line.split("=", 1) key = key.strip() try: parsed = shlex.split(raw_value.strip(), comments=False, posix=True) except ValueError as exc: raise SystemExit(f"Could not parse {path}: {exc}") from exc values[key] = parsed[0] if parsed else "" return values def discover_origin_config() -> dict[str, str]: try: remote = subprocess.run( ["git", "remote", "get-url", "origin"], capture_output=True, text=True, check=True, ).stdout.strip() except (subprocess.CalledProcessError, FileNotFoundError): return {} if not remote: return {} parsed = urlparse(remote) host = "" path = "" if parsed.scheme in {"http", "https", "ssh"} and parsed.netloc: host = parsed.hostname or parsed.netloc.split("@")[-1].split(":")[0] path = parsed.path.lstrip("/") else: match = re.match(r"(?:(?P[^@]+)@)?(?P[^:]+):(?P.+)$", remote) if match: host = match.group("host") path = match.group("path") if not host or not path: return {} if path.endswith(".git"): path = path[:-4] parts = [part for part in path.split("/") if part] if len(parts) < 2: return {} return { "GITEA_BASE_URL": f"https://{host}", "GITEA_OWNER": parts[-2], "GITEA_REPO": parts[-1], } def config(env_file: Path | None = None) -> dict[str, str]: env_path = (env_file or DEFAULT_ENV_FILE).expanduser() file_values = load_env_file(env_path) if env_path == DEFAULT_ENV_FILE: secrets_values = load_env_file(SECRETS_ENV_FILE) legacy_values = load_env_file(LEGACY_ENV_FILE) file_values = {**legacy_values, **secrets_values, **file_values} origin_values = discover_origin_config() base_url = ( os.environ.get("GITEA_BASE_URL") or os.environ.get("GITEA_URL") or origin_values.get("GITEA_BASE_URL") or file_values.get("GITEA_BASE_URL") or file_values.get("GITEA_URL", "") ) values = { "GITEA_BASE_URL": base_url, "GITEA_OWNER": os.environ.get("GITEA_OWNER") or origin_values.get("GITEA_OWNER") or file_values.get("GITEA_OWNER", ""), "GITEA_REPO": os.environ.get("GITEA_REPO") or origin_values.get("GITEA_REPO") or file_values.get("GITEA_REPO", ""), "GITEA_TOKEN": os.environ.get("GITEA_TOKEN") or file_values.get("GITEA_TOKEN", ""), } missing = [key for key, value in values.items() if not value] if missing: raise SystemExit( "Missing required Gitea configuration: " + ", ".join(missing) + f"\nSet them in the shell, {env_path}, {SECRETS_ENV_FILE}, " + "or run from a git repo with a Gitea origin." ) return values def auth_config(env_file: Path | None = None) -> dict[str, str]: """Return Gitea API auth config without requiring a repository context.""" env_path = (env_file or DEFAULT_ENV_FILE).expanduser() file_values = load_env_file(env_path) if env_path == DEFAULT_ENV_FILE: secrets_values = load_env_file(SECRETS_ENV_FILE) legacy_values = load_env_file(LEGACY_ENV_FILE) file_values = {**legacy_values, **secrets_values, **file_values} origin_values = discover_origin_config() base_url = ( os.environ.get("GITEA_BASE_URL") or os.environ.get("GITEA_URL") or origin_values.get("GITEA_BASE_URL") or file_values.get("GITEA_BASE_URL") or file_values.get("GITEA_URL", "") ) values = { "GITEA_BASE_URL": base_url, "GITEA_OWNER": os.environ.get("GITEA_OWNER") or origin_values.get("GITEA_OWNER") or file_values.get("GITEA_OWNER", ""), "GITEA_TOKEN": os.environ.get("GITEA_TOKEN") or file_values.get("GITEA_TOKEN", ""), } missing = [key for key in ("GITEA_BASE_URL", "GITEA_TOKEN") if not values[key]] if missing: raise SystemExit( "Missing required Gitea configuration: " + ", ".join(missing) + f"\nSet them in the shell, {env_path}, {SECRETS_ENV_FILE}, " + "or run from a git repo with a Gitea origin." ) return values