from __future__ import annotations import argparse import os import sys from pathlib import Path from installer.core.controller import ProleController from installer.core.actions import ProleSilentInstaller def _project_root() -> Path: env_home = (os.environ.get("PROLE_HOME") or "").strip() if env_home: try: return Path(env_home).expanduser().resolve() except Exception: pass return Path(__file__).resolve().parents[2] def _normalize_cfg_path(raw: str | None) -> str | None: if not raw: return None s = str(raw).strip() if not s: return None try: p = Path(s).expanduser() if p.is_dir(): return str((p / "prole.cfg").resolve()) return str(p.resolve()) except Exception: return s def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(prog="repair_pipeline") parser.add_argument( "-n", "--namespace", dest="service_namespace", default="", help="Service namespace (common-core namespace)", ) parser.add_argument( "--db-namespace", dest="db_namespace", default="", help="Database namespace", ) parser.add_argument( "-m", "--mode", dest="mode", default="", help="Deployment mode: k3s, k3d, k8s", ) parser.add_argument( "--config", dest="config", default="", help="Path to prole.cfg (or directory containing it)", ) parser.add_argument("--verbose", action="store_true") args = parser.parse_args(argv) mode = (args.mode or "").strip() if mode and mode not in ("k3s", "k3d", "k8s"): print(f"[ERROR] Invalid --mode {mode!r} (expected k3s|k3d|k8s)", file=sys.stderr) return 2 root = _project_root() cfg = _normalize_cfg_path(args.config) if not cfg: env_conf = (os.environ.get("PROLE_CONF") or "").strip() if env_conf: cfg = _normalize_cfg_path(env_conf) if not cfg: cfg = str((root / "conf" / "prole.cfg").resolve()) controller = ProleController(project_root=root, verbose=bool(args.verbose), cfg_path=Path(cfg)) installer = ProleSilentInstaller(controller, cfg_path=cfg) try: existing_inputs = installer._load_inputs_from_cfg() except Exception: existing_inputs = {} installer.inputs = {**installer._default_inputs(), **existing_inputs} # Ensure mode influences the env we build (KUBECONFIG resolution, etc.). if mode == "k3s": installer.inputs["init_cluster.cluster_env"] = "service" elif mode == "k3d": installer.inputs["init_cluster.cluster_env"] = "dev" elif mode == "k8s": installer.inputs["init_cluster.cluster_env"] = "prod" service_ns = (args.service_namespace or "").strip() or installer._service_namespace() db_ns = ( (args.db_namespace or "").strip() or (installer._get_input("init_password.db_namespace", "") or "").strip() or service_ns or "default" ) if args.db_namespace: installer.inputs["init_password.db_namespace"] = db_ns env = installer._script_env_for_namespace(service_ns) env["SERVICE_NAMESPACE"] = service_ns env["DB_NAMESPACE"] = db_ns env["NAMESPACE"] = service_ns anomalies: list[str] = [] if installer._dashboard_kong_missing(env): anomalies.append("dashboard") if installer._authority_context_missing(): anomalies.append("authority") installer._repair_stale_released_pvs(env) installer._run_cluster_repair_pipeline(env=env, anomalies=anomalies) return 0 if __name__ == "__main__": raise SystemExit(main())