Add /docs/ directory with user and technical documentation extracted from README, AGENTS.md, and source code. Add scripts/sync_wiki.py to sync docs to Gitea wiki via API. Add scripts/doc_coverage.py to check CLI commands, modules, and CI scripts are documented. Add sync-wiki.yml workflow for auto-sync on merge and release. Slim down README.md to lean entry point. 28 new unit tests, 100% coverage maintained. Closes GRM-36
98 lines
4.3 KiB
Markdown
98 lines
4.3 KiB
Markdown
# Architecture
|
|
|
|
GRM consists of two layers:
|
|
|
|
1. **Python CLI** (`src/gitea_runner_manager/`) — built with Click, handles argument parsing, environment loading, i18n translations, and delegates to Ansible via the `ansible-playbook` subprocess.
|
|
2. **Ansible Role** (`ansible/roles/gitea-runner/`) — idempotent role that creates a dedicated system user, sets up rootless Docker, installs the runner binary, creates a systemd user service, and registers the runner with Gitea.
|
|
|
|
## Component Tree
|
|
|
|
```
|
|
grm install <host>
|
|
└── RunnerManager.install()
|
|
└── ansible-playbook ansible/install-runner.yml
|
|
└── role: gitea-runner
|
|
├── user_setup.yml (create per-runner system user + lingering)
|
|
├── rootless_docker.yml (rootless Docker setup under runner user)
|
|
├── install_runner.yml (download binary, config, register, service)
|
|
├── prune.yml (Docker prune timer)
|
|
└── integration_test.yml (validate service is active)
|
|
```
|
|
|
|
The Ansible role task execution order (from `AGENTS.md`):
|
|
|
|
```
|
|
main.yml → systemd_check → user_setup → rootless_docker → install_runner → prune → integration_test
|
|
```
|
|
|
|
- `install_runner.yml` handles: download, config, validate, register, service
|
|
- `main.yml` handles: prune, integration_test (NOT install_runner — avoids duplicates)
|
|
- `systemctl --user` tasks must be guarded by `docker_rootless_setup`
|
|
- Template creation tasks are NOT guarded by `docker_rootless_setup` (they just create files)
|
|
|
|
## Per-Runner Isolation
|
|
|
|
Each runner runs as a systemd user service under a dedicated system user (`grm-<name>`). Each instance has fully isolated resources:
|
|
|
|
- **User**: `grm-<name>` (dedicated system user with lingering enabled)
|
|
- **Home**: `/home/grm-<name>/`
|
|
- **Data**: `/var/lib/gitea-runner/<name>/`
|
|
- **Config**: `/etc/gitea-runner/<name>/`
|
|
- **Service**: `gitea-runner.service` (systemd user service)
|
|
- **Docker socket**: `/run/user/<UID>/docker.sock` (rootless, per-runner)
|
|
|
|
## Component Interactions
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
CLI["Python CLI<br/>src/gitea_runner_manager/<br/>(Click)"]
|
|
RM["RunnerManager<br/>runner_manager.py"]
|
|
EXEC["Executor<br/>executor.py"]
|
|
REG["Registry<br/>registry.py<br/>~/.local/share/grm/runners.json"]
|
|
ANS["ansible-playbook subprocess"]
|
|
ROLE["Ansible Role<br/>ansible/roles/gitea-runner/"]
|
|
USER["user_setup.yml<br/>create system user + lingering"]
|
|
DOCKER["rootless_docker.yml<br/>rootless Docker setup"]
|
|
INSTALL["install_runner.yml<br/>download, config, register, service"]
|
|
PRUNE["prune.yml<br/>Docker prune timer"]
|
|
TEST["integration_test.yml<br/>validate service active"]
|
|
GITEA["Gitea instance<br/>registration + API"]
|
|
SYSTEMD["systemd user service<br/>gitea-runner.service"]
|
|
|
|
CLI --> RM
|
|
RM --> REG
|
|
RM --> EXEC
|
|
EXEC -->|subprocess| ANS
|
|
ANS --> ROLE
|
|
ROLE --> USER
|
|
ROLE --> DOCKER
|
|
ROLE --> INSTALL
|
|
ROLE --> PRUNE
|
|
ROLE --> TEST
|
|
INSTALL -->|register| GITEA
|
|
INSTALL --> SYSTEMD
|
|
DOCKER --> SYSTEMD
|
|
```
|
|
|
|
## Additional Components
|
|
|
|
From `AGENTS.md`, the project also includes:
|
|
|
|
- **CI Scripts** (`scripts/`) — Automation for auto-merge, post-merge, release, publishing, molecule distribution, PR reviews, failure notifications
|
|
- **Versioning** (`cliff.toml`) — git-cliff configuration for automated semver versioning from conventional commits
|
|
|
|
## Python Modules
|
|
|
|
The Python CLI layer (`src/gitea_runner_manager/`) consists of the following modules:
|
|
|
|
| Module | Description |
|
|
|--------|-------------|
|
|
| `cli.py` | Click-based CLI entry point — defines all commands (install, update, start, stop, enable, disable, status, remove, list) |
|
|
| `runner_manager.py` | Ansible orchestration + registry integration — delegates to executor and manages runner lifecycle |
|
|
| `executor.py` | Ansible subprocess execution — runs `ansible-playbook` with extra-vars via temp JSON files |
|
|
| `registry.py` | Local JSON runner registry at `~/.local/share/grm/runners.json` — stores connection metadata |
|
|
| `i18n.py` | Internationalization translations (en, bg, de, ru, zh) |
|
|
| `exceptions.py` | Custom exceptions (`GRMError`, `APIError`) |
|
|
| `api_clients.py` | Gitea and Vikunja API client classes for CI automation scripts |
|
|
| `config.py` | Configuration constants (API URLs, repo owner/name, project IDs) — overridable via environment variables |
|