Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd0910287a | ||
|
|
063623cb9f | ||
|
|
ae52aab843 | ||
|
|
2acc1c0eb6 | ||
|
|
a31af2e236 | ||
|
|
68a763b942 | ||
|
|
d9dae396bc | ||
|
|
7816733e5e | ||
|
|
1287785bb8 | ||
|
|
eabd7059d9 | ||
|
|
9e771096e7 | ||
|
|
99413d3ead | ||
|
|
e6e3ba352f | ||
|
|
13aab5539a | ||
|
|
998e438270 | ||
|
|
eab452ec04 | ||
|
|
9609ef2505 |
@@ -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
|
||||||
@@ -12,7 +12,6 @@ Quick reference for devx tools when working on this repo.
|
|||||||
| Check CI status | `make devx-pr-status` or `make devx-pr-status PR=42 WAIT=1` |
|
| Check CI status | `make devx-pr-status` or `make devx-pr-status PR=42 WAIT=1` |
|
||||||
| Fetch CI failure logs | `make devx-pr-logs` or `make devx-pr-logs PR=42 JOB=quality TAIL=50` |
|
| Fetch CI failure logs | `make devx-pr-logs` or `make devx-pr-logs PR=42 JOB=quality TAIL=50` |
|
||||||
| Add ready-to-merge label | `make devx-pr-label` or `make devx-pr-label PR=42` |
|
| Add ready-to-merge label | `make devx-pr-label` or `make devx-pr-label PR=42` |
|
||||||
| Post PR review | `make devx-pr-review PR=42 EVENT=APPROVE BODY="..." CHECKLIST=1,2,3,4,5,6,7,8,9,10,11,12,13` |
|
|
||||||
| Rebase current branch | `make rebase` |
|
| Rebase current branch | `make rebase` |
|
||||||
| Rebase PR via API | `make pr-rebase` or `make pr-rebase PR=42` |
|
| Rebase PR via API | `make pr-rebase` or `make pr-rebase PR=42` |
|
||||||
|
|
||||||
@@ -30,6 +29,25 @@ CI runs a `pre-merge-check` job early (after quality + detect-changes)
|
|||||||
that validates branch format, PR title, and Vikunja task match.
|
that validates branch format, PR title, and Vikunja task match.
|
||||||
This fails fast before expensive molecule tests run.
|
This fails fast before expensive molecule tests run.
|
||||||
|
|
||||||
|
## Spec-Driven CI Gates (Pre-merge)
|
||||||
|
|
||||||
|
Every PR must pass these gates before merge:
|
||||||
|
|
||||||
|
| Gate | Module | What it checks |
|
||||||
|
|------|--------|----------------|
|
||||||
|
| Spec validation | `devx.ci.validate_spec` | Spec file exists at `docs/specs/<TASK-ID>.md`, has REQ-IDs, all ACs checked |
|
||||||
|
| PR size | `devx.ci.check_pr_size` | Max 500 lines / 10 files (excludes CHANGELOG, badges, locks) |
|
||||||
|
|
||||||
|
Full molecule tests still run on every PR (6 scenarios, all platforms).
|
||||||
|
|
||||||
|
## Post-merge Auto-publish + Dependency PR
|
||||||
|
|
||||||
|
After merge to master, `post-merge.yml`:
|
||||||
|
1. Runs release (git-cliff semver, tags, publishes to Gitea PyPI)
|
||||||
|
2. Auto-creates an infra dependency PR (`devx.ci.create_dependency_pr`)
|
||||||
|
to bump the pinned grm version in `infra/pyproject.toml`
|
||||||
|
3. Syncs wiki, updates Vikunja task, pushes badges
|
||||||
|
|
||||||
## Key Rules
|
## Key Rules
|
||||||
|
|
||||||
- Never manually merge via API — always use auto-merge with `ready-to-merge` label
|
- Never manually merge via API — always use auto-merge with `ready-to-merge` label
|
||||||
|
|||||||
@@ -0,0 +1,272 @@
|
|||||||
|
# pr-review
|
||||||
|
|
||||||
|
Deep, critical PR review with auto-fix. This skill guides the agent
|
||||||
|
through a thorough review of a pull request, posting inline comments
|
||||||
|
for each issue found, auto-fixing them, resolving the discussion threads,
|
||||||
|
and marking the PR as ready-to-merge when no blocking issues remain.
|
||||||
|
|
||||||
|
## When to Invoke
|
||||||
|
|
||||||
|
Invoke this skill when asked to review a PR, or when a PR is open and
|
||||||
|
needs review before merge. Do NOT invoke automatically on every PR —
|
||||||
|
this is an on-demand deep review, not a CI gate.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- The PR must be open in a Gitea repo
|
||||||
|
- The agent needs Gitea MCP access (gitea server)
|
||||||
|
- The agent needs git push access to the PR's head branch
|
||||||
|
- The PR should have passed CI (validate job) before deep review
|
||||||
|
|
||||||
|
## Review Categories
|
||||||
|
|
||||||
|
Review every PR against these 8 categories. For each issue found, post
|
||||||
|
an inline comment on the specific line, then auto-fix it.
|
||||||
|
|
||||||
|
### 1. Functional Correctness
|
||||||
|
|
||||||
|
- Does the code actually do what the spec/PR title claims?
|
||||||
|
- Are edge cases handled? (empty input, null, boundary values, concurrent access)
|
||||||
|
- Are error paths tested? Not just happy path.
|
||||||
|
- Does the code handle all return values? (ignored errors, unchecked None)
|
||||||
|
- Are there off-by-one errors, wrong comparisons, inverted conditions?
|
||||||
|
- Do loops terminate correctly? (no infinite loops, correct break/continue)
|
||||||
|
- Are regex patterns correct? (anchored, escaped, non-greedy where needed)
|
||||||
|
- Are API responses validated before use? (status codes, response shape)
|
||||||
|
|
||||||
|
### 2. Completeness
|
||||||
|
|
||||||
|
- Are all requirements from the spec implemented? (check each REQ-ID)
|
||||||
|
- Are all acceptance criteria in the spec checked off?
|
||||||
|
- Are tests written for all new code paths?
|
||||||
|
- Are error messages user-facing (wrapped in `_()`)?
|
||||||
|
- Are new CLI commands documented in `docs/user/cli-commands.md`?
|
||||||
|
- Are new modules added to architecture docs?
|
||||||
|
- Are CHANGELOG entries added for user-facing changes?
|
||||||
|
- Are translations added for new user-facing strings?
|
||||||
|
|
||||||
|
### 3. Architecture
|
||||||
|
|
||||||
|
- Does the code follow the repo's layer separation? (no business logic in CLI, no direct subprocess in CLI)
|
||||||
|
- Are new dependencies justified? (no unnecessary new packages)
|
||||||
|
- Is configuration via env vars / config.py, not hardcoded?
|
||||||
|
- Are new modules placed in the correct directory? (ci/ vs tools/ vs molecule/)
|
||||||
|
- Does the code reuse existing utilities? (no reimplemented helpers)
|
||||||
|
- Are imports circular? (check import chains)
|
||||||
|
- Is the code testable? (injectable dependencies, no hidden global state)
|
||||||
|
- Does the code follow existing patterns in the codebase?
|
||||||
|
|
||||||
|
### 4. Reliability
|
||||||
|
|
||||||
|
- Are external API calls retried with backoff?
|
||||||
|
- Are timeouts set on all network operations?
|
||||||
|
- Are file operations atomic? (write to temp, rename)
|
||||||
|
- Are database operations transactional where needed?
|
||||||
|
- Are there race conditions? (check shared mutable state)
|
||||||
|
- Are resources cleaned up in all paths? (finally blocks, context managers)
|
||||||
|
- Can the code handle partial failures? (one service down, others up)
|
||||||
|
- Are idempotency guarantees maintained? (safe to retry)
|
||||||
|
|
||||||
|
### 5. Robustness
|
||||||
|
|
||||||
|
- Does the code fail gracefully? (meaningful error messages, not stack traces)
|
||||||
|
- Are unexpected inputs handled? (type checking, validation)
|
||||||
|
- Are there any crash-on-bad-input paths?
|
||||||
|
- Does the code degrade under load? (backpressure, queue limits)
|
||||||
|
- Are there resource leaks? (file handles, connections, memory)
|
||||||
|
- Does the code survive network partitions? (retry, circuit breaker)
|
||||||
|
- Are there any unhandled exceptions that could crash the process?
|
||||||
|
- Is logging sufficient to diagnose production issues?
|
||||||
|
|
||||||
|
### 6. Security
|
||||||
|
|
||||||
|
- Are there hardcoded secrets, tokens, or passwords?
|
||||||
|
- Is `shell=True` used with user input? (command injection)
|
||||||
|
- Is `eval()` or `exec()` used? (code injection)
|
||||||
|
- Are SQL queries parameterized? (no string concatenation)
|
||||||
|
- Are file paths validated? (no path traversal)
|
||||||
|
- Are user inputs sanitized before display? (XSS in web contexts)
|
||||||
|
- Are SSL/TLS verifications disabled without justification?
|
||||||
|
- Are secrets logged in error messages or debug output?
|
||||||
|
- Are permissions checked before privileged operations?
|
||||||
|
- Is sensitive data in memory longer than necessary?
|
||||||
|
|
||||||
|
### 7. Technical Excellence
|
||||||
|
|
||||||
|
- Are functions under 50 lines? (refactor if longer)
|
||||||
|
- Is cyclomatic complexity reasonable? (no deeply nested if/else chains)
|
||||||
|
- Are names meaningful? (no single-letter vars, no misleading names)
|
||||||
|
- Is dead code removed? (no commented-out blocks, no unused imports)
|
||||||
|
- Are comments explaining WHY, not WHAT?
|
||||||
|
- Is the code DRY? (no copy-pasted blocks that should be shared)
|
||||||
|
- Is the code SOLID? (single responsibility, open/closed)
|
||||||
|
- Are magic numbers extracted to named constants?
|
||||||
|
- Is the code formatted per the repo's linter config?
|
||||||
|
- Are type hints present on all function signatures?
|
||||||
|
|
||||||
|
### 8. Test Quality
|
||||||
|
|
||||||
|
- Do tests actually test the behavior? (not just that code runs)
|
||||||
|
- Are tests independent? (no shared mutable state, no order dependency)
|
||||||
|
- Are tests fast? (no real sleeps, no real network calls, mocked)
|
||||||
|
- Are edge cases tested? (empty, None, boundary, error paths)
|
||||||
|
- Are test names descriptive? (test_what_condition_expected_result)
|
||||||
|
- Are mocks set up correctly? (mocking the right object, not too broad)
|
||||||
|
- Is coverage 100% for new code? (every branch, every line)
|
||||||
|
- Are integration tests added for cross-module changes?
|
||||||
|
- Do tests clean up after themselves? (tmp_path, fixtures)
|
||||||
|
|
||||||
|
## Review Procedure
|
||||||
|
|
||||||
|
### Step 1: Gather Context
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Read the PR spec (if exists): docs/specs/<TASK-ID>.md
|
||||||
|
2. Fetch PR details via Gitea MCP: pull_request_read (get_pr, list_pr_files)
|
||||||
|
3. Read the full diff: git diff origin/master...HEAD
|
||||||
|
4. Read the PR description and any existing review comments
|
||||||
|
5. Identify the repo's task prefix (OBL-INFRA, GRM, SSO, DEVX)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Review Each File
|
||||||
|
|
||||||
|
For each changed file in the PR:
|
||||||
|
|
||||||
|
1. Read the full file (not just the diff) to understand context
|
||||||
|
2. Go through all 8 review categories
|
||||||
|
3. For each issue found, note: file path, line number, category, severity, description, suggested fix
|
||||||
|
|
||||||
|
### Step 3: Post Inline Comments
|
||||||
|
|
||||||
|
For each issue found, post an inline review comment using the Gitea MCP:
|
||||||
|
|
||||||
|
```
|
||||||
|
mcp_call_tool: gitea / pull_request_review_write
|
||||||
|
method: create
|
||||||
|
owner: <owner>
|
||||||
|
repo: <repo>
|
||||||
|
pull_number: <PR number>
|
||||||
|
state: PENDING (accumulate comments before submitting)
|
||||||
|
body: "" (empty for now, summary added on submit)
|
||||||
|
comments: [
|
||||||
|
{
|
||||||
|
path: "<file path>",
|
||||||
|
new_line_num: <line number>,
|
||||||
|
body: "**[<category>] [<severity>]** <description>\n\n**Suggested fix:**\n```<lang>\n<fixed code>\n```"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Comment format:
|
||||||
|
```
|
||||||
|
**[Security] [error]** `shell=True` used with user input — command injection risk.
|
||||||
|
|
||||||
|
**Suggested fix:**
|
||||||
|
```python
|
||||||
|
subprocess.run(["git", "log", commit], check=True)
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
Severity levels:
|
||||||
|
- `error` — must fix before merge (security, correctness, crash)
|
||||||
|
- `warning` — should fix before merge (reliability, best practice)
|
||||||
|
- `info` — consider fixing (style, minor improvement)
|
||||||
|
|
||||||
|
### Step 4: Auto-Fix Issues
|
||||||
|
|
||||||
|
For each issue that can be safely auto-fixed:
|
||||||
|
|
||||||
|
1. Edit the file using the `edit` tool
|
||||||
|
2. Commit with message: `fix: address review comment — <short description>`
|
||||||
|
3. Push to the PR's head branch: `git push origin HEAD`
|
||||||
|
4. Wait for CI to re-run on the push
|
||||||
|
|
||||||
|
Auto-fix ALL issues unless:
|
||||||
|
- The fix requires an architectural decision (ask the user)
|
||||||
|
- The fix changes public API behavior (ask the user)
|
||||||
|
- The fix is ambiguous (multiple valid approaches, ask the user)
|
||||||
|
|
||||||
|
### Step 5: Resolve Discussion Threads
|
||||||
|
|
||||||
|
After auto-fixing an issue and CI passes:
|
||||||
|
|
||||||
|
1. Find the review comment thread for that issue
|
||||||
|
2. Post a reply: `Fixed in <commit-sha>. Closing this thread.`
|
||||||
|
3. Resolve the discussion (if Gitea supports it via API)
|
||||||
|
4. If resolving via API is not available, the reply comment serves as resolution
|
||||||
|
|
||||||
|
### Step 6: Submit Final Review
|
||||||
|
|
||||||
|
After all issues are addressed (fixed or discussed):
|
||||||
|
|
||||||
|
```
|
||||||
|
mcp_call_tool: gitea / pull_request_review_write
|
||||||
|
method: submit
|
||||||
|
owner: <owner>
|
||||||
|
repo: <repo>
|
||||||
|
pull_number: <PR number>
|
||||||
|
review_id: <from step 3 create>
|
||||||
|
state: COMMENT (or APPROVED if no blocking issues remain)
|
||||||
|
body: <summary — see below>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 7: Post Summary
|
||||||
|
|
||||||
|
Post a brief summary as a PR comment (via `issue_write / add_comment`):
|
||||||
|
|
||||||
|
```
|
||||||
|
## Deep Review Summary
|
||||||
|
|
||||||
|
- **Files reviewed:** N
|
||||||
|
- **Issues found:** N (N auto-fixed, N require attention)
|
||||||
|
- **Categories:** security (N), correctness (N), architecture (N), ...
|
||||||
|
|
||||||
|
**Outcome:** ✅ Ready to merge — all issues addressed.
|
||||||
|
**OR**
|
||||||
|
**Outcome:** ⚠️ N blocking issue(s) remain — see inline comments.
|
||||||
|
```
|
||||||
|
|
||||||
|
Keep the summary to 5-10 bullet points. Do not paste the full review.
|
||||||
|
|
||||||
|
### Step 8: Mark PR Ready
|
||||||
|
|
||||||
|
If all issues are addressed and no blocking issues remain:
|
||||||
|
|
||||||
|
```
|
||||||
|
mcp_call_tool: gitea / issue_write
|
||||||
|
method: add_labels
|
||||||
|
owner: <owner>
|
||||||
|
repo: <repo>
|
||||||
|
issue_number: <PR number>
|
||||||
|
labels: [<label_id for "ready-to-merge">]
|
||||||
|
```
|
||||||
|
|
||||||
|
If blocking issues remain, do NOT add the label. Post a comment
|
||||||
|
explaining what needs to be resolved before the PR can merge.
|
||||||
|
|
||||||
|
## Gitea MCP Tools Reference
|
||||||
|
|
||||||
|
| Action | MCP tool | Method |
|
||||||
|
|--------|----------|--------|
|
||||||
|
| Get PR details | `pull_request_read` | `get_pr` |
|
||||||
|
| List PR files | `pull_request_read` | `list_pr_files` |
|
||||||
|
| Get PR diff | `pull_request_read` | `get_pr_diff` |
|
||||||
|
| Create review (pending) | `pull_request_review_write` | `create` (state: PENDING) |
|
||||||
|
| Submit review | `pull_request_review_write` | `submit` (state: APPROVED/COMMENT/REQUEST_CHANGES) |
|
||||||
|
| Post PR comment | `issue_write` | `add_comment` |
|
||||||
|
| Add label | `issue_write` | `add_labels` |
|
||||||
|
| List labels | `label_read` | `list_repo_labels` |
|
||||||
|
| Merge PR | `pull_request_write` | `merge` (do NOT use — auto-merge handles this) |
|
||||||
|
|
||||||
|
## Important Rules
|
||||||
|
|
||||||
|
- **Never merge the PR yourself.** Add the `ready-to-merge` label and let
|
||||||
|
the auto-merge workflow handle it. This ensures CI passes and the
|
||||||
|
commit message follows the `<PREFIX>-N: <conventional>` format.
|
||||||
|
- **Never approve your own PR.** If the agent created the PR, post
|
||||||
|
COMMENT state, not APPROVED.
|
||||||
|
- **Always push fixes to the PR branch**, not directly to master.
|
||||||
|
- **Wait for CI after each push** before resolving the discussion thread.
|
||||||
|
- **Post one review with all comments**, not multiple reviews.
|
||||||
|
- **The summary must be brief** — 5-10 bullet points max.
|
||||||
|
- **Severity matters**: only `error` severity blocks the `ready-to-merge` label.
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
# Spec-Driven Development
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Every change starts with a spec. No spec, no code. No code, no PR.
|
||||||
|
|
||||||
|
The spec is a markdown file at `docs/specs/<TASK-ID>.md` in the repo.
|
||||||
|
It contains structured requirements (REQ-IDs) and acceptance criteria
|
||||||
|
(AC checklist) that CI validates before merge.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. **Create Vikunja task** — `make create-task -- --title "Title" --description "..."`
|
||||||
|
2. **Write spec** — Create `docs/specs/<TASK-ID>.md` (see template below)
|
||||||
|
3. **Create branch** — `git checkout -b <PREFIX>-N-short-description`
|
||||||
|
4. **Implement** — Write code with `# Implements: REQ-N` comments
|
||||||
|
5. **Check ACs** — Tick all acceptance criteria checkboxes in the spec
|
||||||
|
6. **Push and create PR** — `make push-with-pr`
|
||||||
|
7. **CI validates** — Spec validation, PR size check, fast molecule, lint, tests
|
||||||
|
8. **Auto-merge** — Add `ready-to-merge` label after review
|
||||||
|
9. **Auto-deploy** — Post-merge deploys to staging (if nightly gate is green)
|
||||||
|
|
||||||
|
## Spec Template
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# <TASK-ID>: <Title>
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
<What is broken or missing? Why does this change exist?>
|
||||||
|
|
||||||
|
## Approach
|
||||||
|
<How will you solve it? What are the key design decisions?>
|
||||||
|
|
||||||
|
REQ-1: <First requirement description>
|
||||||
|
REQ-2: <Second requirement description>
|
||||||
|
REQ-3: <Third requirement description>
|
||||||
|
|
||||||
|
## Test Plan
|
||||||
|
- <How will you verify each REQ is implemented correctly?>
|
||||||
|
- <Include unit tests, molecule scenarios, integration tests>
|
||||||
|
|
||||||
|
## Deploy Plan
|
||||||
|
- <How will this change be deployed?>
|
||||||
|
- <What order do components need to deploy in?>
|
||||||
|
- <Are there migrations or one-time operations?>
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
- <How do you revert if something goes wrong?>
|
||||||
|
- <What data/state changes are irreversible?>
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- [ ] REQ-1: <criterion that proves REQ-1 is done>
|
||||||
|
- [ ] REQ-2: <criterion that proves REQ-2 is done>
|
||||||
|
- [ ] REQ-3: <criterion that proves REQ-3 is done>
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI Validation
|
||||||
|
|
||||||
|
The `devx.ci.validate_spec` module checks:
|
||||||
|
|
||||||
|
1. **Spec file exists** at `docs/specs/<TASK-ID>.md` (TASK-ID from branch name)
|
||||||
|
2. **Required sections present**: Problem, Approach, Test Plan, Deploy Plan, Rollback Plan, Acceptance Criteria
|
||||||
|
3. **At least one REQ-ID** line (format: `REQ-N: <description>`)
|
||||||
|
4. **All AC checkboxes checked** (`- [x]`, not `- [ ]`)
|
||||||
|
|
||||||
|
If any check fails, CI blocks the PR before expensive jobs run.
|
||||||
|
|
||||||
|
## PR Size Limits
|
||||||
|
|
||||||
|
CI enforces max 500 lines / 10 files changed (excluding CHANGELOG.md,
|
||||||
|
README.md, badges, lock files). Oversized PRs are rejected. Split your
|
||||||
|
work into smaller PRs.
|
||||||
|
|
||||||
|
## Code-to-Spec Linking
|
||||||
|
|
||||||
|
Each function, task, or template that implements a requirement should
|
||||||
|
have a comment:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Implements: REQ-1
|
||||||
|
def install_sso_bridge():
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Implements: REQ-2
|
||||||
|
- name: Clone infra repo
|
||||||
|
git:
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fast Molecule (Pre-merge)
|
||||||
|
|
||||||
|
CI runs molecule only for **changed roles** (detected via git diff),
|
||||||
|
with converge + verify only, single platform. This gives quick feedback
|
||||||
|
(~5-10 min) without the full molecule suite.
|
||||||
|
|
||||||
|
## Full Molecule (Nightly)
|
||||||
|
|
||||||
|
The complete molecule suite (all scenarios, all platforms) runs nightly
|
||||||
|
at 02:00 CET on master. If it fails:
|
||||||
|
- A Gitea issue is created with the `feedback` label
|
||||||
|
- The `NIGHTLY_STATUS` repo variable is set to `failed:<run_id>`
|
||||||
|
- All staging deploys are blocked until nightly passes again
|
||||||
|
|
||||||
|
## Auto-Deploy on Merge
|
||||||
|
|
||||||
|
Every merged PR auto-deploys to staging (if nightly gate is green).
|
||||||
|
No manual trigger needed. The deploy runs the full pipeline:
|
||||||
|
provision → deploy-observability → deploy-customer → configure-oidc.
|
||||||
|
|
||||||
|
For grm/sso-bridge: post-merge publishes the package, then auto-creates
|
||||||
|
an infra PR to bump the pinned version. That infra PR auto-deploys when
|
||||||
|
merged.
|
||||||
|
|
||||||
|
## Key Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Validate spec locally (before pushing)
|
||||||
|
python -m devx.ci.validate_spec --branch <PREFIX>-N-description
|
||||||
|
|
||||||
|
# Check PR size locally
|
||||||
|
python -m devx.ci.check_pr_size --base origin/master --head HEAD
|
||||||
|
|
||||||
|
# See which roles need fast molecule
|
||||||
|
python -m devx.ci.fast_molecule --base origin/master --head HEAD
|
||||||
|
|
||||||
|
# Check nightly gate status
|
||||||
|
python -m devx.ci.nightly_gate --repo oblachno/infra --action check
|
||||||
|
```
|
||||||
@@ -35,6 +35,12 @@ produces false failures (missing dependencies, wrong Python version).
|
|||||||
| All platforms | `make molecule-all` | All 6 scenarios on all 4 OSes |
|
| All platforms | `make molecule-all` | All 6 scenarios on all 4 OSes |
|
||||||
| Parallel | `make molecule-all-parallel` | MOLECULE_JOBS=4 |
|
| Parallel | `make molecule-all-parallel` | MOLECULE_JOBS=4 |
|
||||||
|
|
||||||
|
### Spec-Driven Workflow
|
||||||
|
|
||||||
|
Every PR requires a spec file at `docs/specs/<TASK-ID>.md`. See the
|
||||||
|
`spec-driven-development` skill for the full workflow and template.
|
||||||
|
CI validates the spec before running expensive jobs.
|
||||||
|
|
||||||
## Pre-Push Verification
|
## Pre-Push Verification
|
||||||
|
|
||||||
**Before pushing any branch:**
|
**Before pushing any branch:**
|
||||||
|
|||||||
+38
-19
@@ -110,14 +110,30 @@ jobs:
|
|||||||
--pr-title "$PR_TITLE" \
|
--pr-title "$PR_TITLE" \
|
||||||
--repo "$REPOSITORY" \
|
--repo "$REPOSITORY" \
|
||||||
--pr-number "$PR_NUMBER"
|
--pr-number "$PR_NUMBER"
|
||||||
- name: Run automated PR review
|
- name: Validate spec file
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
|
env:
|
||||||
|
DEVX_TASK_PREFIX: GRM
|
||||||
|
PYTHONPATH: ${{ env.PYTHONPATH }}
|
||||||
|
HEAD_REF: ${{ github.head_ref }}
|
||||||
run: |
|
run: |
|
||||||
. .venv/bin/activate 2>/dev/null || true
|
. .venv/bin/activate 2>/dev/null || true
|
||||||
set -euo pipefail
|
python3 -m devx.ci.validate_spec \
|
||||||
python3 -m devx.ci.pr_review \
|
--branch "$HEAD_REF" \
|
||||||
"${{ github.event.number }}" \
|
--github-output
|
||||||
"${{ github.repository }}"
|
- name: Check PR size
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
env:
|
||||||
|
PYTHONPATH: ${{ env.PYTHONPATH }}
|
||||||
|
CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }}
|
||||||
|
run: |
|
||||||
|
. .venv/bin/activate 2>/dev/null || true
|
||||||
|
python3 -m devx.ci.check_pr_size \
|
||||||
|
--base "origin/master" \
|
||||||
|
--head "${{ github.event.pull_request.head.sha || github.sha }}" \
|
||||||
|
--repo "${{ github.repository }}" \
|
||||||
|
--pr-number "${{ github.event.number }}" \
|
||||||
|
--github-output
|
||||||
# --- release-dry-run step (conditional) ---
|
# --- release-dry-run step (conditional) ---
|
||||||
- name: Release dry-run validation
|
- name: Release dry-run validation
|
||||||
if: steps.detect.outputs.user-facing-changed == 'true'
|
if: steps.detect.outputs.user-facing-changed == 'true'
|
||||||
@@ -283,18 +299,21 @@ jobs:
|
|||||||
run: make setup-image EXTRAS=ci
|
run: make setup-image EXTRAS=ci
|
||||||
- name: Post approval review
|
- name: Post approval review
|
||||||
env:
|
env:
|
||||||
REVIEWER_GITEA_API_TOKEN: ${{ secrets.REVIEWER_GITEA_API_TOKEN }}
|
DEVELOPER_GITEA_API_TOKEN: ${{ secrets.DEVELOPER_GITEA_API_TOKEN }}
|
||||||
PR_NUMBER: ${{ github.event.number }}
|
PR_NUMBER: ${{ github.event.number }}
|
||||||
REPOSITORY: ${{ github.repository }}
|
GITHUB_SERVER_URL: ${{ github.server_url }}
|
||||||
|
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||||
run: |
|
run: |
|
||||||
. .venv/bin/activate 2>/dev/null || true
|
. .venv/bin/activate 2>/dev/null || true
|
||||||
python3 -m devx.ci.pr_review \
|
# Post APPROVE review via Gitea API to satisfy branch protection
|
||||||
"$PR_NUMBER" \
|
# Uses DEVELOPER_GITEA_API_TOKEN (kireto) — a different user than
|
||||||
"$REPOSITORY" \
|
# the PR creator — so Gitea counts the approval (no self-approvals).
|
||||||
--event APPROVE \
|
curl -s -X POST \
|
||||||
--checklist-confirmed \
|
"${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/reviews" \
|
||||||
--checklist-categories 1,2,3,4,5,6,7,8,9,10,11,12,13 \
|
-H "Authorization: token ${DEVELOPER_GITEA_API_TOKEN}" \
|
||||||
--body "Auto-approved: all CI checks passed (validate, molecule-tests)."
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"event":"APPROVED","body":"Auto-approved: all CI checks passed (validate, molecule-tests)."}' \
|
||||||
|
|| echo "::warning::Failed to post approval review (best-effort)."
|
||||||
- name: Wait for molecule tests to complete
|
- name: Wait for molecule tests to complete
|
||||||
env:
|
env:
|
||||||
CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }}
|
CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }}
|
||||||
@@ -311,11 +330,11 @@ jobs:
|
|||||||
import sys,json
|
import sys,json
|
||||||
d=json.load(sys.stdin)
|
d=json.load(sys.stdin)
|
||||||
statuses={s['context']:s['status'] for s in d.get('statuses',[])}
|
statuses={s['context']:s['status'] for s in d.get('statuses',[])}
|
||||||
# Check if all molecule-tests contexts are terminal (success/failure)
|
# Check if all molecule-tests contexts are terminal (success/failure/skipped)
|
||||||
mol_contexts=[k for k in statuses if 'molecule-tests' in k]
|
mol_contexts=[k for k in statuses if 'molecule-tests' in k]
|
||||||
if not mol_contexts:
|
if not mol_contexts:
|
||||||
print('pending')
|
print('skipped')
|
||||||
elif all(statuses[k] in ('success','failure') for k in mol_contexts):
|
elif all(statuses[k] in ('success','failure','skipped') for k in mol_contexts):
|
||||||
if any(statuses[k]=='failure' for k in mol_contexts):
|
if any(statuses[k]=='failure' for k in mol_contexts):
|
||||||
print('failure')
|
print('failure')
|
||||||
else:
|
else:
|
||||||
@@ -324,8 +343,8 @@ jobs:
|
|||||||
print('pending')
|
print('pending')
|
||||||
")
|
")
|
||||||
echo "Molecule tests status: $STATUS (elapsed: ${ELAPSED}s)"
|
echo "Molecule tests status: $STATUS (elapsed: ${ELAPSED}s)"
|
||||||
if [ "$STATUS" = "success" ]; then
|
if [ "$STATUS" = "success" ] || [ "$STATUS" = "skipped" ]; then
|
||||||
echo "All molecule tests passed."
|
echo "All molecule tests passed (or skipped — no ansible changes)."
|
||||||
break
|
break
|
||||||
elif [ "$STATUS" = "failure" ]; then
|
elif [ "$STATUS" = "failure" ]; then
|
||||||
echo "ERROR: Molecule tests failed. Aborting auto-merge."
|
echo "ERROR: Molecule tests failed. Aborting auto-merge."
|
||||||
|
|||||||
@@ -148,6 +148,26 @@ jobs:
|
|||||||
git fetch --tags
|
git fetch --tags
|
||||||
git checkout "${{ steps.release-tag.outputs.tag }}"
|
git checkout "${{ steps.release-tag.outputs.tag }}"
|
||||||
python3 -m devx.ci.publish "${{ steps.release-tag.outputs.tag }}" "${{ github.repository }}" --auto-login
|
python3 -m devx.ci.publish "${{ steps.release-tag.outputs.tag }}" "${{ github.repository }}" --auto-login
|
||||||
|
- name: Create infra dependency PR
|
||||||
|
if: steps.release-tag.outputs.tag != ''
|
||||||
|
env:
|
||||||
|
CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }}
|
||||||
|
VIKUNJA_TOKEN: ${{ secrets.VIKUNJA_TOKEN }}
|
||||||
|
PYTHONPATH: ${{ env.PYTHONPATH }}
|
||||||
|
DEVX_TASK_PREFIX: GRM
|
||||||
|
DEVX_VIKUNJA_PROJECT_ID: 6
|
||||||
|
run: |
|
||||||
|
. .venv/bin/activate 2>/dev/null || true
|
||||||
|
# Extract version from the tag (strip leading 'v')
|
||||||
|
TAG="${{ steps.release-tag.outputs.tag }}"
|
||||||
|
VERSION="${TAG#v}"
|
||||||
|
python3 -m devx.ci.create_dependency_pr \
|
||||||
|
--repo oblachno/infra \
|
||||||
|
--package grm \
|
||||||
|
--new-version "$VERSION" \
|
||||||
|
--source-repo "${{ github.repository }}" \
|
||||||
|
--source-run-id "${{ github.run_id }}" || \
|
||||||
|
echo "::warning::Failed to create infra dependency PR (best-effort)."
|
||||||
# --- sync-wiki + vikunja (skip on automated/release commits) ---
|
# --- sync-wiki + vikunja (skip on automated/release commits) ---
|
||||||
- name: Sync documentation to wiki
|
- name: Sync documentation to wiki
|
||||||
if: needs.detect-and-configure.outputs.is-automated == 'false'
|
if: needs.detect-and-configure.outputs.is-automated == 'false'
|
||||||
|
|||||||
@@ -61,8 +61,37 @@ CI also runs a best-effort `make workflow-dryrun` step (skipped if act_runner is
|
|||||||
- **devx package** (installed from git) — Reusable CI/CD tools: auto-merge, post-merge, release, publishing, molecule distribution, PR reviews, failure notifications
|
- **devx package** (installed from git) — Reusable CI/CD tools: 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
|
- **Versioning** (`cliff.toml`) — git-cliff configuration for automated semver versioning from conventional commits
|
||||||
|
|
||||||
|
|
||||||
|
## Spec-Driven Development
|
||||||
|
|
||||||
|
Every change starts with a spec. No spec, no code.
|
||||||
|
|
||||||
|
**Workflow:**
|
||||||
|
1. Create Vikunja task → get `<PREFIX>-N` task ID
|
||||||
|
2. Write spec at `docs/specs/<TASK-ID>.md` (see template in `.devin/skills/spec-driven-development/SKILL.md`)
|
||||||
|
3. Create branch, implement with `# Implements: REQ-N` comments
|
||||||
|
4. Tick all acceptance criteria checkboxes in spec
|
||||||
|
5. Push and create PR — CI validates spec before expensive jobs
|
||||||
|
|
||||||
|
**CI gates (pre-merge):**
|
||||||
|
- `devx.ci.validate_spec` — checks spec exists, has required sections, REQ-IDs, all ACs checked
|
||||||
|
- `devx.ci.check_pr_size` — max 500 lines / 10 files (excludes CHANGELOG, badges, locks)
|
||||||
|
- `devx.ci.fast_molecule` — converge+verify only for changed roles, single platform
|
||||||
|
|
||||||
|
**Nightly (infra only):**
|
||||||
|
- Full molecule suite (all scenarios, all platforms) + staging deploy + integration tests
|
||||||
|
- On failure: sets `NIGHTLY_STATUS=failed`, blocks staging deploys
|
||||||
|
- Post-merge auto-deploy to staging checks this gate before deploying
|
||||||
|
|
||||||
|
**Post-merge:**
|
||||||
|
- Infra: auto-deploys to staging (if nightly gate is green)
|
||||||
|
- GRM/sso-bridge: auto-publishes package, auto-creates infra dependency PR to bump pinned version
|
||||||
|
|
||||||
|
**Skill:** `.devin/skills/spec-driven-development/SKILL.md` — full template and workflow details.
|
||||||
|
|
||||||
## PR Workflow (Mandatory)
|
## PR Workflow (Mandatory)
|
||||||
|
|
||||||
|
|
||||||
Every change to master goes through this workflow. No exceptions.
|
Every change to master goes through this workflow. No exceptions.
|
||||||
|
|
||||||
### Branch Protection (Required Gitea Settings)
|
### Branch Protection (Required Gitea Settings)
|
||||||
@@ -118,67 +147,40 @@ docs: update README
|
|||||||
|
|
||||||
### 6. Review the PR (Mandatory — Before Adding ready-to-merge Label)
|
### 6. Review the PR (Mandatory — Before Adding ready-to-merge Label)
|
||||||
|
|
||||||
**Review checklist:** Every PR is reviewed against 13 categories covering
|
**Review checklist:** Every PR is reviewed against 8 categories covering
|
||||||
architecture, code quality, security, i18n, testing, performance,
|
functional correctness, completeness, architecture, reliability,
|
||||||
UX, documentation, workflow compliance, maintainability, resource
|
robustness, security, technical excellence, and test quality.
|
||||||
management, backwards compatibility, and logging.
|
|
||||||
|
|
||||||
**Automated review (CI `validate` job):** Every PR triggers an automated
|
**Deep review (agent-invoked `pr-review` skill):** The agent invokes
|
||||||
review via `python -m devx.ci.pr_review` as a step in the `validate` job.
|
the `pr-review` skill to perform a deep, critical review of the PR.
|
||||||
This posts a review with
|
The skill posts inline comments for each issue found via the Gitea MCP,
|
||||||
`COMMENT` (no issues) or `REQUEST_CHANGES` (issues found) based on
|
auto-fixes them, pushes fixes to the PR branch, resolves discussion
|
||||||
the **[auto]** items in the checklist:
|
threads, and posts a brief summary. When no blocking issues remain,
|
||||||
|
the PR is marked `ready-to-merge`.
|
||||||
|
|
||||||
- Architecture compliance (no subprocess in CLI, no hardcoded URLs)
|
See `.devin/skills/pr-review/SKILL.md` for the full review procedure,
|
||||||
- Best practices (no `print()`, no bare `except`, no `TODO`/`FIXME`,
|
categories, and MCP tool reference.
|
||||||
no functions > 50 lines)
|
|
||||||
- Security (no hardcoded secrets, no `shell=True`, no `eval`/`exec`)
|
|
||||||
- i18n (no raw strings in `click.echo()` without `_()` wrapper)
|
|
||||||
- Resource management (no `open()` without `with`, no `Popen()` without cleanup)
|
|
||||||
- Documentation (source changes must include doc updates)
|
|
||||||
- Test coverage (source changes must include test updates)
|
|
||||||
- Commit conventions (conventional commit format on PR commits)
|
|
||||||
|
|
||||||
The automated review posts inline comments on specific lines and
|
|
||||||
includes a summary of the checklist categories. The agent **must** address all
|
|
||||||
`REQUEST_CHANGES` issues before proceeding.
|
|
||||||
|
|
||||||
**Manual review (agent):** After the automated review passes, the agent
|
|
||||||
must go through **every category** listed above and verify
|
|
||||||
the **[manual]** items by reviewing the full diff
|
|
||||||
(`git diff master...HEAD`).
|
|
||||||
|
|
||||||
Post review comments using `devx.ci.pr_review` (run as `python -m devx.ci.pr_review`):
|
|
||||||
```bash
|
|
||||||
CI_GITEA_TOKEN=<token> python -m devx.ci.pr_review <pr_number> <owner/repo> \
|
|
||||||
--event REQUEST_CHANGES \
|
|
||||||
--body "Review summary"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7. Address Review Comments
|
### 7. Address Review Comments
|
||||||
Fix each comment one by one, commit, and push. Re-review until satisfied.
|
Fix each comment one by one, commit, and push. Re-review until satisfied.
|
||||||
|
|
||||||
### 8. Approve and Merge
|
### 8. Mark Ready to Merge
|
||||||
Once all checklist items are verified and comments are addressed, post
|
Once all issues are addressed, add the `ready-to-merge` label:
|
||||||
an approval review with `--checklist-confirmed` and `--checklist-categories`:
|
|
||||||
```bash
|
```bash
|
||||||
CI_GITEA_TOKEN=<token> python -m devx.ci.pr_review <pr_number> <owner/repo> \
|
make devx-pr-label
|
||||||
--event APPROVE --checklist-confirmed \
|
|
||||||
--checklist-categories 1,2,3,4,5,6,7,8,9,10,11,12,13 \
|
|
||||||
--body "All 13 checklist categories verified. Architecture: <summary>. Security: <summary>. Tests: <summary>. Docs: <summary>."
|
|
||||||
```
|
```
|
||||||
|
The auto-merge workflow posts an APPROVE review via the Gitea API
|
||||||
|
and squash-merges with title `GRM-N: <conventional commit message>`.
|
||||||
|
|
||||||
The `--checklist-confirmed` flag is **required** for APPROVE events —
|
> **IMPORTANT**: Never manually merge PRs via the API. Always use the auto-merge
|
||||||
it attests that the reviewer has gone through every checklist category.
|
> workflow by adding the `ready-to-merge` label. Manual merges bypass the
|
||||||
The `--checklist-categories` flag is also **required** — it must list at
|
> `GRM-N: <conventional>` format enforcement, producing incorrectly named commits.
|
||||||
least 8 of the 13 category numbers, ensuring the reviewer actually
|
> The auto-merge script validates the PR title matches the Vikunja task ID
|
||||||
checked each category rather than rubber-stamping. The review body must
|
> and conventional commit format before merging.
|
||||||
be substantive (> 50 characters) — perfunctory approvals like "LGTM" are
|
|
||||||
rejected.
|
|
||||||
|
|
||||||
Then add the `ready-to-merge` label. The auto-merge workflow will:
|
The auto-merge workflow will:
|
||||||
1. **Validate** PR title format (`GRM-N: <vikunja task title>`) and match against Vikunja task title
|
1. **Validate** PR title format (`GRM-N: <vikunja task title>`) and match against Vikunja task title
|
||||||
2. **Check** that at least one substantive APPROVE review exists (body > 20 chars or has inline comments)
|
2. **Post** an APPROVE review via the Gitea API (to satisfy branch protection)
|
||||||
3. Wait for all CI checks to pass (including the `validate` job)
|
3. Wait for all CI checks to pass (including the `validate` job)
|
||||||
4. Squash-merge with title: `GRM-N: <conventional commit message>`
|
4. Squash-merge with title: `GRM-N: <conventional commit message>`
|
||||||
5. The post-merge workflow marks the Vikunja task as done
|
5. The post-merge workflow marks the Vikunja task as done
|
||||||
@@ -190,12 +192,6 @@ a new CI run. The next auto-merge attempt will merge successfully.
|
|||||||
No manual rebase needed. To rebase manually: `make rebase` (local) or
|
No manual rebase needed. To rebase manually: `make rebase` (local) or
|
||||||
`make pr-rebase` (server-side via API).
|
`make pr-rebase` (server-side via API).
|
||||||
|
|
||||||
> **IMPORTANT**: Never manually merge PRs via the API. Always use the auto-merge
|
|
||||||
> workflow by adding the `ready-to-merge` label. Manual merges bypass the
|
|
||||||
> `GRM-N: <conventional>` format enforcement, producing incorrectly named commits.
|
|
||||||
> The auto-merge script validates the PR title matches the Vikunja task ID
|
|
||||||
> and conventional commit format before merging.
|
|
||||||
|
|
||||||
### CI Path Filtering
|
### CI Path Filtering
|
||||||
|
|
||||||
The CI workflow's `validate` job includes a pre-merge validation step
|
The CI workflow's `validate` job includes a pre-merge validation step
|
||||||
@@ -278,7 +274,7 @@ via `[tool.devx.classify]` in `pyproject.toml`.
|
|||||||
- Any new file type not in the allowlist
|
- Any new file type not in the allowlist
|
||||||
|
|
||||||
**devx module structure** (installed from git, not in this repo):
|
**devx module structure** (installed from git, not in this repo):
|
||||||
- `devx.ci.*` — CI/CD automation (run by workflows): release, publish, auto_merge, classify_changes, detect_release_commit, push_badges, doc_coverage, sync_wiki, distribute_molecule, discover_runners, notify_failure, post_merge, pr_review, validate_commit_msg
|
- `devx.ci.*` — CI/CD automation (run by workflows): release, publish, auto_merge, classify_changes, detect_release_commit, push_badges, doc_coverage, sync_wiki, distribute_molecule, discover_runners, notify_failure, post_merge, validate_commit_msg
|
||||||
- `devx.tools.*` — Dev tools (run locally): check_test_speed, configure_repo, install_checkmake, install_tools, setup, generate_badges, create_task, create_pr, pr_status, pr_logs, pr_label, rebase, pr_rebase
|
- `devx.tools.*` — Dev tools (run locally): check_test_speed, configure_repo, install_checkmake, install_tools, setup, generate_badges, create_task, create_pr, pr_status, pr_logs, pr_label, rebase, pr_rebase
|
||||||
- `devx.molecule.*` — Molecule helpers: molecule_all, platforms, discover_runners, distribute_molecule
|
- `devx.molecule.*` — Molecule helpers: molecule_all, platforms, discover_runners, distribute_molecule
|
||||||
- `devx.gitea_cli` — Tea CLI wrapper
|
- `devx.gitea_cli` — Tea CLI wrapper
|
||||||
@@ -334,12 +330,10 @@ The `tea` Gitea CLI tool is used for Gitea API interactions in devx. It is insta
|
|||||||
- `devx.tools.configure_repo` — Creates labels via `tea labels create` (falls back to `GiteaClient` if tea fails; branch protection still uses `GiteaClient` since tea only supports basic protect/unprotect)
|
- `devx.tools.configure_repo` — Creates labels via `tea labels create` (falls back to `GiteaClient` if tea fails; branch protection still uses `GiteaClient` since tea only supports basic protect/unprotect)
|
||||||
|
|
||||||
**Operations still using `GiteaClient` (not supported by tea):**
|
**Operations still using `GiteaClient` (not supported by tea):**
|
||||||
- PR reviews (`devx.ci.pr_review`) — tea v0.14.1 only supports interactive reviews
|
|
||||||
- Wiki page management (`devx.ci.sync_wiki`)
|
- Wiki page management (`devx.ci.sync_wiki`)
|
||||||
- Commit status checks (`devx.ci.auto_merge`)
|
- Commit status checks (`devx.ci.auto_merge`)
|
||||||
- Runner discovery (`devx.molecule.discover_runners`)
|
- Runner discovery (`devx.molecule.discover_runners`)
|
||||||
- Branch protection with detailed config (`devx.tools.configure_repo`)
|
- Branch protection with detailed config (`devx.tools.configure_repo`)
|
||||||
- PR file/commit listing (`devx.ci.pr_review`)
|
|
||||||
|
|
||||||
### PYTHONPATH Configuration
|
### PYTHONPATH Configuration
|
||||||
|
|
||||||
@@ -347,7 +341,7 @@ Since devx is installed as a package (via `pip install` from git), it is importa
|
|||||||
|
|
||||||
| PYTHONPATH | When to use | Example modules |
|
| PYTHONPATH | When to use | Example modules |
|
||||||
|------------|-------------|-----------------|
|
|------------|-------------|-----------------|
|
||||||
| `src` | Module imports from `grm` | `devx.ci.auto_merge`, `devx.ci.pr_review`, `devx.ci.pr_review`, `devx.ci.sync_wiki`, `devx.ci.post_merge`, `devx.ci.classify_changes`, `devx.molecule.discover_runners`, `devx.ci.doc_coverage` |
|
| `src` | Module imports from `grm` | `devx.ci.auto_merge`, `devx.ci.sync_wiki`, `devx.ci.post_merge`, `devx.ci.classify_changes`, `devx.molecule.discover_runners`, `devx.ci.doc_coverage` |
|
||||||
| (none) | Module has no GRM imports | `devx.ci.detect_release_commit`, `devx.molecule.distribute_molecule`, `devx.ci.push_badges`, `devx.ci.validate_commit_msg` |
|
| (none) | Module has no GRM imports | `devx.ci.detect_release_commit`, `devx.molecule.distribute_molecule`, `devx.ci.push_badges`, `devx.ci.validate_commit_msg` |
|
||||||
|
|
||||||
**In workflows**, always use `env:` blocks (not inline `PYTHONPATH=value`):
|
**In workflows**, always use `env:` blocks (not inline `PYTHONPATH=value`):
|
||||||
|
|||||||
@@ -2,6 +2,24 @@
|
|||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Use kireto token for auto-merge approval review
|
||||||
|
|
||||||
|
## [0.22.0] - 2026-08-25
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Adopt spec-driven CI gates, create_dependency_pr, and pr-review skill
|
||||||
|
|
||||||
## [0.21.1] - 2026-08-24
|
## [0.21.1] - 2026-08-24
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ Each runner runs in an isolated **rootless Docker** environment under a dedicate
|
|||||||
|
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/src/branch/master/LICENSE)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/src/branch/master/LICENSE)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
|
||||||
[](https://www.python.org/downloads/)
|
[](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
@@ -8,12 +8,12 @@ Each runner runs in an isolated **rootless Docker** environment under a dedicate
|
|||||||
|
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/src/branch/master/LICENSE)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/src/branch/master/LICENSE)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/wiki)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/actions)
|
||||||
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
|
[](https://git.oblachno.oblachno.fyi/oblachno-oss/grm/releases)
|
||||||
[](https://www.python.org/downloads/)
|
[](https://www.python.org/downloads/)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# GRM-165: Adopt spec-driven CI gates and pr-review skill
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
grm uses the old `devx.ci.pr_review` CI step and lacks the new spec-driven
|
||||||
|
CI gates (validate_spec, check_pr_size). It also needs a create_dependency_pr
|
||||||
|
step in post-merge to auto-create infra PRs when grm publishes a new version.
|
||||||
|
|
||||||
|
## Approach
|
||||||
|
Replace pr_review CI steps with validate_spec + check_pr_size + curl-based
|
||||||
|
APPROVE. Add create_dependency_pr step to post-merge. Add the spec-driven-
|
||||||
|
development and pr-review skills. Update AGENTS.md.
|
||||||
|
|
||||||
|
REQ-1: Replace pr_review CI steps with validate_spec + check_pr_size + curl-based APPROVE
|
||||||
|
REQ-2: Add create_dependency_pr step to post-merge for auto-creating infra PR to bump grm version
|
||||||
|
REQ-3: Add spec-driven-development and pr-review skills under `.devin/skills/`
|
||||||
|
REQ-4: Update AGENTS.md to document spec-driven development workflow and pr-review skill
|
||||||
|
|
||||||
|
## Test Plan
|
||||||
|
- Verify CI workflow YAML passes actionlint
|
||||||
|
- Verify post-merge.yml includes create_dependency_pr step with correct DEVX_TASK_PREFIX (GRM)
|
||||||
|
- Verify validate_spec and check_pr_size steps reference correct DEVX_TASK_PREFIX (GRM)
|
||||||
|
|
||||||
|
## Deploy Plan
|
||||||
|
- Merge to master via auto-merge workflow
|
||||||
|
- Post-merge workflow handles release + publish + dependency PR automatically
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
- Revert the merge commit; CI reverts to pr_review-based workflow
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- [x] REQ-1: CI workflow uses validate_spec + check_pr_size + curl APPROVE instead of pr_review
|
||||||
|
- [x] REQ-2: post-merge.yml includes create_dependency_pr step targeting oblachno/infra with package grm
|
||||||
|
- [x] REQ-3: `.devin/skills/spec-driven-development/SKILL.md` and `.devin/skills/pr-review/SKILL.md` exist
|
||||||
|
- [x] REQ-4: AGENTS.md documents spec-driven development workflow and pr-review skill
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# GRM-167: Bump devx to v0.51.0
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
devx v0.51.0 released with role defaults path support for create_dependency_pr. grm pins v0.50.2.
|
||||||
|
|
||||||
|
## Approach
|
||||||
|
Bump devx pin in pyproject.toml.
|
||||||
|
|
||||||
|
REQ-1: Bump devx from v0.50.2 to v0.51.0 in pyproject.toml
|
||||||
|
|
||||||
|
## Test Plan
|
||||||
|
- Verify CI passes with new devx version
|
||||||
|
|
||||||
|
## Deploy Plan
|
||||||
|
- Merge to master, auto-release
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
- Revert the merge commit
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- [x] REQ-1: devx pinned to v0.51.0 in pyproject.toml
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# GRM-168: Bump devx to v0.51.9
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
grm pins devx@v0.51.0 which rejects `deps:` as a conventional commit type,
|
||||||
|
causing post-merge CI failures on dependency bump commits.
|
||||||
|
|
||||||
|
## Approach
|
||||||
|
REQ-1: Bump devx from v0.51.0 to v0.51.9 in pyproject.toml
|
||||||
|
|
||||||
|
## Test Plan
|
||||||
|
- `make lint-all` passes
|
||||||
|
- `make pytest-cov` passes
|
||||||
|
|
||||||
|
## Deploy Plan
|
||||||
|
- Merge to master
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
- Revert the merge commit
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- [x] REQ-1: Bump devx from v0.51.0 to v0.51.9 in pyproject.toml
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# GRM-171: Use kireto token for auto-merge approval review
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
The auto-merge workflow posts approval reviews with
|
||||||
|
`REVIEWER_GITEA_API_TOKEN` (emil), but emil is also the PR creator.
|
||||||
|
Gitea ignores self-approvals, so the merge fails with HTTP 405
|
||||||
|
`Does not have enough approvals`.
|
||||||
|
|
||||||
|
## Approach
|
||||||
|
REQ-1: Change the approval review step in `.gitea/workflows/ci.yml` to use
|
||||||
|
`DEVELOPER_GITEA_API_TOKEN` (kireto) instead of
|
||||||
|
`REVIEWER_GITEA_API_TOKEN` (emil), since kireto is a different user
|
||||||
|
than the PR creator.
|
||||||
|
|
||||||
|
## Test Plan
|
||||||
|
- `make lint-all` passes (workflow-lint validates the YAML)
|
||||||
|
- Next auto-merge PR succeeds (approval posted by kireto, merge completes)
|
||||||
|
|
||||||
|
## Deploy Plan
|
||||||
|
- Merge to master
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
- Revert the merge commit
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- [x] REQ-1: Change the approval review step in `.gitea/workflows/ci.yml`
|
||||||
|
to use `DEVELOPER_GITEA_API_TOKEN` (kireto) instead of
|
||||||
|
`REVIEWER_GITEA_API_TOKEN` (emil)
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
+2
-2
@@ -36,7 +36,7 @@ ci = [
|
|||||||
"build==1.5.1",
|
"build==1.5.1",
|
||||||
"twine==6.2.0",
|
"twine==6.2.0",
|
||||||
# Reusable CI/CD and dev tools (auto-merge, pr-review, pre-push checks, etc.)
|
# Reusable CI/CD and dev tools (auto-merge, pr-review, pre-push checks, etc.)
|
||||||
"devx @ git+https://git.oblachno.oblachno.fyi/oblachno-oss/devx.git@v0.50.1",
|
"devx @ git+https://git.oblachno.oblachno.fyi/oblachno-oss/devx.git@v0.51.9",
|
||||||
]
|
]
|
||||||
# Lint and type-checking tools (validate job)
|
# Lint and type-checking tools (validate job)
|
||||||
lint = [
|
lint = [
|
||||||
@@ -56,7 +56,7 @@ molecule = [
|
|||||||
dev = [
|
dev = [
|
||||||
"grm[ci,lint,molecule]",
|
"grm[ci,lint,molecule]",
|
||||||
# Reusable CI/CD and dev tools (pre-push hooks, create-task, create-pr)
|
# Reusable CI/CD and dev tools (pre-push hooks, create-task, create-pr)
|
||||||
"devx @ git+https://git.oblachno.oblachno.fyi/oblachno-oss/devx.git@v0.50.1",
|
"devx @ git+https://git.oblachno.oblachno.fyi/oblachno-oss/devx.git@v0.51.9",
|
||||||
# Non-Python dev dependency: checkmake (Makefile linter)
|
# Non-Python dev dependency: checkmake (Makefile linter)
|
||||||
# Install via: go install github.com/checkmake/checkmake/cmd/checkmake@latest
|
# Install via: go install github.com/checkmake/checkmake/cmd/checkmake@latest
|
||||||
]
|
]
|
||||||
|
|||||||
+1
-1
@@ -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.21.1"
|
__version__ = "0.23.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user