Files
grm/AGENTS.md
T

170 lines
6.9 KiB
Markdown

# AGENTS.md — Project Conventions for GRM
## Build & Test Commands
```bash
make setup # Create venv, install deps, set up hooks
make lint-all # ruff + pyright + bandit + ansible-lint + checkmake
make pytest-cov # Unit tests with 100% coverage enforcement
make test-unit # Unit tests without coverage
make molecule # All 6 scenarios on Ubuntu 22.04
make molecule-all # All 6 scenarios on all 4 supported OSes
make test-all # pytest-cov + molecule
```
## Architecture
- **Python CLI** (`src/gitea_runner_manager/`) — Click-based CLI that delegates to Ansible
- **Ansible Role** (`ansible/roles/gitea-runner/`) — Idempotent role for rootless Docker runner setup
- **CI Scripts** (`scripts/`) — Automation for auto-merge, post-merge, release, publishing, molecule distribution, PR reviews
- **Versioning** (`cliff.toml`) — git-cliff configuration for automated semver versioning from conventional commits
## PR Workflow (Mandatory)
Every change to master goes through this workflow. No exceptions.
### 1. Create Vikunja Task
Create a task in Vikunja project 6 to get a `GRM-N` identifier.
### 2. Create Branch
```bash
git checkout master && git pull
git checkout -b GRM-N-short-description
```
### 3. Implement Changes
- Write code following conventions below
- Write/update tests (100% coverage required)
- Update documentation (CHANGELOG, README, AGENTS.md as needed)
### 4. Commit (Conventional Commits)
Branch commits use conventional commit format (no `GRM-N:` prefix):
```
feat: add new feature
fix: resolve bug
docs: update README
```
### 5. Push and Create PR
- **PR title format**: `GRM-N: <vikunja task title>` (colon-separated)
- PR body: summary of changes, `Closes GRM-N`
- Add `ready-to-merge` label **only after review is complete**
### 6. Review the PR (Mandatory — Before Adding ready-to-merge Label)
Review the full diff (`git diff master...HEAD`) focusing on:
- **Functional completeness**: Does the code do what it claims? Are all requirements met?
- **Edge cases**: Are boundary conditions, empty inputs, error paths handled?
- **Technical excellence**:
- Architecture compliance and evolution
- Single Responsibility Principle (SRP)
- Deduplication (no copy-paste, single source of truth)
- Code smells detection and removal
- Best industry practices
- Industry-grade code quality
- Reusability
- Clean code
- Readability
- Maintainability
- Extensibility
- **Performance**: No unnecessary allocations, O(n) vs O(n²), efficient data structures
- **Security**: No secrets in logs/process list, input validation, no injection vectors
- **User experience**: Clear error messages, intuitive CLI flags, helpful output
- **Documentation**: Completeness and relevance of docs, CHANGELOG entries, AGENTS.md updates
Post review comments using `scripts/review_pr.py`:
```bash
REPO_TOKEN=<token> python3 scripts/review_pr.py <pr_number> <owner/repo> \
--event REQUEST_CHANGES \
--body "Review summary" \
--comments-json comments.json
```
### 7. Address Review Comments
Fix each comment one by one, commit, and push. Re-review until satisfied.
### 8. Approve and Merge
Once all comments are addressed:
```bash
REPO_TOKEN=<token> python3 scripts/review_pr.py <pr_number> <owner/repo> \
--event APPROVE \
--body "All comments addressed. LGTM."
```
Then add the `ready-to-merge` label. The auto-merge workflow will:
1. Wait for all CI checks to pass
2. Squash-merge with title: `GRM-N <conventional commit message>` (space-separated)
3. The post-merge workflow marks the Vikunja task as done
4. The release workflow automatically versions, tags, and publishes (see below)
### Automated Release Pipeline
After a PR is merged to master, the release pipeline runs automatically:
1. **Release workflow** (`.gitea/workflows/release.yml`):
- Triggers on push to master (skips `chore(release):` commits to avoid loops)
- Runs `scripts/release.py` which uses **git-cliff** to:
- Calculate the next semver version from conventional commits since the last tag
- Update `__version__` in `src/gitea_runner_manager/__init__.py` (single source of truth)
- Create a `chore(release): prepare for vX.Y.Z` commit
- Create an annotated tag `vX.Y.Z` with the changelog as the tag message
- Push the commit and tag to master
2. **Publish workflow** (`.gitea/workflows/publish.yml`):
- Triggers on tag push (`v*`)
- Builds the Python package
- Optionally publishes to PyPI (if `PYPI_TOKEN` is set)
- Creates a Gitea release with git-cliff-generated release notes
### Version Bumping Rules (git-cliff)
| Commit type | Version bump |
|-------------|-------------|
| `feat:` | minor (0.X.0) |
| `fix:` | patch (0.0.X) |
| `feat!:` or `BREAKING CHANGE` | minor (pre-1.0: major would be 1.0.0) |
| `chore:`, `ci:`, `docs:` | no bump (excluded by cliff.toml) |
The version source is `__version__` in `src/gitea_runner_manager/__init__.py`, read by setuptools via `dynamic = ["version"]` in `pyproject.toml`. The release script only updates `__init__.py` — no need to touch `pyproject.toml`. `grm --version` reports this version.
### Title Format Summary
| What | Format | Example |
|------|--------|---------|
| Branch name | `GRM-N-short-description` | `GRM-33-add-pr-review-step` |
| Branch commits | `<conventional commit>` | `feat: add review script` |
| PR title | `GRM-N: <vikunja task title>` | `GRM-33: Add mandatory PR review step` |
| Merge commit | `GRM-N <conventional commit>` | `GRM-33 feat: add review script` |
## Key Conventions
- Python 3.12+ required (ruff/pyright target `py312`)
- 100% test coverage required (`--cov-fail-under=100`)
- Conventional commits on feature branches (no `GRM-N:` prefix)
- Branch names must include `GRM-N` task ID
- Line length: 120 chars
- Secrets are passed via temp JSON files, never on the command line (CWE-214)
- CI triggers only on `opened` and `synchronize` PR events (not `labeled`)
## Ansible Role Structure
```
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)
## Molecule Scenarios
6 scenarios: `default`, `multi-instance`, `lifecycle`, `template-content`, `deregister`, `update`
4 platforms: `ubuntu-2204`, `ubuntu-2404`, `debian-12`, `archlinux`
Platform list is defined in `scripts/distribute_molecule.py` (single source of truth)
## Known Issues
- `ansible-lint` may warn about `command-instead-of-module` for `systemctl --user` calls — this is expected (systemd module doesn't support user services) and skipped in `.ansible-lint`
- Molecule Docker driver may print "Event loop is closed" warnings on interrupt — harmless