114 lines
4.1 KiB
Python
114 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Load and validate every available GovOPlaN module manifest from source."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import importlib
|
|
import json
|
|
import sys
|
|
import tomllib
|
|
from pathlib import Path
|
|
|
|
|
|
META_ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument(
|
|
"--workspace-root",
|
|
type=Path,
|
|
default=None,
|
|
help="Directory containing the GovOPlaN repositories. Defaults to repositories.json default_parent.",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
catalog = json.loads((META_ROOT / "repositories.json").read_text(encoding="utf-8"))
|
|
workspace_root = (args.workspace_root or Path(catalog["default_parent"])).resolve()
|
|
repositories = tuple(catalog["repositories"])
|
|
|
|
source_roots: list[Path] = []
|
|
manifest_sources: list[tuple[str, Path, Path]] = []
|
|
for repository in repositories:
|
|
repository_root = workspace_root / repository["path"]
|
|
source_root = repository_root / "src"
|
|
if not source_root.is_dir():
|
|
continue
|
|
source_roots.append(source_root)
|
|
manifest_sources.extend(
|
|
(repository["name"], source_root, manifest_path)
|
|
for manifest_path in sorted(source_root.glob("*/backend/manifest.py"))
|
|
)
|
|
|
|
if not manifest_sources:
|
|
print(f"No module manifests found below {workspace_root}.", file=sys.stderr)
|
|
return 1
|
|
|
|
core_source = workspace_root / "govoplan-core" / "src"
|
|
ordered_source_roots = [core_source, *(root for root in source_roots if root != core_source)]
|
|
sys.path[:0] = [str(root) for root in ordered_source_roots]
|
|
|
|
from govoplan_core.core.modules import ModuleManifest # noqa: PLC0415
|
|
from govoplan_core.core.registry import PlatformRegistry, RegistryError # noqa: PLC0415
|
|
|
|
manifests: list[ModuleManifest] = []
|
|
errors: list[str] = []
|
|
for repository_name, source_root, manifest_path in manifest_sources:
|
|
module_name = ".".join(manifest_path.relative_to(source_root).with_suffix("").parts)
|
|
try:
|
|
loaded_module = importlib.import_module(module_name)
|
|
get_manifest = getattr(loaded_module, "get_manifest")
|
|
manifest = get_manifest()
|
|
except Exception as exc: # pragma: no cover - rendered as a check failure
|
|
errors.append(f"{repository_name}: could not load {module_name}: {exc}")
|
|
continue
|
|
|
|
if not isinstance(manifest, ModuleManifest):
|
|
errors.append(f"{repository_name}: {module_name}.get_manifest() did not return ModuleManifest")
|
|
continue
|
|
|
|
entry_points = _module_entry_points(manifest_path.parents[3] / "pyproject.toml")
|
|
expected_target = f"{module_name}:get_manifest"
|
|
actual_target = entry_points.get(manifest.id)
|
|
if actual_target != expected_target:
|
|
declared = ", ".join(f"{name}={target}" for name, target in sorted(entry_points.items())) or "none"
|
|
errors.append(
|
|
f"{repository_name}: module {manifest.id!r} must declare entry point "
|
|
f"{manifest.id}={expected_target}; found {declared}"
|
|
)
|
|
continue
|
|
|
|
manifests.append(manifest)
|
|
|
|
if errors:
|
|
print("\n".join(errors), file=sys.stderr)
|
|
return 1
|
|
|
|
registry = PlatformRegistry()
|
|
try:
|
|
for manifest in manifests:
|
|
registry.register(manifest)
|
|
snapshot = registry.validate()
|
|
except RegistryError as exc:
|
|
print(f"Manifest registry validation failed: {exc}", file=sys.stderr)
|
|
return 1
|
|
|
|
print(
|
|
f"Manifest registry check passed: {len(snapshot.manifests)} manifests "
|
|
f"from {len(manifest_sources)} source files."
|
|
)
|
|
return 0
|
|
|
|
|
|
def _module_entry_points(pyproject_path: Path) -> dict[str, str]:
|
|
if not pyproject_path.is_file():
|
|
return {}
|
|
pyproject = tomllib.loads(pyproject_path.read_text(encoding="utf-8"))
|
|
values = pyproject.get("project", {}).get("entry-points", {}).get("govoplan.modules", {})
|
|
return {str(name): str(target) for name, target in values.items()}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|