Files
open-reuse/open_reuse/registry.py
tegwick 12b5d83091
Some checks failed
ci / validate-registry (push) Has been cancelled
feat(cli): add open-reuse validate and register portfolio integrations
Implement Integration Definition validator CLI with schema and index checks,
pytest suite, and CI workflow. Register open-cmis-tck and issue-core-gitea in
the integration index.

Closes OPEN-WP-0003 and OPEN-WP-0004.
2026-06-24 18:25:13 +02:00

91 lines
2.4 KiB
Python

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