GRM-9: feat: add runner registry for simplified CLI UX

This commit is contained in:
Emil Simeonov
2026-06-19 00:57:26 +02:00
parent a575a89026
commit 4c08606d0c
10 changed files with 869 additions and 244 deletions
+83
View File
@@ -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()