"""Local JSON registry for installed Gitea Runners. Stores connection metadata (host, user, key, labels) on the local machine so that subsequent lifecycle commands only need the runner name. """ from __future__ import annotations import json from datetime import UTC, datetime from pathlib import Path from typing import Any, cast from .exceptions import GRMError from .i18n import _ class RunnerRegistry: """Manages a local JSON file mapping runner names to connection metadata.""" def __init__(self, path: Path | None = None) -> None: self._path = path or Path.home() / ".local" / "share" / "grm" / "runners.json" self._data: dict[str, dict[str, Any]] = self._load() def _load(self) -> dict[str, dict[str, Any]]: if self._path.exists(): try: with open(self._path) as f: data: Any = json.load(f) if isinstance(data, dict): return cast(dict[str, dict[str, Any]], data) except (json.JSONDecodeError, OSError): pass return {} def _save(self) -> None: self._path.parent.mkdir(parents=True, exist_ok=True) with open(self._path, "w") as f: json.dump(self._data, f, indent=2) def add( self, name: str, host: str, user: str, key: str | None = None, gitea_url: str = "", labels: str | None = None, ) -> None: """Register a runner in the local database.""" self._data[name] = { "host": host, "user": user, "key": key, "gitea_url": gitea_url, "labels": labels, "created_at": datetime.now(UTC).isoformat(), } self._save() def get(self, name: str) -> dict[str, Any] | None: """Retrieve runner metadata by name.""" info = self._data.get(name) if info: return dict(info) return None def remove(self, name: str) -> None: """Remove a runner from the local database.""" if name in self._data: del self._data[name] self._save() def list(self) -> dict[str, dict[str, Any]]: """Return a copy of all registered runners.""" return {name: dict(info) for name, info in self._data.items()} def update(self, name: str, **kwargs: str | None) -> None: """Update fields for an existing runner entry.""" if name not in self._data: raise GRMError(_("Runner '{name}' not found in registry.", name=name)) self._data[name].update({k: v for k, v in kwargs.items() if v is not None}) self._save()