GRM-9: feat: add runner registry for simplified CLI UX
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
"""Local JSON registry for installed Gitea Runners.
|
||||
|
||||
Stores connection metadata (host, user, key, mode) 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,
|
||||
mode: str = "docker",
|
||||
gitea_url: str = "",
|
||||
) -> None:
|
||||
"""Register a runner in the local database."""
|
||||
self._data[name] = {
|
||||
"host": host,
|
||||
"user": user,
|
||||
"key": key,
|
||||
"mode": mode,
|
||||
"gitea_url": gitea_url,
|
||||
"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()
|
||||
Reference in New Issue
Block a user