"""Core logic for managing Gitea runners.""" from __future__ import annotations import os import subprocess from pathlib import Path from typing import Any from .api_client import GiteaAPIClient from .exceptions import AnsibleError class RunnerManager: """Orchestrates runner installation and updates.""" def __init__(self, api_client: GiteaAPIClient) -> None: self.api = api_client def list_runners(self) -> list[dict[str, Any]]: """List all registered runners.""" return self.api.get_runners() def generate_token(self) -> str: """Generate a new registration token.""" return self.api.create_registration_token() def install( self, host: str, user: str, key: str | None = None, name: str | None = None, token: str | None = None, ) -> None: """Install a runner on a remote host using Ansible.""" if not name: name = host if not token: token = self.generate_token() playbook = Path(__file__).parent.parent.parent / "ansible" / "install-runner.yml" if not playbook.exists(): raise AnsibleError(f"Playbook not found: {playbook}") cmd = [ "ansible-playbook", str(playbook), "-i", f"{host},", "-u", user, "--extra-vars", f"registration_token={token} runner_name={name} gitea_url={self.api.base_url}", ] if key: cmd.extend(["--private-key", key]) self._run_ansible(cmd) def update( self, host: str, user: str, key: str | None = None, version: str | None = None, ) -> None: """Update the act_runner binary on a remote host.""" playbook = Path(__file__).parent.parent.parent / "ansible" / "update-runner.yml" if not playbook.exists(): raise AnsibleError(f"Playbook not found: {playbook}") cmd = [ "ansible-playbook", str(playbook), "-i", f"{host},", "-u", user, ] if key: cmd.extend(["--private-key", key]) if version: cmd.extend(["--extra-vars", f"act_runner_version={version}"]) self._run_ansible(cmd) def _run_ansible(self, cmd: list[str]) -> None: """Execute an Ansible command, streaming output.""" env = os.environ.copy() result = subprocess.run(cmd, env=env, check=False) # noqa: S603 if result.returncode != 0: raise AnsibleError(f"Ansible failed with exit code {result.returncode}")