357 lines
15 KiB
Markdown
357 lines
15 KiB
Markdown
---
|
|
name: gitea
|
|
description: Da acceso a la instancia self-hosted de Gitea del beneficio Rio Frio (gitea.nucleoriofrio.com, org NucleOS). Úsala para crear/listar/ver PRs, leer comentarios, crear repos (en NucleOS o en tu propio usuario claudeCode0 — los del usuario del bot quedan SIEMPRE públicos por regla dura), y la killer feature — verificar runs de Gitea Actions post-merge (status, logs filtrados con --tail/--head/--errors/--grep, sin volcar el log entero a la ventana). Cubre los casos "mergeé el PR, ¿deployó OK?", "creame un repo nuevo para la skill X", reemplaza los curl ad-hoc anteriores. Ejemplos: "listame los PRs abiertos en nucleo-infra", "creá un PR desde la branch X a main", "qué pasó con el último deploy", "buscame los errores en el run 11 del workflow deploy-infra", "mostrame las últimas 30 líneas de logs del run que falló", "creá un repo skill-foo en mi usuario", "crea repo en NucleOS llamado servicio-X privado".
|
|
allowed-tools: Bash, Read, Grep
|
|
---
|
|
|
|
# Gitea — skill de PRs + Actions de gitea.nucleoriofrio.com
|
|
|
|
## Qué es esto
|
|
|
|
Skill para interactuar con la instancia **self-hosted de Gitea** del beneficio
|
|
Rio Frio (`gitea.nucleoriofrio.com`, Gitea 1.24.7) hostea el org **`NucleOS`**.
|
|
Resuelve dos casos de uso principales:
|
|
|
|
1. **Crear/leer PRs** sin escribir curl ad-hoc cada vez (con guards anti-AI
|
|
attribution y body UTF-8 safe).
|
|
2. **Killer feature**: introspección de runs de Gitea Actions post-merge —
|
|
`actions-list-runs`, `actions-view`, `actions-logs`. Cierra el loop GitOps:
|
|
yo mergeo un PR, el workflow `deploy-infra.yml` dispara, y con la skill
|
|
verifico que deployó OK sin necesitar SSH a nucleo001.
|
|
|
|
Las operaciones admin (Actions secrets/variables) están **bloqueadas** porque
|
|
el PAT de `claudecode0` no es admin del org. Hay un override `GITEA_USER_PAT`
|
|
para esos casos one-shot.
|
|
|
|
- **Server**: `https://gitea.nucleoriofrio.com`
|
|
- **Auth**: `Authorization: token <PAT>` (PAT de `claudecode0` en `.env`,
|
|
extraído de la skill `bitwarden` por `setup.sh`)
|
|
- **Override admin**: `GITEA_USER_PAT=...` en el entorno (one-shot temporal)
|
|
|
|
## Cómo invocarla correctamente
|
|
|
|
Antes de cualquier query:
|
|
|
|
1. **Verificá `~/.claude/skills/gitea/.env`**. Si no existe o está vacío:
|
|
```bash
|
|
bash ~/.claude/skills/gitea/scripts/setup.sh
|
|
```
|
|
Eso extrae el PAT desde la skill `bitwarden` (item con substring "Gitea PAT
|
|
claude-agent-gitops") y lo escribe al `.env`. Re-corré `setup.sh` cada vez
|
|
que rotás el PAT.
|
|
|
|
2. **Si una query devuelve 401**: el PAT está revocado o el item correcto
|
|
en bitwarden cambió. Re-corré `setup.sh`. Si persiste, podría ser que
|
|
`setup.sh` está agarrando el PAT duplicado equivocado (hay dos items con
|
|
el mismo nombre en bitwarden, ver "Errores típicos").
|
|
|
|
## Scripts disponibles
|
|
|
|
| Script | Propósito |
|
|
|---|---|
|
|
| `setup.sh` | Extrae PAT de bitwarden, escribe `.env`, valida con `/version` |
|
|
| `query.sh` | Helper REST genérico con admin-guard (foundation de todos los demás) |
|
|
| `pr-list.sh` | Lista PRs (con `--state open\|closed\|all`, `--limit N`) |
|
|
| `pr-view.sh` | Ver detalle de un PR (incluye body completo) |
|
|
| `pr-comments.sh` | Lista comentarios de un PR (requiere PAT con `read:issue`) |
|
|
| `pr-create.sh` | Crear PR con guards anti-AI-attribution + body UTF-8 safe |
|
|
| `repo-create.sh` | Crear repo (default owner = bot user; **regla dura**: bot user → siempre público) |
|
|
| `repo-list.sh` | Listar repos de un owner (default = bot user). Filtros: `--limit`, `--sort`. |
|
|
| `actions-list-runs.sh` | Lista runs (filtros client-side: workflow, branch, status, event) |
|
|
| `actions-view.sh` | Detalle de un run + probe del job_id |
|
|
| `actions-logs.sh` | Lee logs con filtros precisos (--tail/--head/--lines/--grep/--errors) |
|
|
|
|
Ver `endpoints.md` para los endpoints crudos de la API.
|
|
|
|
## Modelo de seguridad
|
|
|
|
### Admin guard en `query.sh`
|
|
|
|
`query.sh` bloquea cualquier path que matchee:
|
|
- `/admin/*` (operaciones admin del server)
|
|
- `/orgs/{org}/actions/(secrets|variables)/*`
|
|
- `/repos/{o}/{r}/actions/(secrets|variables)/*`
|
|
|
|
Mensaje del guard: pide al usuario un PAT temporal con scope admin, lo exporta
|
|
como `GITEA_USER_PAT=...`, y le **recuerda BORRAR el PAT** desde
|
|
`https://gitea.nucleoriofrio.com/user/settings/applications` apenas termine
|
|
(Gitea no tiene PATs efímeros nativos).
|
|
|
|
```bash
|
|
# Bypass legítimo del guard:
|
|
export GITEA_USER_PAT=<el-pat-temporal-admin>
|
|
bash ~/.claude/skills/gitea/scripts/query.sh /repos/NucleOS/X/actions/secrets
|
|
unset GITEA_USER_PAT # limpiar al terminar
|
|
```
|
|
|
|
### Anti-AI-attribution guard en `pr-create.sh`
|
|
|
|
**Regla dura** (ver memoria `feedback_no_ai_attribution.md`). Antes de POST,
|
|
escanea title + body por (case-insensitive):
|
|
|
|
- `Co-Authored-By: Claude` (o `Anthropic`)
|
|
- `🤖`
|
|
- `Generated with [Claude Code]` / `Generated with Claude` / `Generated with Anthropic`
|
|
- `Created with Claude` / `Powered by Claude` / `Made with Claude`
|
|
|
|
Si match → exit 4 con mensaje + las líneas ofensivas. Esto es **imposibilitar**,
|
|
no advertir — el usuario considera deshonesto darle crédito a Claude/Anthropic
|
|
por trabajo del usuario.
|
|
|
|
### Visibilidad de repos del bot (regla dura)
|
|
|
|
`repo-create.sh` rechaza `--private` cuando el owner es el bot user
|
|
(case-insensitive match contra `$GITEA_BOT_USER`). Justificación: la cuenta
|
|
`claudeCode0` es del bot, pero el usuario humano tiene que poder auditarla
|
|
sin login. Si los repos del bot fueran privados, sería una caja negra. Para
|
|
algo realmente privado, crearlo bajo la org NucleOS.
|
|
|
|
Para repos en orgs (NucleOS u otra), el flag `--public` o `--private` es
|
|
**requerido** — sin default sorpresa. Forzá la decisión consciente.
|
|
|
|
### Scope del PAT de claudecode0
|
|
|
|
El PAT cacheado tiene scope: `write:organization`, `write:package`,
|
|
`write:repository`, `write:user`. **NO incluye `read:issue` / `write:issue`**,
|
|
así que `pr-comments.sh` (que lee issue comments) puede devolver 403. Si el
|
|
usuario quiere usar `pr-comments` o crear PRs con descripciones largas que
|
|
generen comentarios, debe regenerar el PAT con esos scopes adicionales y
|
|
re-correr `setup.sh`.
|
|
|
|
## Cuándo usar qué endpoint
|
|
|
|
### Caso típico: "mergeé el PR, ¿deployó OK?"
|
|
|
|
Después de mergear un PR a main en `nucleo-infra`, el workflow corre. Para
|
|
verificar:
|
|
|
|
```bash
|
|
# 1. Ver el último run del workflow:
|
|
bash ~/.claude/skills/gitea/scripts/actions-list-runs.sh NucleOS/nucleo-infra --limit 3
|
|
|
|
# 2. Si está in_progress, esperar 30-60s y reintentar.
|
|
# Si terminó, ver los logs:
|
|
bash ~/.claude/skills/gitea/scripts/actions-logs.sh NucleOS/nucleo-infra <run_number>
|
|
|
|
# 3. Si falló, buscar errores específicos:
|
|
bash ~/.claude/skills/gitea/scripts/actions-logs.sh NucleOS/nucleo-infra <run> --errors -C 3
|
|
```
|
|
|
|
### "Quiero crear un PR"
|
|
|
|
Para body con tildes/ñ, **siempre** pasarlo via `--body-file`. NO usar heredoc
|
|
inline (memoria `feedback_api_utf8_encoding.md`):
|
|
|
|
```bash
|
|
# Crear el body en un archivo (con Write tool, UTF-8 nativo):
|
|
cat > /c/Users/jodar/AppData/Local/Temp/pr-body.md <<'EOF'
|
|
## Qué cambia
|
|
|
|
Esto arregla el bug X — resumen de cambios.
|
|
|
|
## Por qué
|
|
[...]
|
|
EOF
|
|
|
|
bash ~/.claude/skills/gitea/scripts/pr-create.sh NucleOS/nucleo-infra \
|
|
--head fix/foo \
|
|
--title "fix(x): short imperative" \
|
|
--body-file /c/Users/jodar/AppData/Local/Temp/pr-body.md
|
|
```
|
|
|
|
Para body simple sin caracteres especiales, `--body "..."` inline también va.
|
|
|
|
### "Listame PRs abiertos"
|
|
|
|
```bash
|
|
bash ~/.claude/skills/gitea/scripts/pr-list.sh NucleOS/nucleo-infra
|
|
# Default state=open, limit=20.
|
|
```
|
|
|
|
### "Ver detalle del PR #14"
|
|
|
|
```bash
|
|
bash ~/.claude/skills/gitea/scripts/pr-view.sh NucleOS/nucleo-infra 14
|
|
```
|
|
|
|
### "Mostrame solo los runs que fallaron"
|
|
|
|
```bash
|
|
bash ~/.claude/skills/gitea/scripts/actions-list-runs.sh NucleOS/nucleo-infra \
|
|
--status failure --limit 20
|
|
```
|
|
|
|
### "Buscá errores en el run 7"
|
|
|
|
```bash
|
|
bash ~/.claude/skills/gitea/scripts/actions-logs.sh NucleOS/nucleo-infra 7 --errors -C 3
|
|
# --errors es shortcut para --grep '(error|fail|fatal|exception|panic|...)'
|
|
```
|
|
|
|
### "Quiero el log entero del run X para grep-ear con mis tools"
|
|
|
|
```bash
|
|
bash ~/.claude/skills/gitea/scripts/actions-logs.sh NucleOS/nucleo-infra X \
|
|
--save /c/Users/jodar/AppData/Local/Temp/run-X.log
|
|
# Guarda raw (sin sanitizar) para preservar fidelidad.
|
|
```
|
|
|
|
### "Creá un repo nuevo en mi (bot) usuario"
|
|
|
|
Default owner = `$GITEA_BOT_USER` (claudeCode0). **Siempre público** por
|
|
regla dura — `--private` está bloqueado bajo el bot user (exit 5):
|
|
|
|
```bash
|
|
bash ~/.claude/skills/gitea/scripts/repo-create.sh skill-foo \
|
|
--description "Skill X — qué hace" --license "MIT" --gitignore "Node"
|
|
# → claudeCode0/skill-foo (public), html_url + clone_url + ssh_url
|
|
```
|
|
|
|
### "Listame los repos del bot / del org"
|
|
|
|
```bash
|
|
bash ~/.claude/skills/gitea/scripts/repo-list.sh # bot user
|
|
bash ~/.claude/skills/gitea/scripts/repo-list.sh --owner NucleOS # org
|
|
bash ~/.claude/skills/gitea/scripts/repo-list.sh --sort alpha --limit 10
|
|
```
|
|
|
|
### "Creá un repo en NucleOS"
|
|
|
|
Para org, `--public` o `--private` es **requerido** (exit 6 si no pasás
|
|
ninguno — sin default sorpresa):
|
|
|
|
```bash
|
|
bash ~/.claude/skills/gitea/scripts/repo-create.sh nuevo-servicio \
|
|
--owner NucleOS --private --description "..." --gitignore "Go"
|
|
```
|
|
|
|
## Reglas de comportamiento
|
|
|
|
### Filtros sobre logs son obligatorios
|
|
|
|
Los runs de `deploy-infra` tienen ~1700 líneas (post-sanitización). Volcar
|
|
entero satura mi ventana. **Default es siempre summary** (header + tail 5/40
|
|
según status). Solo usar `--full` cuando es chico, y nunca sin `--i-mean-it`
|
|
si supera 1000 líneas.
|
|
|
|
### Workflow GitOps (memoria `feedback_swarm_changes_via_gitea.md`)
|
|
|
|
Toda modificación al Swarm va por PR + workflow. Después de mergear, **antes
|
|
de declarar el cambio listo**, verificar con `actions-logs` que el deploy
|
|
terminó OK. Sino, esa "termina" es prematura.
|
|
|
|
### Operaciones disruptivas requieren OK explícito
|
|
|
|
`pr-merge` no está en la skill (out of scope). Mergear con la API requiere
|
|
construir el endpoint manualmente con `query.sh` y antes pedir confirmación
|
|
explícita al usuario — un merge dispara deploy a producción. Memoria
|
|
`feedback_disruptive_actions.md` aplica.
|
|
|
|
### PAT duplicado en bitwarden
|
|
|
|
Hay 2 items con substring `Gitea PAT claude-agent-gitops` en el vault de
|
|
`claudecode0`. `setup.sh` toma el primero. Si la validación falla:
|
|
- Listar los items: `bash ~/.claude/skills/bitwarden/scripts/query.sh "/list/object/items?search=Gitea"`
|
|
- Pedirle al usuario que limpie el duplicado desde
|
|
`https://vault.nucleoriofrio.com` (la skill `bitwarden` tiene DELETE bloqueado).
|
|
|
|
## Errores típicos
|
|
|
|
| Error | Causa | Qué hacer |
|
|
|---|---|---|
|
|
| `setup.sh` "no encontré ningún item..." | El PAT no está guardado en bitwarden | Generar PAT en Gitea + guardarlo en bitwarden con substring "Gitea PAT claude-agent-gitops" |
|
|
| Validación 401 | PAT revocado o duplicado equivocado | Limpiar duplicados desde web vault, regenerar si es necesario, re-correr `setup.sh` |
|
|
| `query.sh` admin guard exit 3 | Endpoint requiere admin | Pedir PAT temporal admin al usuario, exportar `GITEA_USER_PAT`, recordar borrarlo apenas termine |
|
|
| `pr-create.sh` exit 4 | Body con AI attribution | Remover los markers (regla dura) |
|
|
| `pr-comments.sh` 403 | PAT sin scope `read:issue` | Regenerar PAT con `read:issue`+`write:issue`, re-correr `setup.sh` |
|
|
| `actions-logs.sh` "no pude resolver job_id" | Probe de ±10 falló (Gitea no expone listado de jobs) | Probar con `--job <jid>` directo; rangear con `query.sh /repos/.../actions/jobs/<jid>/logs` manualmente |
|
|
| `actions-list-runs` 0 results con filtro | Filtros son client-side; el server solo respeta `limit` | Subir `--limit` (default 10) para incluir runs más viejos |
|
|
|
|
## Lifecycle: cuándo correr cada script
|
|
|
|
| Script | Cuándo |
|
|
|---|---|
|
|
| `setup.sh` | (a) Primera vez. (b) Cuando el PAT rota. (c) Cuando agregás scopes nuevos al PAT. (d) Cuando 401 inesperado |
|
|
| `query.sh` | Endpoint nuevo o ad-hoc no cubierto por otros scripts |
|
|
| `pr-*.sh` | Operaciones de PRs |
|
|
| `actions-*.sh` | Verificar deploys, debug runs, etc. |
|
|
|
|
## Setup inicial (lo hace el usuario UNA vez)
|
|
|
|
### 1. Generar PAT en Gitea
|
|
- `https://gitea.nucleoriofrio.com/user/settings/applications` (logueado como `claudecode0`)
|
|
- "Generate New Token" con scopes:
|
|
- `read:repo`, `write:repo` — push, leer/crear PRs, branches
|
|
- `read:issue`, `write:issue` — comentarios y descripciones de PRs
|
|
- `read:user` — sanity check de auth
|
|
- (NO usar admin scopes acá; eso queda para PATs temporales)
|
|
- Copiar el token (sólo se ve una vez).
|
|
|
|
### 2. Guardar en bitwarden (vault de claudecode0)
|
|
- Web: `https://vault.nucleoriofrio.com` → login como `claudecode0`
|
|
- New item: name = `claudecode0 · Gitea PAT claude-agent-gitops`
|
|
(cualquier nombre con substring `Gitea PAT claude-agent-gitops` sirve)
|
|
- Username: `claudecode0`. Password: el PAT.
|
|
- Save.
|
|
|
|
### 3. Correr setup
|
|
```bash
|
|
bash ~/.claude/skills/gitea/scripts/setup.sh
|
|
```
|
|
|
|
Esperado:
|
|
```
|
|
→ Listo: Gitea 1.24.7 | user=claudeCode0 | https://gitea.nucleoriofrio.com
|
|
```
|
|
|
|
### 4. Probar
|
|
```bash
|
|
bash ~/.claude/skills/gitea/scripts/pr-list.sh NucleOS/nucleo-infra
|
|
bash ~/.claude/skills/gitea/scripts/actions-list-runs.sh NucleOS/nucleo-infra --limit 3
|
|
```
|
|
|
|
## Limitaciones conocidas de la API de Gitea 1.24
|
|
|
|
- **No hay endpoint global de runs**: `/actions/runs` 404. Usamos
|
|
`/actions/tasks` que sí existe pero solo acepta `page`+`limit` — los demás
|
|
filtros (workflow, branch, status, event) son client-side.
|
|
- **No hay endpoint para listar jobs de un run**: `/actions/runs/{id}/jobs`
|
|
404. La skill hace **probe** de job_ids en `task_id ± 10` matcheando la
|
|
primera línea del log con `received task <task_id>`. Si tu run tiene
|
|
múltiples jobs o el offset es mayor, falla — pasale `--job <jid>` directo.
|
|
- **Logs por job son texto plano** (`/actions/jobs/{job_id}/logs`). Logs por
|
|
run completo (`/actions/runs/{id}/logs`) devuelven zip — la skill **nunca
|
|
los toca** porque no son procesables desde Bash sin extraer.
|
|
- **Actor del run no expuesto**: `/actions/tasks` no devuelve quién disparó
|
|
el run. Si querés saber quién mergeó, mirá el merge commit con `git log`.
|
|
|
|
## Qué NO hace esta skill
|
|
|
|
- **No mergea PRs** (out of scope; disruptive — dispara deploy a prod, requiere
|
|
confirmación explícita del usuario y prefijar `query.sh` manualmente).
|
|
- **No comenta PRs** (POST a issues/comments — out of scope; el PAT
|
|
tampoco tiene scope para issues por default).
|
|
- **No gestiona issues, releases, webhooks ni runners**. Out of scope.
|
|
- **No lee zip de logs run-level**. Sólo job-level texto plano.
|
|
- **No hace AI attribution** en commits ni PRs. Imposible de bypassear desde
|
|
esta skill (regla dura).
|
|
|
|
## Archivos de la skill
|
|
|
|
| Archivo | Qué tiene |
|
|
|---|---|
|
|
| `SKILL.md` | Este archivo |
|
|
| `endpoints.md` | Cheat sheet de la API de Gitea 1.24 (lo relevante) |
|
|
| `.env.example` | Plantilla de config |
|
|
| `.env` | Generado por `setup.sh` (NO versionado, contiene el PAT) |
|
|
| `scripts/setup.sh` | Extrae PAT de bitwarden + valida |
|
|
| `scripts/query.sh` | Helper REST con admin guard |
|
|
| `scripts/pr-*.sh` | Wrappers de PRs |
|
|
| `scripts/actions-*.sh` | Wrappers de Gitea Actions |
|
|
|
|
## Referencias
|
|
|
|
- API spec oficial Gitea: https://docs.gitea.com/api/1.24/
|
|
- Swagger JSON de la instancia: `https://gitea.nucleoriofrio.com/swagger.v1.json`
|
|
- `endpoints.md` — cheat sheet de los endpoints relevantes
|