Files
grm/docs/tech/architecture.md
T
emil 5b05db4e6d GRM-36: feat: implement documentation-as-code with wiki sync and doc-coverage
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
2026-06-21 19:45:34 +00:00

4.3 KiB

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

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