From 8c385dcbdc71cee7bd18c5fcd0c49df02d4f9d65 Mon Sep 17 00:00:00 2001 From: Emil Simeonov Date: Thu, 18 Jun 2026 03:21:19 +0200 Subject: [PATCH] GRM-7: docs: overhaul README and add project documentation --- .env.example | 14 ++- CHANGELOG.md | 51 +++++++++++ README.md | 173 ++++++++++++++++++++++++++++++++++++-- ansible/inventory.example | 13 ++- 4 files changed, 242 insertions(+), 9 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.env.example b/.env.example index eddb359..b4e9acd 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,12 @@ +# Gitea instance URL (used for runner registration and API validation) GITEA_URL=https://git.example.com -GITEA_TOKEN=your-personal-access-token -# Optional: GITEA_RUNNER_USER=ubuntu -# Optional: GITEA_RUNNER_KEY=~/.ssh/id_ed25519 + +# Runner registration token from Gitea admin panel: +# Admin → Actions → Runners → Create Registration Token +GITEA_REGISTRATION_TOKEN=your-registration-token + +# Default SSH user for remote hosts (optional, overrides --user) +# GITEA_RUNNER_USER=ubuntu + +# Default SSH private key path (optional, overrides --key) +# GITEA_RUNNER_KEY=~/.ssh/id_ed25519 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ef872ab --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,51 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [Unreleased] + +### Added + +- Parameterized all hardcoded configuration values as Ansible variables in `defaults/main.yml`: + - `gitea_runner_docker_image` — Docker image name (default: `gitea/runner`) + - `gitea_runner_data_dir` — Runtime data directory + - `gitea_runner_config_dir` — Config directory + - `gitea_runner_binary_path` — Binary install path + - `gitea_runner_prune_until` — Prune age filter + - `gitea_runner_prune_schedule` — Prune timer schedule + - `gitea_runner_prune_label` — Docker label for pruning + - `gitea_runner_service_restart_sec` — systemd restart interval + - `gitea_runner_service_user` — Service user + - `gitea_runner_log_level` — Runner log level + - `gitea_runner_container_label` — Container label + - `gitea_runner_file` — Runner metadata file + - `docker_gpg_key_path` — Docker GPG key path +- Added `console_scripts` entry point in `pyproject.toml` (`grm = "gitea_runner_manager.cli:cli"`). +- Added shared `molecule/common/prepare.yml` to eliminate duplicated prepare playbooks. +- Extracted repeated systemd availability check into `tasks/systemd_check.yml`. +- Added idempotence checks to both Molecule scenarios (`default` and `binary`). +- Comprehensive README overhaul with Architecture, Configuration, Development, Testing, and Troubleshooting sections. + +### Changed + +- Replaced legacy runner terminology with `gitea_runner` / `gitea-runner` / `Gitea Runner`. +- Updated default Docker image from `gitea/gitea_runner` to `gitea/runner`. +- `Makefile` now uses the installed `grm` console script instead of `python grm`. +- `docker_update.yml`: reordered `register` before `changed_when` for readability. +- `binary_update.yml`: removed redundant explicit service restart (handler handles this idempotently). + +### Removed + +- Deleted `setup.py` (redundant with `pyproject.toml`). +- Deleted `grm` shell entrypoint script (replaced by `console_scripts`). +- Deleted `initial-plan.md` and `tests/integration/test_provision.py` (dead code). +- Removed empty `__init__.py` files from `tests/` directories. +- Removed unused `runner_validated` fact from `validate.yml`. +- Removed duplicate `prune.yml` and `integration_test.yml` includes from `docker_mode.yml` and `binary_mode.yml` (now included once from `main.yml`). + +### Fixed + +- Molecule idempotence failures caused by non-idempotent service restart in `binary_update.yml`. +- Missing `/etc/docker` directory handling in Molecule tests. +- `ansible-lint` formatting warnings (yaml empty lines). +- Verify playbooks now explicitly load role defaults so parameterized variables are available during verification. diff --git a/README.md b/README.md index cb394c9..08512ca 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ A lean command-line tool to automate the installation, configuration, and lifecycle management of Gitea Actions runners on Arch Linux, Ubuntu, and Debian hosts. +By default, runners are deployed as **Docker containers** using the official `gitea/runner` image. A traditional **binary** deployment mode is also available. + > **Pronunciation note:** GRM is short for *Gitea Runner Manager*, but say it like **ГРЪМ** (roughly "GRUM" in Latin letters) — the Bulgarian word for **thunder**. Wherever there are clouds, there may be thunders. This is an open-source project from **Oblachno** (облачно means *cloudy* in Bulgarian). ## Features @@ -17,6 +19,11 @@ A lean command-line tool to automate the installation, configuration, and lifecy - Ubuntu 22.04 / 24.04 / 26.04 - Debian 12 / 13 +## 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 privileges** — The specified user must have passwordless or password-prompted (`--ask-become-pass`) sudo access on the remote host. Runner installation requires root privileges for tasks such as installing packages, creating systemd services, and managing Docker. + ## Quick Start ### Developer Setup @@ -35,23 +42,40 @@ make setup cp .env.example .env # Edit .env: # GITEA_URL=https://git.example.com -# GITEA_TOKEN=your-personal-access-token +# GITEA_REGISTRATION_TOKEN=your-registration-token ``` -The token needs `admin:runner` scope. +`GITEA_REGISTRATION_TOKEN` is the runner registration token obtained from your Gitea instance (Admin → Actions → Runners → Create Registration Token). ### Install a Runner Using the CLI: ```bash -./grm install 192.168.1.10 --user ubuntu --key ~/.ssh/id_ed25519 --name prod-runner +# Docker mode (default) — deploys gitea_runner as a container +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 +``` + +If the remote user requires a sudo password: + +```bash +grm install 192.168.1.10 --user ubuntu --ask-become-pass ``` Using Make: ```bash +# Docker mode (default) 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 + +# With sudo password prompt: +make install HOST=192.168.1.10 USER=ubuntu ASK_BECOME_PASS=1 ``` ### Verify Runner @@ -61,15 +85,154 @@ Check Gitea admin UI under **Actions → Runners**. The runner should appear as ### View Logs ```bash -sudo journalctl -u act-runner- -f +# Binary mode logs +sudo journalctl -u gitea-runner- -f + +# Docker mode logs +docker logs gitea-runner- -f ``` +## Architecture + +GRM consists of two layers: + +1. **Python CLI** (`src/gitea_runner_manager/`) — built with Click, handles argument parsing, environment loading, and delegates to Ansible via the `ansible-playbook` subprocess. +2. **Ansible Role** (`ansible/roles/gitea-runner/`) — idempotent role that installs Docker, configures the runner (binary or container), creates systemd services, and registers the runner with Gitea. + +``` +grm install + └── RunnerManager.install() + └── ansible-playbook ansible/install-runner.yml + └── role: gitea-runner + ├── docker.yml (Docker installation) + ├── docker_mode.yml (container deployment) + ├── binary_mode.yml (binary + systemd deployment) + ├── prune.yml (Docker prune timer) + └── integration_test.yml (validate online status) +``` + +## Configuration + +All tunable values are exposed as Ansible variables in `ansible/roles/gitea-runner/defaults/main.yml`: + +| Variable | Default | Description | +|----------|---------|-------------| +| `runner_mode` | `docker` | Deployment mode: `docker` or `binary` | +| `gitea_runner_version` | `1.0.8` | Docker image tag / binary version | +| `gitea_runner_docker_image` | `gitea/runner` | Docker image name | +| `runner_labels` | `ubuntu-latest:docker://runner-images:ubuntu-22.04` | Runner labels | +| `skip_runner_registration` | `false` | Skip API registration (useful for tests) | +| `gitea_runner_data_dir` | `/var/lib/gitea-runner` | Runtime data directory | +| `gitea_runner_config_dir` | `/etc/gitea-runner` | Config directory (binary mode) | +| `gitea_runner_binary_path` | `/usr/local/bin/gitea_runner` | Binary install path | +| `gitea_runner_prune_until` | `24h` | Prune resources older than this | +| `gitea_runner_prune_schedule` | `daily` | systemd timer schedule | +| `gitea_runner_prune_label` | `gitea-runner=true` | Docker label for pruning | +| `gitea_runner_service_restart_sec` | `5` | systemd RestartSec value | +| `gitea_runner_service_user` | `{{ ansible_user \| default('root') }}` | Service user | +| `gitea_runner_log_level` | `info` | Runner log level | +| `gitea_runner_container_label` | `gitea-runner=true` | Container label | +| `docker_gpg_key_path` | `/etc/apt/keyrings/docker.asc` | Docker GPG key path | + +Override any variable by passing it to the CLI with `--extra-vars` or by setting it in your Ansible inventory. + +## Development + +### Project Structure + +``` +. +├── src/gitea_runner_manager/ # Python CLI source +│ ├── cli.py # Click commands +│ ├── runner_manager.py # Ansible orchestration +│ └── exceptions.py # Custom exceptions +├── ansible/ +│ ├── roles/gitea-runner/ # Main Ansible role +│ │ ├── defaults/main.yml # Default variables +│ │ ├── tasks/ # Task files +│ │ ├── templates/ # Jinja2 templates +│ │ └── molecule/ # Test scenarios +│ ├── install-runner.yml # Install playbook +│ └── update-runner.yml # Update playbook +├── tests/ # Unit tests +├── Makefile # Build & test automation +└── pyproject.toml # Python project metadata +``` + +### Setup Development Environment + +```bash +make setup # Creates venv, installs deps, sets up hooks +source .venv/bin/activate +``` + +### Running Linters + +```bash +make lint # Python (ruff + pyright) +make ansible-lint # Ansible +make makefile-lint # Makefile +``` + +## Testing + +### Unit Tests + +```bash +make test-unit +``` + +Runs pytest with 100% coverage requirement. + +### Molecule Tests + +```bash +make molecule +``` + +Runs two scenarios: + +- **default** — Docker mode installation in an Ubuntu 22.04 container +- **binary** — Binary mode installation in an Ubuntu 22.04 container + +Both scenarios test idempotence (second run produces zero changes). + +### Full Test Suite + +```bash +make test-all # Runs unit tests + linters + molecule +``` + +## Troubleshooting + +### "Event loop is closed" warning + +This is a harmless cleanup traceback from Molecule's Docker driver when the test process is interrupted. It does not indicate a test failure. + +### Runner appears offline after installation + +- Check that the `GITEA_URL` and `GITEA_REGISTRATION_TOKEN` environment variables are correct. +- Verify the runner container or service is running: `docker ps` or `systemctl status gitea-runner-`. +- Check logs for registration errors. + +### Docker mode: container won't start + +- Ensure Docker is installed and running on the host. +- Verify the Docker socket is accessible: `docker version`. + +### Binary mode: systemd service fails + +- Check the service status: `systemctl status gitea-runner-`. +- Verify the binary exists at `gitea_runner_binary_path`. +- Ensure the service user is in the `docker` group. + ## Makefile Targets | Target | Description | |--------|-------------| | `setup` | Full environment setup | -| `install` | Installs a runner on a host | +| `install` | Installs a runner on a host (`MODE=docker` or `binary`) | +| `update` | Updates a runner on a host (`MODE=docker` or `binary`) | | `lint` | Runs Python linters | | `ansible-lint` | Runs `ansible-lint` | | `test-unit` | Runs unit tests with coverage | diff --git a/ansible/inventory.example b/ansible/inventory.example index 2e553a6..a505d00 100644 --- a/ansible/inventory.example +++ b/ansible/inventory.example @@ -1,3 +1,14 @@ +# Gitea Runner Manager inventory example +# Each line represents a target host for runner installation. +# +# Required variables per host: +# ansible_user — SSH login user +# ansible_ssh_private_key_file — Path to SSH private key +# +# Optional variables per host: +# runner_mode=docker — Deployment mode: docker or binary +# gitea_runner_version=1.0.8 — Runner image/binary version + [runners] 192.168.1.10 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_ed25519 -runner.example.com ansible_user=arch +runner.example.com ansible_user=arch ansible_ssh_private_key_file=~/.ssh/id_ed25519 runner_mode=binary