from __future__ import annotations from pathlib import Path from typing import Any import yaml PACKAGE_ROOT = Path(__file__).resolve().parent.parent INDEX_REQUIRED_FIELDS = ( "id", "name", "status", "owner", "reuse_mode", "path", "repo", "upstream", ) def resolve_repo_root(root: Path | None = None) -> Path: if root is not None: return root.resolve() candidate = PACKAGE_ROOT markers = ( candidate / "schemas" / "integration.schema.yaml", candidate / "registry" / "indexes" / "integrations.yaml", ) if all(path.exists() for path in markers): return candidate raise FileNotFoundError( "Could not resolve open-reuse repo root; pass --root explicitly." ) def registry_paths(repo_root: Path) -> dict[str, Path]: registry = repo_root / "registry" return { "registry": registry, "integrations": registry / "integrations", "index": registry / "indexes" / "integrations.yaml", "schema": repo_root / "schemas" / "integration.schema.yaml", } def load_schema(repo_root: Path) -> dict[str, Any]: schema_path = registry_paths(repo_root)["schema"] with schema_path.open(encoding="utf-8") as handle: return yaml.safe_load(handle) def load_yaml(path: Path) -> dict[str, Any]: with path.open(encoding="utf-8") as handle: data = yaml.safe_load(handle) if not isinstance(data, dict): raise ValueError(f"{path}: expected YAML mapping at root") return data def load_index(repo_root: Path) -> dict[str, Any]: index_path = registry_paths(repo_root)["index"] if not index_path.exists(): return {"integrations": []} return load_yaml(index_path) def integration_paths(repo_root: Path, targets: list[Path] | None = None) -> list[Path]: if targets: return [path.resolve() for path in targets] integrations_dir = registry_paths(repo_root)["integrations"] if not integrations_dir.exists(): return [] return sorted( path for path in integrations_dir.glob("*.integration.yaml") if path.is_file() ) def resolve_external_definition( repo_slug: str, relative_path: str, repos_base: Path | None, ) -> Path | None: if repos_base is None: return None candidate = (repos_base / repo_slug / relative_path).resolve() if candidate.is_file(): return candidate return None