GRM-18: feat: make --ask-become-pass the default behavior

- Change --ask-become-pass from opt-in to opt-out across all commands
  (install, update, start, stop, enable, disable, status, remove)
- Use Click toggle pattern: --ask-become-pass/--no-ask-become-pass with
  default=True so users are always prompted for sudo unless they
  explicitly opt out
- Update i18n translations for both help texts
- Update all CLI tests to expect ask_become_pass=True as default and
  add test for --no-ask-become-pass
- Update README: remove --ask-become-pass from examples, document
  --no-ask-become-pass for passwordless-sudo setups
- 120 tests, 100% coverage, pyright clean, ruff clean
This commit is contained in:
Emil Simeonov
2026-06-19 02:11:38 +02:00
parent 18760f6a2a
commit ebb1088a8c
4 changed files with 71 additions and 31 deletions
+10 -10
View File
@@ -26,7 +26,7 @@ By default, runners are deployed as **Docker containers** using the official `gi
## Prerequisites
- **SSH key authentication** — The remote host must be reachable via SSH using the user specified with `--user` and the private key specified with `--key`. GRM uses Ansible under the hood, which connects to the target host over SSH to execute all installation and configuration tasks. Without valid SSH credentials, Ansible cannot establish a connection and the deployment will fail.
- **Sudo access** — GRM requires root privileges on the remote host to install packages, create systemd services, and manage Docker. You will be prompted interactively for the sudo password. For automation or uninterrupted workflows, configure passwordless sudo on the remote host.
- **Sudo access** — GRM requires root privileges on the remote host to install packages, create systemd services, and manage Docker. By default, you will be prompted interactively for the sudo password. For automation or uninterrupted workflows, configure passwordless sudo on the remote host and pass `--no-ask-become-pass`.
## Quick Start
@@ -62,26 +62,26 @@ API checks, if enabled, are purely informational and do not affect pass/fail.
### Install a Runner
Using the CLI (you will be prompted for the sudo password interactively):
Using the CLI (you will be prompted for the sudo password by default):
```bash
# Docker mode (default) — deploys gitea_runner as a container
grm install 192.168.1.10 --user ubuntu --key ~/.ssh/id_ed25519 --name prod-runner --ask-become-pass
grm install 192.168.1.10 --user ubuntu --key ~/.ssh/id_ed25519 --name prod-runner
# Binary mode — downloads and installs the gitea_runner binary with systemd
grm install 192.168.1.10 --user ubuntu --key ~/.ssh/id_ed25519 --name prod-runner --mode binary --ask-become-pass
grm install 192.168.1.10 --user ubuntu --key ~/.ssh/id_ed25519 --name prod-runner --mode binary
```
> **Automation tip:** Configure passwordless sudo on the remote host to skip the password prompt. This is recommended for CI/CD pipelines.
> **Automation tip:** Configure passwordless sudo on the remote host and pass `--no-ask-become-pass` to skip the password prompt. This is recommended for CI/CD pipelines.
Using Make:
```bash
# Docker mode (default)
make install HOST=192.168.1.10 USER=ubuntu KEY=~/.ssh/id_ed25519 NAME=prod-runner ASK_BECOME_PASS=1
make install HOST=192.168.1.10 USER=ubuntu KEY=~/.ssh/id_ed25519 NAME=prod-runner
# Binary mode
make install HOST=192.168.1.10 USER=ubuntu KEY=~/.ssh/id_ed25519 NAME=prod-runner MODE=binary ASK_BECOME_PASS=1
make install HOST=192.168.1.10 USER=ubuntu KEY=~/.ssh/id_ed25519 NAME=prod-runner MODE=binary
```
### Runner Registry
@@ -123,7 +123,7 @@ You can override any stored value by passing the corresponding flag:
grm start prod-runner --host 192.168.1.11 --user root --mode binary
```
> **Automation tip:** If the remote host has passwordless sudo configured, omit `--ask-become-pass`.
> **Automation tip:** If the remote host has passwordless sudo configured, pass `--no-ask-become-pass`.
### Multiple Instances on the Same Host
@@ -131,8 +131,8 @@ Each runner instance is fully isolated with its own data directory and systemd s
```bash
# Install two runners on the same host
grm install 192.168.1.10 --user ubuntu --name workflow-runner --ask-become-pass
grm install 192.168.1.10 --user ubuntu --name build-runner --mode binary --ask-become-pass
grm install 192.168.1.10 --user ubuntu --name workflow-runner
grm install 192.168.1.10 --user ubuntu --name build-runner --mode binary
# Manage them independently by name
grm stop workflow-runner
+15 -3
View File
@@ -20,7 +20,11 @@ load_dotenv(override=True)
def _runner_options(func: Callable[..., Any]) -> Callable[..., Any]:
"""Apply common override options for registry-based lifecycle commands."""
func = click.option("--ask-become-pass", is_flag=True, help=_("Prompt for sudo password"))(func)
func = click.option(
"--ask-become-pass/--no-ask-become-pass",
default=True,
help=_("Prompt for sudo password (default)"),
)(func)
func = click.option("--key", "-k", help=_("Override SSH key from registry"))(func)
func = click.option("--user", "-u", help=_("Override user from registry"))(func)
func = click.option("--host", help=_("Override host from registry"))(func)
@@ -85,7 +89,11 @@ def cli() -> None:
default=lambda: int(os.getenv("GITEA_INTEGRATION_RETRIES", "3")),
help=_("Integration test API retries (default: 3, env: GITEA_INTEGRATION_RETRIES)"),
)
@click.option("--ask-become-pass", is_flag=True, help=_("Prompt for sudo password"))
@click.option(
"--ask-become-pass/--no-ask-become-pass",
default=True,
help=_("Prompt for sudo password (default)"),
)
def install(
host: str,
user: str,
@@ -132,7 +140,11 @@ def install(
default="docker",
help=_("Gitea Runner deployment mode (default: docker)"),
)
@click.option("--ask-become-pass", is_flag=True, help=_("Prompt for sudo password"))
@click.option(
"--ask-become-pass/--no-ask-become-pass",
default=True,
help=_("Prompt for sudo password (default)"),
)
@_handle_errors("Update failed: {error}")
def update(
host: str,
+13 -6
View File
@@ -135,12 +135,19 @@ TRANSLATIONS: dict[str, dict[str, str]] = {
"ru": "Повторы API интеграционного теста (по умолчанию: 3, env: GITEA_INTEGRATION_RETRIES)",
"zh": "集成测试 API 重试次数(默认: 3,环境变量: GITEA_INTEGRATION_RETRIES",
},
"Prompt for sudo password": {
"en": "Prompt for sudo password",
"bg": "Подканване за sudo парола",
"de": "Nach sudo-Passwort fragen",
"ru": "Запросить пароль sudo",
"zh": "提示输入 sudo 密码",
"Prompt for sudo password (default)": {
"en": "Prompt for sudo password (default)",
"bg": "Подканване за sudo парола (по подразбиране)",
"de": "Nach sudo-Passwort fragen (Standard)",
"ru": "Запросить пароль sudo (по умолчанию)",
"zh": "提示输入 sudo 密码(默认)",
},
"Do not prompt for sudo password": {
"en": "Do not prompt for sudo password",
"bg": "Без подканване за sudo парола",
"de": "Nicht nach sudo-Passwort fragen",
"ru": "Не запрашивать пароль sudo",
"zh": "不提示输入 sudo 密码",
},
"Specific Gitea Runner version": {
"en": "Specific Gitea Runner version",
+33 -12
View File
@@ -22,6 +22,27 @@ class TestCLI:
runner = CliRunner(env={"GITEA_URL": "https://git.example.com", "GITEA_ADMIN_TOKEN": ""})
result = runner.invoke(cli, ["install", "host1", "--user", "ubuntu", "--token", "tok"])
assert result.exit_code == 0
mock_manager.install.assert_called_once_with(
host="host1",
user="ubuntu",
key=None,
name=None,
token="tok",
gitea_url="https://git.example.com",
mode="docker",
admin_token="",
integration_retries=3,
ask_become_pass=True,
)
@patch("gitea_runner_manager.cli.RunnerManager")
def test_install_no_ask_become_pass(self, mock_manager_class: MagicMock) -> None:
mock_manager = MagicMock()
mock_manager_class.return_value = mock_manager
runner = CliRunner(env={"GITEA_URL": "https://git.example.com", "GITEA_ADMIN_TOKEN": ""})
result = runner.invoke(cli, ["install", "host1", "--user", "ubuntu", "--token", "tok", "--no-ask-become-pass"])
assert result.exit_code == 0
mock_manager.install.assert_called_once_with(
host="host1",
user="ubuntu",
@@ -95,7 +116,7 @@ class TestCLI:
mode="docker",
admin_token="",
integration_retries=3,
ask_become_pass=False,
ask_become_pass=True,
)
@patch("gitea_runner_manager.cli.RunnerManager")
@@ -137,7 +158,7 @@ class TestCLI:
mode="binary",
admin_token="",
integration_retries=3,
ask_become_pass=False,
ask_become_pass=True,
)
@patch("gitea_runner_manager.cli.RunnerManager")
@@ -179,7 +200,7 @@ class TestCLI:
key="/key",
version="v0.2.0",
mode="docker",
ask_become_pass=False,
ask_become_pass=True,
)
@patch("gitea_runner_manager.cli.RunnerManager")
@@ -213,7 +234,7 @@ class TestCLI:
key=None,
version=None,
mode="binary",
ask_become_pass=False,
ask_become_pass=True,
)
@patch("gitea_runner_manager.cli.RunnerManager")
@@ -243,7 +264,7 @@ class TestCLI:
user=None,
key=None,
mode=None,
ask_become_pass=False,
ask_become_pass=True,
)
@patch("gitea_runner_manager.cli.RunnerManager")
@@ -260,7 +281,7 @@ class TestCLI:
user="newuser",
key=None,
mode="binary",
ask_become_pass=False,
ask_become_pass=True,
)
@patch("gitea_runner_manager.cli.RunnerManager")
@@ -276,7 +297,7 @@ class TestCLI:
host=None,
user=None,
key=None,
ask_become_pass=False,
ask_become_pass=True,
)
@patch("gitea_runner_manager.cli.RunnerManager")
@@ -292,7 +313,7 @@ class TestCLI:
host=None,
user=None,
key=None,
ask_become_pass=False,
ask_become_pass=True,
)
@patch("gitea_runner_manager.cli.RunnerManager")
@@ -311,7 +332,7 @@ class TestCLI:
token="tok",
gitea_url="https://git.example.com",
mode=None,
ask_become_pass=False,
ask_become_pass=True,
)
@patch("gitea_runner_manager.cli.RunnerManager")
@@ -355,7 +376,7 @@ class TestCLI:
user=None,
key=None,
mode=None,
ask_become_pass=False,
ask_become_pass=True,
)
@patch("gitea_runner_manager.cli.RunnerManager")
@@ -374,7 +395,7 @@ class TestCLI:
token="tok",
gitea_url="https://git.example.com",
mode=None,
ask_become_pass=False,
ask_become_pass=True,
force=False,
)
@@ -394,7 +415,7 @@ class TestCLI:
token="tok",
gitea_url="https://git.example.com",
mode=None,
ask_become_pass=False,
ask_become_pass=True,
force=True,
)