Compare commits

..
8 Commits
Author SHA1 Message Date
gitea-actions-bot bd0910287a chore: update badge URLs to commit fe21232e [skip ci] 2026-09-05 14:24:30 +00:00
kireto 063623cb9f GRM-173: docs: add dependency-graph, deployment-coordination, and skill-creation skills
Post-merge / detect-and-configure (push) Successful in 59s
Post-merge / release-and-maintain (push) Successful in 5m43s
2026-09-05 14:17:44 +00:00
gitea-actions-bot ae52aab843 chore: update badge URLs to commit b5f4d883 [skip ci] 2026-08-28 16:15:40 +00:00
grm-ci-bot 2acc1c0eb6 release: v0.23.0 [skip ci] 2026-08-28 16:15:05 +00:00
kireto a31af2e236 GRM-162: feat: add pre-cache timer, force_pull, and Docker socket options to runner config
Post-merge / detect-and-configure (push) Successful in 1m2s
Post-merge / release-and-maintain (push) Successful in 3m0s
2026-08-28 16:11:34 +00:00
gitea-actions-bot 68a763b942 chore: update badge URLs to commit 9ae4b38a [skip ci] 2026-08-27 16:08:53 +00:00
emo d9dae396bc GRM-172: docs: document pre-pull image usage guidelines
Post-merge / detect-and-configure (push) Successful in 2m52s
Post-merge / release-and-maintain (push) Successful in 1m13s
Co-authored-by: emo <emo@oblachno.com>
2026-08-27 16:04:45 +00:00
gitea-actions-bot 7816733e5e chore: update badge URLs to commit 8dd25f8c [skip ci] 2026-08-26 20:38:17 +00:00
17 changed files with 642 additions and 13 deletions
+135
View File
@@ -0,0 +1,135 @@
# dependency-graph
Map of the oblachno ecosystem. Knows which repo produces what, which
repos depend on which, and the correct order for cross-repo changes.
## When to Invoke
Invoke this skill when:
- Changes span multiple repos
- A change in one repo requires version bumps in downstream repos
- Deploying infrastructure that depends on published packages/images
- Verifying the ecosystem is in a consistent state before deployment
- Determining which repos to update and in what order
## Prerequisites
- All repos cloned under `/home/emo/dev/ideas/oblachno/`
- `.env` with `DEVELOPER_GITEA_API_TOKEN` in each repo
## Ecosystem Map
```
devx (PyPI package)
/ | \
/ | \
grm sso-bridge infra
(PyPI) (PyPI+Docker) (deploys all)
| | |
v v v
infra bump infra bump staging
(auto PR) (auto PR) production
|
mattermost-oidc (Docker image)
(infra pulls :latest at deploy)
```
## Repositories
| Repo | Produces | Consumers | Release Trigger |
|------|----------|-----------|-----------------|
| `devx` | PyPI package `devx` | grm, sso-bridge, infra | User-facing changes to `src/devx/**` |
| `grm` | PyPI package `grm` | infra | User-facing changes to `src/grm/**` or `ansible/**` |
| `sso-bridge` | PyPI package `sso_bridge` + Docker image | infra | User-facing changes to `src/sso_bridge/**` or `ansible/**` |
| `infra` | Staging/production deployment | (end users) | User-facing changes + nightly gate |
| `mattermost-oidc` | Docker image `mattermost-oidc` | infra (pulls at deploy) | `Dockerfile` or `build.yml` changes |
## Dependency Chain
### devx → all repos
devx publishes to the Gitea PyPI registry. grm, sso-bridge, and infra
pin devx in `pyproject.toml`:
```toml
"devx @ git+https://git.oblachno.oblachno.fyi/oblachno-oss/devx.git@vX.Y.Z"
```
When devx publishes a new version:
1. grm, sso-bridge, and infra must bump their pinned devx version
2. This is currently manual — no auto-dependency-PR from devx
3. Each repo must `make setup` to pick up the new version
### grm → infra
grm publishes to PyPI. Its post-merge workflow auto-creates an infra
dependency PR via `devx.ci.create_dependency_pr --repo oblachno/infra
--package grm`. The PR bumps the pinned grm version in infra's
`pyproject.toml`.
### sso-bridge → infra
sso-bridge publishes to PyPI AND builds a Docker image. Its post-merge
workflow auto-creates an infra dependency PR via
`devx.ci.create_dependency_pr --repo oblachno/infra --package
sso_bridge`. The PR bumps the pinned sso_bridge version.
The Docker image is pulled by infra at deploy time (`sso-bridge:latest`).
### mattermost-oidc → infra
mattermost-oidc builds a Docker image tagged `:latest` and `:MM_VERSION`.
infra pulls `mattermost-oidc:latest` at deploy time. There is no
auto-dependency-PR — infra simply pulls the latest image.
### infra → staging/production
infra deploys to staging and production. The deployment:
1. Provisions VMs from golden images
2. Runs Ansible roles (including grm and sso-bridge roles)
3. Pulls Docker images (sso-bridge, mattermost-oidc)
4. Configures services
## Correct Order for Cross-Repo Changes
When a change spans multiple repos, follow this order:
1. **devx first** — if the change starts in devx, merge and publish devx
first. Wait for the PyPI publish job to complete.
2. **Bump devx in consumers** — in grm/sso-bridge/infra, bump the pinned
devx version, run `make setup`, verify tests pass, merge.
3. **grm/sso-bridge second** — merge and publish grm/sso-bridge. Wait
for the PyPI publish + Docker image build to complete.
4. **Auto-dependency-PRs** — grm/sso-bridge post-merge auto-creates infra
PRs to bump pinned versions. Wait for these PRs to appear.
5. **Merge infra dependency PRs** — review and merge the auto-created
infra PRs.
6. **infra last** — deploy to staging, validate, promote to production.
## State Verification Before Deployment
Before deploying infra, verify:
1. **devx version consistent** — all repos pin the same devx version
2. **grm published** — latest grm tag exists in PyPI
3. **sso-bridge published** — latest sso_bridge tag exists in PyPI
4. **sso-bridge image built** — latest sso-bridge Docker image exists
5. **mattermost-oidc image built** — latest mattermost-oidc image exists
6. **infra pins match published versions** — no stale pins
7. **Nightly gate green**`NIGHTLY_STATUS` is not `failed`
## Quick Check Commands
```bash
# Check latest devx version
curl -sS https://git.oblachno.oblachno.fyi/api/v1/repos/oblachno-oss/devx/releases/latest | python3 -c "import json,sys; print(json.load(sys.stdin).get('tag_name','?'))"
# Check pinned devx version in each repo
for repo in grm sso-bridge infra; do
echo -n "$repo: "; grep 'devx @' /home/emo/dev/ideas/oblachno/$repo/pyproject.toml | grep -oP 'v[\d.]+'
done
# Check latest sso-bridge image build
curl -sS -H "Authorization: token $DEVELOPER_GITEA_API_TOKEN" \
"https://git.oblachno.oblachno.fyi/api/v1/repos/oblachno/sso-bridge/actions/runs?per_page=5" \
| python3 -c "import json,sys; [print(r['id'],r['status'],r['conclusion']) for r in json.load(sys.stdin).get('workflow_runs',[]) if r.get('event')=='push']"
```
@@ -0,0 +1,78 @@
# deployment-coordination
How grm releases propagate to infra. grm publishes a PyPI package and
auto-creates an infra dependency PR. Coordination ensures the PR is
merged before infra deploys.
## When to Invoke
Invoke this skill when:
- Changes to grm affect infra deployments
- Preparing a grm release that infra depends on
- Verifying infra has bumped to the latest grm version
- Coordinating a multi-repo change that includes grm
## Prerequisites
- grm repo at `/home/emo/dev/ideas/oblachno/grm`
- `.env` with `DEVELOPER_GITEA_API_TOKEN`
- See `dependency-graph` skill for the full ecosystem map
## What grm Produces
grm publishes a Python package to the Gitea PyPI registry. infra pins it:
```toml
"grm @ git+https://git.oblachno.oblachno.fyi/oblachno-oss/grm.git@vX.Y.Z"
```
## Release Flow
1. PR merged to master
2. Post-merge workflow runs `devx.ci.release` — classifies changes
3. If user-facing changes: git-cliff bumps version, creates tag, pushes
4. `devx.ci.publish` builds and publishes to Gitea PyPI registry
5. `devx.ci.create_dependency_pr --repo oblachno/infra --package grm`
auto-creates an infra PR to bump the pinned grm version
## Downstream Consumer
| Repo | Pin location | Auto-bump? |
|------|-------------|------------|
| infra | `pyproject.toml` | Yes — auto PR created by post-merge |
## Coordinating a grm Change
1. **Merge grm PR** — wait for post-merge publish + dependency-PR creation
2. **Verify publish** — check the new tag:
```bash
curl -sS -H "Authorization: token $DEVELOPER_GITEA_API_TOKEN" \
https://git.oblachno.oblachno.fyi/api/v1/repos/oblachno-oss/grm/releases/latest \
| python3 -c "import json,sys; print(json.load(sys.stdin).get('tag_name','?'))"
```
3. **Find the auto-created infra PR** — check infra for open PRs with
`dependency` label or title containing `bump grm`
4. **Review and merge the infra dependency PR** — verify the version bump,
run `make pytest-cov` in infra, add `ready-to-merge`
5. **Verify infra staging deploy** — after infra merges, staging deploy
picks up the new grm version
## State Verification
```bash
# Current grm version
grep '__version__' /home/emo/dev/ideas/oblachno/grm/src/grm/__init__.py
# What infra pins
grep 'grm @' /home/emo/dev/ideas/oblachno/infra/pyproject.toml | grep -oP 'v[\d.]+'
# Check for open infra dependency PRs
curl -sS -H "Authorization: token $DEVELOPER_GITEA_API_TOKEN" \
"https://git.oblachno.oblachno.fyi/api/v1/repos/oblachno/infra/pulls?state=open" \
| python3 -c "import json,sys; [print(p['number'],p['title']) for p in json.load(sys.stdin) if 'grm' in p.get('title','').lower()]"
```
## Common Mistakes
- Merging the grm PR but ignoring the auto-created infra dependency PR
- Deploying infra before the dependency PR is merged — stale grm version
- Forgetting that grm also has an Ansible role used by infra at deploy time
+142
View File
@@ -0,0 +1,142 @@
# skill-creation
How to create, validate, and maintain Devin skills. Skills must be
clear, succinct, and actionable — no AI slop.
## When to Invoke
Invoke this skill when:
- Creating a new skill
- Amending an existing skill
- Evaluating whether a skill is needed
- Reviewing a PR that adds or modifies skills
## Prerequisites
- Skill directory: `.devin/skills/<skill-name>/SKILL.md`
- Validator: `tests/test_skills.py` (infra) or reference to it
- Tests: `tests/unit/test_skills_validation.py` (infra)
## When to Create a Skill
Create a skill when:
- An agent struggles with a task repeatedly (branch hygiene, PR order)
- A workflow has non-obvious ordering constraints (deployment coordination)
- A task requires specific tool usage over raw commands (CI monitoring)
- Multiple agents need shared context (dependency graph)
Do NOT create a skill for:
- One-off tasks (use a spec instead)
- Tasks already covered by AGENTS.md
- Tasks that are obvious from the Makefile or README
- Tasks that change frequently (skills should be stable)
## Skill Structure
Every skill MUST have:
```markdown
# <skill-name>
One-line description of what the skill does.
## When to Invoke
2-4 bullet points describing when to use this skill.
## Prerequisites
What must exist before using the skill (venv, .env, tools).
## <Core Content>
The actual guidance. Keep it actionable.
## Verification (if applicable)
How to verify the skill's guidance works.
## Common Mistakes (if applicable)
What agents get wrong without this skill.
```
## Quality Standards
### Do
- **Be specific.** Reference exact make targets, file paths, commands.
- **Be concise.** Each section should be scannable in under 30 seconds.
- **Be actionable.** Every paragraph should tell the agent what to DO.
- **Use tables** for command reference, mappings, and comparisons.
- **Use code blocks** for commands the agent should run.
- **Link to other skills** when related (e.g., "See `dependency-graph` skill").
### Don't
- **No preamble.** Don't start with "This skill helps agents..." — just state what it does.
- **No filler.** Don't repeat information from AGENTS.md or other skills.
- **No vague advice.** "Be careful with branches" is useless. "Run `git branch --show-current` before every commit" is useful.
- **No AI slop.** Don't write "In this comprehensive guide, we will explore..." — just give the guidance.
- **No redundant sections.** If "Common Mistakes" would repeat "When to Invoke", skip it.
- **No marketing.** Don't describe the skill as "powerful" or "comprehensive".
## Scope Rules
- **One skill per concern.** Don't mix branch hygiene with CI monitoring.
- **Project-specific, not generic.** Skills reference this repo's make targets, file paths, and conventions — not abstract advice.
- **Shared skills must be identical across repos.** Use `SHARED_SKILLS` in the validator to enforce this.
- **Per-repo skills must reflect that repo's reality.** Don't copy infra-specific targets to sso-bridge.
## Automated Validation
Every skill must pass the validator (`tests/test_skills.py`). The validator checks:
1. **Structure** — H1 title, "When to Invoke" section, "Prerequisites" section
2. **Commands** — referenced `make <target>` commands exist in Makefile or devx.mak
3. **Paths** — referenced file paths exist in the repo
4. **Shared skills** — identical content across repos (SHA-256 comparison)
5. **No drift** — no references to nonexistent commands or files
Run the validator:
```bash
python3 tests/test_skills.py --repo infra --repo sso-bridge
```
## Effectiveness Evaluation
### Static Checks (automated, CI)
The validator runs in CI as part of `make pytest-cov`. A failing skill
test blocks the PR. This catches:
- Missing sections
- Invalid commands
- Broken file references
- Cross-repo drift
### Runtime Metrics (manual, periodic)
Track these signals to evaluate skill effectiveness:
- **Skill invocation frequency** — how often agents invoke the skill
- **Success rate when invoked** — did the skill prevent the mistake it targets?
- **Feedback issues** — agents create Gitea issues with `feedback` label when a skill is unclear or wrong
- **Mistake recurrence** — if agents still make the mistake the skill targets, the skill needs improvement
### Retrospective Review
Periodically (monthly or after major incidents) review skills:
1. List all skills and their last-modified dates
2. Check for feedback issues tagged `skill-improvement`
3. Verify referenced commands still exist (run validator)
4. Remove skills that are no longer relevant
5. Update skills where mistakes still recur
6. Document lessons in this skill's "Common Mistakes" section
## Creating a New Skill — Checklist
- [ ] Identify the repeated struggle or non-obvious workflow
- [ ] Check no existing skill covers it
- [ ] Write the skill following the structure above
- [ ] Run `python3 tests/test_skills.py` — must pass
- [ ] Run `make pytest-cov` — must pass with 100% coverage
- [ ] If shared across repos, copy identical content to each repo
- [ ] Add the skill to `SHARED_SKILLS` in the validator if shared
- [ ] Create PR, verify CI passes, merge
+6
View File
@@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [0.23.0] - 2026-08-28
### Features
- Add pre-cache timer, force_pull, and Docker socket options to runner config
## [0.22.1] - 2026-08-26 ## [0.22.1] - 2026-08-26
### Bug Fixes ### Bug Fixes
+6 -6
View File
@@ -8,12 +8,12 @@ Each runner runs in an isolated **rootless Docker** environment under a dedicate
[![CI](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions/workflows/ci.yml/badge.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions) [![CI](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions/workflows/ci.yml/badge.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![License: GPL-3.0](https://img.shields.io/badge/license-GPL--3.0-blue)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/src/branch/master/LICENSE) [![License: GPL-3.0](https://img.shields.io/badge/license-GPL--3.0-blue)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/src/branch/master/LICENSE)
[![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5ba6b90dffc24876035267268dd3780f7089264c/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions) [![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/fe21232ef9dd554a49e4309bd918d358194b65d8/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5ba6b90dffc24876035267268dd3780f7089264c/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions) [![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/fe21232ef9dd554a49e4309bd918d358194b65d8/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5ba6b90dffc24876035267268dd3780f7089264c/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki) [![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/fe21232ef9dd554a49e4309bd918d358194b65d8/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
[![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5ba6b90dffc24876035267268dd3780f7089264c/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions) [![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/fe21232ef9dd554a49e4309bd918d358194b65d8/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5ba6b90dffc24876035267268dd3780f7089264c/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases) [![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/fe21232ef9dd554a49e4309bd918d358194b65d8/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
[![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5ba6b90dffc24876035267268dd3780f7089264c/python.svg)](https://www.python.org/downloads/) [![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/fe21232ef9dd554a49e4309bd918d358194b65d8/python.svg)](https://www.python.org/downloads/)
## Why GRM? ## Why GRM?
@@ -90,6 +90,22 @@ gitea_runner_remove_user: true
gitea_runner_log_level: "info" gitea_runner_log_level: "info"
gitea_runner_container_label: "gitea-runner=true" gitea_runner_container_label: "gitea-runner=true"
gitea_runner_file: ".runner" gitea_runner_file: ".runner"
# force_pull: when false (default), the runner reuses locally cached images
# instead of pulling on every job. Pre-cached images (via the pre-cache timer
# or pre_pull_images task) eliminate registry thundering-herd when all runners
# start jobs simultaneously.
gitea_runner_force_pull: false
# Container options passed to `docker run` for CI job containers.
# Mounts the host rootless Docker socket as /run/host-docker.sock so
# start_docker.py inside the container can detect and use the host daemon
# (full disk, no nested DinD) instead of starting an inner dockerd.
gitea_runner_container_options: "-v /run/user/{{ gitea_runner_uid }}/docker.sock:/run/host-docker.sock"
# Volumes allowed in CI job containers (validated by the runner against
# container.options and job-level volumes). Must include the host Docker
# socket mount target.
gitea_runner_valid_volumes:
- "/run/host-docker.sock"
- "/run/user/{{ gitea_runner_uid }}/docker.sock"
# Containerd version pinning — Docker 28.x vendors containerd v2.1.x internally. # Containerd version pinning — Docker 28.x vendors containerd v2.1.x internally.
# containerd.io >= 2.3 ships a shim that returns a protobuf BootstrapResult which # containerd.io >= 2.3 ships a shim that returns a protobuf BootstrapResult which
@@ -135,3 +151,11 @@ gitea_runner_docker_ipv6_cidr: "fd00:dead:beef::/48"
# disk-space prune only removes dangling images, so pre-pulled tagged images persist. # disk-space prune only removes dangling images, so pre-pulled tagged images persist.
# Set to [] to skip pre-pulling. Images are pulled as the runner user via rootless Docker. # Set to [] to skip pre-pulling. Images are pulled as the runner user via rootless Docker.
gitea_runner_pre_pull_images: [] gitea_runner_pre_pull_images: []
# Pre-cache timer: periodically pulls the runner container image so it stays
# fresh in the local Docker cache. This prevents thundering-herd registry
# timeouts when all runners start CI jobs simultaneously with empty caches.
# Runs every 6 hours (aligned with prune schedule). Set to empty string to
# disable the timer.
gitea_runner_pre_cache_schedule: "*-*-* 00/6:30:00"
gitea_runner_pre_cache_images: "{{ gitea_runner_pre_pull_images }}"
@@ -20,6 +20,9 @@
- name: Include pre-pull images - name: Include pre-pull images
ansible.builtin.include_tasks: pre_pull_images.yml ansible.builtin.include_tasks: pre_pull_images.yml
- name: Include pre-cache timer
ansible.builtin.include_tasks: pre_cache.yml
- name: Include integration test - name: Include integration test
ansible.builtin.include_tasks: integration_test.yml ansible.builtin.include_tasks: integration_test.yml
when: not gitea_runner_skip_registration when: not gitea_runner_skip_registration
@@ -0,0 +1,67 @@
---
# Periodic timer that pre-pulls CI runner images into the local Docker cache.
# Prevents thundering-herd registry timeouts when all runners start jobs
# simultaneously with empty/stale caches. Runs every 6 hours (configurable).
# The prune timer removes dangling images but NOT tagged ones, so pre-pulled
# images persist between runs.
- name: Create docker-pull-images user service file
ansible.builtin.template:
src: docker-pull-images.service.j2
dest: "{{ gitea_runner_home }}/.config/systemd/user/docker-pull-images.service"
owner: "{{ gitea_runner_service_user }}"
group: "{{ gitea_runner_service_user }}"
mode: "0644"
register: gitea_runner_pre_cache_service
- name: Create docker-pull-images user timer file
ansible.builtin.template:
src: docker-pull-images.timer.j2
dest: "{{ gitea_runner_home }}/.config/systemd/user/docker-pull-images.timer"
owner: "{{ gitea_runner_service_user }}"
group: "{{ gitea_runner_service_user }}"
mode: "0644"
register: gitea_runner_pre_cache_timer
- name: Reload systemd user daemon for pre-cache timer
ansible.builtin.command: systemctl --user daemon-reload
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ gitea_runner_uid | default(0) }}/bus"
changed_when: true
when:
- gitea_runner_systemd_available.stat.exists
- gitea_runner_docker_rootless_setup
- gitea_runner_pre_cache_service is changed or gitea_runner_pre_cache_timer is changed
- gitea_runner_pre_cache_schedule | length > 0
- gitea_runner_pre_cache_images | length > 0
- name: Enable and start docker-pull-images user timer
ansible.builtin.command: systemctl --user enable --now docker-pull-images.timer
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ gitea_runner_uid | default(0) }}/bus"
changed_when: true
when:
- gitea_runner_systemd_available.stat.exists
- gitea_runner_docker_rootless_setup
- gitea_runner_pre_cache_schedule | length > 0
- gitea_runner_pre_cache_images | length > 0
- name: Disable and stop docker-pull-images timer (no images or schedule)
ansible.builtin.command: systemctl --user disable --now docker-pull-images.timer
become: true
become_user: "{{ gitea_runner_service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid }}"
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ gitea_runner_uid | default(0) }}/bus"
changed_when: true
failed_when: false
when:
- gitea_runner_systemd_available.stat.exists
- gitea_runner_docker_rootless_setup
- gitea_runner_pre_cache_schedule | length == 0 or gitea_runner_pre_cache_images | length == 0
@@ -8,6 +8,15 @@
# #
# Set gitea_runner_pre_pull_images to a list of image refs to pull, or # Set gitea_runner_pre_pull_images to a list of image refs to pull, or
# empty list to skip pre-pulling. # empty list to skip pre-pulling.
#
# IMPORTANT: Do NOT use this mechanism for:
# - CI runner container images (e.g. ci-full) — these are already
# cached by the runner setup task and pulling them here is redundant.
# - Images that molecule tests pull themselves — molecule prepare/converge
# steps handle their own image pulls; pre-pulling them here wastes time
# and disk space.
# This mechanism is intended only for images that are needed by the runner
# itself but not pulled by any molecule scenario or runner setup step.
- name: Pre-pull Docker images for CI runner - name: Pre-pull Docker images for CI runner
ansible.builtin.command: "docker pull {{ item }}" ansible.builtin.command: "docker pull {{ item }}"
@@ -0,0 +1,14 @@
[Unit]
Description=Pre-pull Docker images for CI runner cache
After=docker.service
Wants=docker.service
[Service]
Type=oneshot
Environment=DOCKER_HOST=unix:///run/user/{{ gitea_runner_uid }}/docker.sock
Environment=XDG_RUNTIME_DIR=/run/user/{{ gitea_runner_uid }}
# Pull each image quietly. docker pull exits 0 if image is already up-to-date,
# so this is idempotent. Errors are non-fatal (image may already be cached).
{% for image in gitea_runner_pre_cache_images %}
ExecStart=/usr/bin/docker pull -q {{ image }}
{% endfor %}
@@ -0,0 +1,10 @@
[Unit]
Description=Periodic Docker image pre-cache for CI runner
[Timer]
OnCalendar={{ gitea_runner_pre_cache_schedule }}
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
@@ -9,3 +9,13 @@ runner:
container: container:
label: "{{ gitea_runner_container_label }}" label: "{{ gitea_runner_container_label }}"
docker_host: "unix:///run/user/{{ gitea_runner_uid }}/docker.sock" docker_host: "unix:///run/user/{{ gitea_runner_uid }}/docker.sock"
force_pull: {{ gitea_runner_force_pull | lower }}
{% if gitea_runner_container_options | length > 0 %}
options: "{{ gitea_runner_container_options }}"
{% endif %}
{% if gitea_runner_valid_volumes | length > 0 %}
valid_volumes:
{% for volume in gitea_runner_valid_volumes %}
- "{{ volume }}"
{% endfor %}
{% endif %}
+6 -6
View File
@@ -8,12 +8,12 @@ Each runner runs in an isolated **rootless Docker** environment under a dedicate
[![CI](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions/workflows/ci.yml/badge.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions) [![CI](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions/workflows/ci.yml/badge.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![License: GPL-3.0](https://img.shields.io/badge/license-GPL--3.0-blue)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/src/branch/master/LICENSE) [![License: GPL-3.0](https://img.shields.io/badge/license-GPL--3.0-blue)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/src/branch/master/LICENSE)
[![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5ba6b90dffc24876035267268dd3780f7089264c/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions) [![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/fe21232ef9dd554a49e4309bd918d358194b65d8/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5ba6b90dffc24876035267268dd3780f7089264c/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions) [![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/fe21232ef9dd554a49e4309bd918d358194b65d8/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5ba6b90dffc24876035267268dd3780f7089264c/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki) [![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/fe21232ef9dd554a49e4309bd918d358194b65d8/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
[![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5ba6b90dffc24876035267268dd3780f7089264c/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions) [![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/fe21232ef9dd554a49e4309bd918d358194b65d8/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
[![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5ba6b90dffc24876035267268dd3780f7089264c/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases) [![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/fe21232ef9dd554a49e4309bd918d358194b65d8/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
[![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/5ba6b90dffc24876035267268dd3780f7089264c/python.svg)](https://www.python.org/downloads/) [![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/raw/commit/fe21232ef9dd554a49e4309bd918d358194b65d8/python.svg)](https://www.python.org/downloads/)
## Overview ## Overview
+47
View File
@@ -0,0 +1,47 @@
# GRM-162: Add pre-cache timer, force_pull, and Docker socket options to runner config
## Problem
CI containers were not using the host's rootless Docker daemon, leading to
"no space left on device" errors. The runner config template was missing
`force_pull`, `options` (host Docker socket mount), and `valid_volumes`
fields. Additionally, no pre-cache timer existed to prevent thundering-herd
registry timeouts when all runners pull images simultaneously.
## Approach
REQ-1: Add `force_pull: false` to runner config template (explicit default
so the runner reuses locally cached images instead of pulling on every job)
REQ-2: Add `options` field to mount host rootless Docker socket as
`/run/host-docker.sock` so `start_docker.py` inside CI containers can detect
and use the host daemon (full disk, no nested DinD)
REQ-3: Add `valid_volumes` list for the socket mount targets (validated by
the runner against `container.options` and job-level volumes)
REQ-4: Add `pre_cache.yml` task with a systemd user timer that pre-pulls CI
images every 6 hours (configurable via `gitea_runner_pre_cache_schedule`)
REQ-5: Add `docker-pull-images.service.j2` and `docker-pull-images.timer.j2`
templates for the pre-cache timer
REQ-6: Timer is disabled when `gitea_runner_pre_cache_schedule` is empty or
`gitea_runner_pre_cache_images` is empty (graceful degradation)
## Test Plan
- `make lint-ci` passes (ansible-lint on new task/template files)
- `make molecule` converges successfully with the new pre-cache tasks
- Verify the runner config template renders correctly with and without
container options/valid_volumes
## Deploy Plan
- Merge to master → post-merge auto-publishes package
- Infra dependency PR auto-created to bump pinned grm version
- Runners pick up the new config on next `make setup` or ansible apply
## Rollback Plan
- Revert the merge commit
- Set `gitea_runner_pre_cache_schedule: ""` to disable the timer without
reverting
## Acceptance Criteria
- [x] REQ-1: `force_pull: false` in runner config template
- [x] REQ-2: `options` field mounts host Docker socket as `/run/host-docker.sock`
- [x] REQ-3: `valid_volumes` list includes both socket mount targets
- [x] REQ-4: `pre_cache.yml` task creates and manages systemd user timer
- [x] REQ-5: Service and timer templates created
- [x] REQ-6: Timer disabled gracefully when schedule or images empty
+38
View File
@@ -0,0 +1,38 @@
# GRM-172: Audit and document pre-pull image usage guidelines
## Problem
The grm repo contains a runner-level `pre_pull_images.yml` task file that
pre-pulls Docker images to avoid repeated pulls on every CI run. However,
there was no audit confirming that molecule `prepare.yml` files are not
also redundantly pre-pulling images that the runner setup already caches.
Wasteful pre-pulling wastes CI time and disk space.
## Approach
Audit all molecule `prepare.yml` files in the grm repo for pre-pull tasks.
The audit found NO molecule prepare.yml files contain pre-pull tasks, so no
code removal is needed. Document the audit findings in a spec and add a
comment to the runner-level `pre_pull_images.yml` task file clarifying that
it should not be used for images that molecule tests pull themselves (to
avoid redundant pulls).
REQ-1: Audit all molecule prepare.yml files for pre-pull tasks and confirm none exist
REQ-2: Add documentation comment to pre_pull_images.yml stating it should not be used for CI runner container images (already cached by runner setup) or images molecule tests pull themselves
REQ-3: Confirm gitea_runner_pre_pull_images default remains empty ([]) which is correct
## Test Plan
- Grep all molecule prepare.yml files for pre-pull patterns confirms zero matches
- Verify pre_pull_images.yml comment is present and accurate
- Verify gitea_runner_pre_pull_images default is [] in defaults/main.yml
- Run make lint-ci to confirm no lint regressions
## Deploy Plan
- Merge to master via auto-merge workflow
- No runtime changes; documentation-only
## Rollback Plan
- Revert the merge commit; comments are removed, no functional impact
## Acceptance Criteria
- [x] REQ-1: No molecule prepare.yml files in the grm repo contain pre-pull tasks (audit confirmed via grep)
- [x] REQ-2: pre_pull_images.yml contains a comment documenting it should not be used for CI runner container images or images molecule tests pull themselves
- [x] REQ-3: gitea_runner_pre_pull_images default remains empty ([]) in defaults/main.yml
+46
View File
@@ -0,0 +1,46 @@
# GRM-173: Add dependency-graph, deployment-coordination, and skill-creation skills
## Problem
Agents working across the oblachno ecosystem lack shared, written context
for three recurring struggles: (1) knowing which repo produces what and
the correct order for cross-repo changes, (2) coordinating grm releases
with the downstream infra dependency PR, and (3) creating and validating
new Devin skills consistently. Without these skills, agents repeatedly
make mistakes such as deploying infra before the grm dependency PR is
merged, or writing skills that fail the validator.
## Approach
Add three skill files under `.devin/skills/`. Two are shared skills
(`dependency-graph`, `skill-creation`) that must be identical across
repos; one is grm-specific (`deployment-coordination`). All three
follow the standard skill structure (H1 title, When to Invoke,
Prerequisites, core content) and reference real make targets, file
paths, and API endpoints.
REQ-1: Add `.devin/skills/dependency-graph/SKILL.md` — shared skill mapping the oblachno ecosystem (repos, produces/consumers, dependency chain, correct change order, state verification)
REQ-2: Add `.devin/skills/deployment-coordination/SKILL.md` — grm-specific skill covering release flow, downstream consumer, coordinating a grm change, and common mistakes
REQ-3: Add `.devin/skills/skill-creation/SKILL.md` — shared skill for creating, validating, and maintaining skills (structure, quality standards, scope rules, automated validation, checklist)
## Files Affected
- `.devin/skills/dependency-graph/SKILL.md` (new)
- `.devin/skills/deployment-coordination/SKILL.md` (new)
- `.devin/skills/skill-creation/SKILL.md` (new)
- `docs/specs/GRM-173.md` (new)
## Test Plan
- Verify all three SKILL.md files follow the required structure (H1, When to Invoke, Prerequisites)
- Verify referenced make targets and file paths are accurate
- Run `make pytest-cov` to confirm no test regressions (skills are docs-only, no code changes)
- Confirm shared skills (`dependency-graph`, `skill-creation`) are ready for cross-repo sync
## Deploy Plan
- Merge to master via auto-merge workflow
- No runtime changes; documentation-only (`.devin/**` is infrastructure path, no release triggered)
## Rollback Plan
- Revert the merge commit; skill files are removed, no functional impact
## Acceptance Criteria
- [x] REQ-1: `.devin/skills/dependency-graph/SKILL.md` exists with ecosystem map, dependency chain, correct change order, and state verification sections
- [x] REQ-2: `.devin/skills/deployment-coordination/SKILL.md` exists with release flow, downstream consumer table, coordination steps, and common mistakes
- [x] REQ-3: `.devin/skills/skill-creation/SKILL.md` exists with skill structure template, quality standards, scope rules, automated validation, and creation checklist
+1 -1
View File
@@ -1,3 +1,3 @@
"""Gitea Runner Manager — lean CLI for managing Gitea Actions runners.""" """Gitea Runner Manager — lean CLI for managing Gitea Actions runners."""
__version__ = "0.22.1" __version__ = "0.23.0"