Files
skill-gitea/SKILL.md

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