GRM-19: refactor: resolve_runner returns gitea_url, add --url CLI option, force remove improvements, code quality fixes

- _resolve_runner now returns gitea_url from registry so disable/remove
  can reuse the URL stored at install time without requiring env vars.
- Added --url option to install, disable, and remove CLI commands.
- remove(force=True) no longer requires gitea_url or token.
- Moved _parse_status outside the for loop in list_runners.
- Updated all translations and tests to match.
This commit is contained in:
Emil Simeonov
2026-06-19 03:12:04 +02:00
parent a9adb71a08
commit 3d943b57dc
5 changed files with 214 additions and 37 deletions
+21 -3
View File
@@ -76,6 +76,11 @@ def cli() -> None:
default="docker",
help=_("Gitea Runner deployment mode (default: docker)"),
)
@click.option(
"--url",
default=lambda: os.getenv("GITEA_URL", ""),
help=_("Gitea URL (env: GITEA_URL)"),
)
@click.option(
"--admin-token",
"-a",
@@ -101,6 +106,7 @@ def install(
name: str | None,
token: str | None,
mode: str,
url: str,
admin_token: str | None,
integration_retries: int,
ask_become_pass: bool,
@@ -113,7 +119,7 @@ def install(
key=key,
name=name,
token=token,
gitea_url=os.getenv("GITEA_URL", ""),
gitea_url=url,
mode=mode,
admin_token=admin_token,
integration_retries=integration_retries,
@@ -251,6 +257,11 @@ def enable(
type=click.Choice(["docker", "binary"]),
help=_("Override deployment mode from registry"),
)
@click.option(
"--url",
default=lambda: os.getenv("GITEA_URL", ""),
help=_("Gitea URL (env: GITEA_URL)"),
)
@_handle_errors("Disable failed: {error}")
def disable(
runner_name: str,
@@ -259,6 +270,7 @@ def disable(
key: str | None,
token: str | None,
mode: str | None,
url: str,
ask_become_pass: bool,
) -> None:
manager = RunnerManager()
@@ -268,7 +280,7 @@ def disable(
user=user,
key=key,
token=token,
gitea_url=os.getenv("GITEA_URL", ""),
gitea_url=url,
mode=mode,
ask_become_pass=ask_become_pass,
)
@@ -318,6 +330,11 @@ def status(
type=click.Choice(["docker", "binary"]),
help=_("Override deployment mode from registry"),
)
@click.option(
"--url",
default=lambda: os.getenv("GITEA_URL", ""),
help=_("Gitea URL (env: GITEA_URL)"),
)
@click.option(
"--force",
"-f",
@@ -332,6 +349,7 @@ def remove(
key: str | None,
token: str | None,
mode: str | None,
url: str,
force: bool,
ask_become_pass: bool,
) -> None:
@@ -342,7 +360,7 @@ def remove(
user=user,
key=key,
token=token,
gitea_url=os.getenv("GITEA_URL", ""),
gitea_url=url,
mode=mode,
ask_become_pass=ask_become_pass,
force=force,
+7
View File
@@ -114,6 +114,13 @@ TRANSLATIONS: dict[str, dict[str, str]] = {
"ru": "Токен регистрации (env: GITEA_REGISTRATION_TOKEN)",
"zh": "注册令牌(环境变量: GITEA_REGISTRATION_TOKEN",
},
"Gitea URL (env: GITEA_URL)": {
"en": "Gitea URL (env: GITEA_URL)",
"bg": "Gitea URL (env: GITEA_URL)",
"de": "Gitea-URL (env: GITEA_URL)",
"ru": "URL Gitea (env: GITEA_URL)",
"zh": "Gitea URL(环境变量: GITEA_URL",
},
"Gitea Runner deployment mode (default: docker)": {
"en": "Gitea Runner deployment mode (default: docker)",
"bg": "Режим на разполагане на Gitea Runner (по подразбиране: docker)",
+45 -24
View File
@@ -96,9 +96,15 @@ class RunnerManager:
user: str | None = None,
key: str | None = None,
mode: str | None = None,
) -> tuple[str, str, str | None, str]:
"""Look up runner metadata from registry, applying CLI overrides."""
) -> tuple[str, str, str | None, str, str]:
"""Look up runner metadata from registry, applying CLI overrides.
Returns ``(host, user, key, mode, gitea_url)`` where *gitea_url* is
taken from the registry when available, allowing ``disable`` and
``remove`` to reuse the value stored at install time.
"""
info = self._registry.get(name)
actual_gitea_url = info.get("gitea_url", "") if info else ""
if host and user:
# Explicit connection details — bypass registry
actual_host = host
@@ -117,7 +123,7 @@ class RunnerManager:
name=name,
)
)
return actual_host, actual_user, actual_key, actual_mode
return actual_host, actual_user, actual_key, actual_mode, actual_gitea_url
def start(
self,
@@ -129,7 +135,9 @@ class RunnerManager:
ask_become_pass: bool = False,
) -> None:
"""Start a runner instance on a remote host."""
actual_host, actual_user, actual_key, actual_mode = self._resolve_runner(name, host, user, key, mode)
actual_host, actual_user, actual_key, actual_mode, _gitea_url = self._resolve_runner(
name, host, user, key, mode
)
with track_steps() as tracker:
tracker.begin(_("Starting Gitea Runner {name} on {host}", name=name, host=actual_host))
extra_vars = f"runner_name={name} runner_mode={actual_mode}"
@@ -148,7 +156,7 @@ class RunnerManager:
ask_become_pass: bool = False,
) -> None:
"""Stop a runner instance on a remote host."""
actual_host, actual_user, actual_key, _mode = self._resolve_runner(name, host, user, key)
actual_host, actual_user, actual_key, _mode, _gitea_url = self._resolve_runner(name, host, user, key)
with track_steps() as tracker:
tracker.begin(_("Stopping Gitea Runner {name} on {host}", name=name, host=actual_host))
extra_vars = f"runner_name={name}"
@@ -167,7 +175,7 @@ class RunnerManager:
ask_become_pass: bool = False,
) -> None:
"""Enable a runner instance to start on boot."""
actual_host, actual_user, actual_key, _mode = self._resolve_runner(name, host, user, key)
actual_host, actual_user, actual_key, _mode, _gitea_url = self._resolve_runner(name, host, user, key)
with track_steps() as tracker:
tracker.begin(_("Enabling Gitea Runner {name} on {host}", name=name, host=actual_host))
extra_vars = f"runner_name={name}"
@@ -191,15 +199,19 @@ class RunnerManager:
ask_become_pass: bool = False,
) -> None:
"""Disable and deregister a runner instance."""
if not gitea_url:
actual_host, actual_user, actual_key, actual_mode, registry_gitea_url = self._resolve_runner(
name, host, user, key, mode
)
resolved_gitea_url = gitea_url or registry_gitea_url
if not resolved_gitea_url:
raise AnsibleError(_("GITEA_URL must be set (or pass --url)"))
actual_host, actual_user, actual_key, actual_mode = self._resolve_runner(name, host, user, key, mode)
if not token:
raise AnsibleError(_("GITEA_REGISTRATION_TOKEN must be set (or pass --token)"))
with track_steps() as tracker:
tracker.begin(_("Disabling Gitea Runner {name} on {host}", name=name, host=actual_host))
extra_vars = (
f"runner_name={name} registration_token={token} gitea_url={gitea_url} runner_mode={actual_mode}"
f"runner_name={name} registration_token={token}"
f" gitea_url={resolved_gitea_url} runner_mode={actual_mode}"
)
cmd = self._build_cmd(
"disable-runner.yml", actual_host, actual_user, extra_vars, actual_key, ask_become_pass
@@ -219,7 +231,9 @@ class RunnerManager:
ask_become_pass: bool = False,
) -> None:
"""Check the status of a runner instance."""
actual_host, actual_user, actual_key, actual_mode = self._resolve_runner(name, host, user, key, mode)
actual_host, actual_user, actual_key, actual_mode, _gitea_url = self._resolve_runner(
name, host, user, key, mode
)
with track_steps() as tracker:
tracker.begin(_("Checking status of Gitea Runner {name} on {host}", name=name, host=actual_host))
extra_vars = f"runner_name={name} runner_mode={actual_mode}"
@@ -249,16 +263,22 @@ class RunnerManager:
only remove the local registry entry. Use this when the remote
host is already gone or unreachable.
"""
if not gitea_url:
raise AnsibleError(_("GITEA_URL must be set (or pass --url)"))
actual_host, actual_user, actual_key, actual_mode = self._resolve_runner(name, host, user, key, mode)
if not token:
raise AnsibleError(_("GITEA_REGISTRATION_TOKEN must be set (or pass --token)"))
actual_host, actual_user, actual_key, actual_mode, registry_gitea_url = self._resolve_runner(
name, host, user, key, mode
)
resolved_gitea_url = gitea_url or registry_gitea_url
resolved_token = token
if not force:
if not resolved_gitea_url:
raise AnsibleError(_("GITEA_URL must be set (or pass --url)"))
if not resolved_token:
raise AnsibleError(_("GITEA_REGISTRATION_TOKEN must be set (or pass --token)"))
with track_steps() as tracker:
tracker.begin(_("Removing Gitea Runner {name} from {host}", name=name, host=actual_host))
if not force:
extra_vars = (
f"runner_name={name} registration_token={token} gitea_url={gitea_url} runner_mode={actual_mode}"
f"runner_name={name} registration_token={resolved_token}"
f" gitea_url={resolved_gitea_url} runner_mode={actual_mode}"
)
cmd = self._build_cmd(
"remove-runner.yml", actual_host, actual_user, extra_vars, actual_key, ask_become_pass
@@ -290,11 +310,6 @@ class RunnerManager:
)
)
def _parse_status(stdout: str) -> str:
ansible_noise = (" | CHANGED | ", " | FAILED | ", " | UNREACHABLE | ", "[WARNING]", "ssh:", ">>")
lines = [ln for ln in stdout.splitlines() if ln.strip() and not any(p in ln for p in ansible_noise)]
return lines[-1].strip() if lines else "unknown"
service_status = "unknown"
if mode == "docker":
# Try expected container name first, then host-based fallback for legacy installs.
@@ -317,7 +332,7 @@ class RunnerManager:
)
except Exception:
continue
status = _parse_status(stdout)
status = self._parse_status(stdout)
if status != "unknown":
docker_map = {"running": "active", "exited": "inactive", "dead": "failed"}
service_status = docker_map.get(status, "unknown")
@@ -335,7 +350,7 @@ class RunnerManager:
ask_become_pass=True,
check=False,
)
service_status = _parse_status(stdout)
service_status = self._parse_status(stdout)
except Exception:
service_status = "unknown"
else:
@@ -350,7 +365,7 @@ class RunnerManager:
ask_become_pass=True,
check=False,
)
service_status = _parse_status(stdout)
service_status = self._parse_status(stdout)
except Exception:
service_status = "unknown"
# Translate known status values
@@ -368,6 +383,12 @@ class RunnerManager:
)
return result
@staticmethod
def _parse_status(stdout: str) -> str:
ansible_noise = (" | CHANGED | ", " | FAILED | ", " | UNREACHABLE | ", "[WARNING]", "ssh:", ">>")
lines = [ln for ln in stdout.splitlines() if ln.strip() and not any(p in ln for p in ansible_noise)]
return lines[-1].strip() if lines else "unknown"
def _build_cmd(
self,
playbook_name: str,