addons-cm/tools/pr_bot
GitHub Copilot d9d9e8be2d
[ADD] ci: Forgejo Actions CI and Claude bots ported from ecocentral
Same system as ecocentral-18 (see its docs/CI-CD.md):

- ci.yml: lint (pre-commit), test (tools/ci/run_tests.py, OCA-style
  two-phase install) and review (PR bot, after lint and test).
- issue-bot.yml: the issue bot answers issues or opens a PR on bot/issue-N.
- pr-bot.yml: review when WIP is removed or the i18n label is added.
- .gitea/issue-bot.md: context for the bots; this repo is shared by
  several clients.

The bot scripts are copies of ecocentral's. Only the defaults differ (repo,
token and state files, i18n languages es,eu,ca,gl) along with the [I18N]
commit message and the references to the runner's sources.txt.

The jobs run on a second forgejo-runner registration (CriptoMart org) on
ecocentral's runner machine, sharing its image, mounts and cache. The
runner files (Dockerfile, update.sh, sources.txt) live in ecocentral.
Specific to this repo:

- ODOO_SRC_SKIP: the runner's /odoo-src/18.0 holds a clone of addons-cm,
  so test skips it and checks the PR checkout instead.
- BOT_BASE_BRANCH "18.0": the repo's default branch is 16.0.
- test PostgreSQL on port 54330: ecocentral's runs on 54329 and a test job
  from each repo can run at the same time.

The lint job fails until the repo is cleaned up with
pre-commit run --all-files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 14:34:58 +02:00
..
i18n.py [ADD] ci: Forgejo Actions CI and Claude bots ported from ecocentral 2026-10-04 14:34:58 +02:00
prompt.md [ADD] ci: Forgejo Actions CI and Claude bots ported from ecocentral 2026-10-04 14:34:58 +02:00
README.md [ADD] ci: Forgejo Actions CI and Claude bots ported from ecocentral 2026-10-04 14:34:58 +02:00
review.py [ADD] ci: Forgejo Actions CI and Claude bots ported from ecocentral 2026-10-04 14:34:58 +02:00

Bot de revisión de PR

Revisa con Claude los pull requests de CriptoMart/addons-cm en Forgejo cuando ya han pasado el linter y los tests, y deja un comentario con lo que encuentra. No aprueba, no bloquea y no fusiona: la decisión sigue siendo de quien revisa.

  • review.py: orquestador. Decide si toca revisar, prepara la cabeza del PR, lanza claude -p y publica el comentario. Reutiliza la API de Forgejo, el clon y la llamada a Claude del bot de incidencias.
  • prompt.md: instrucciones de la revisión, comunes a todos los repos.
  • i18n.py: prepara y repasa los .po.
  • ../../.gitea/issue-bot.md: qué es este repo y sus convenciones, entre ellas la de idiomas. Es el mismo fichero que usa el bot de incidencias.

Qué revisa

Parte Qué busca
Seguridad y permisos ACL que faltan o sobran, reglas de registro, sudo(), métodos públicos, controladores, SQL a mano
Idiomas y traducciones Textos en un idioma que no es el del addon o que Odoo no puede traducir (_() mal usado)
Flujos de Odoo Sobrescrituras, otras entradas al mismo modelo, otros addons que tocan lo mismo, datos que ya hay en producción

Cada hallazgo lleva gravedad (alta, media, baja), fichero y línea, el caso concreto y un arreglo propuesto. Estilo, formato y lo que cubren los tests no entran: de eso ya se han ocupado el linter y el CI.

Cuándo actúa

Disparo Dónde
El CI de un PR pasa lint y test job review de ci.yml (needs)
Se quita la marca WIP o se pone i18n pr-bot.yml, si el CI ya había pasado

Se salta los PR cerrados, los marcados como WIP (WIP: o [WIP] en el título), los que no van contra 18.0 y los que llevan la etiqueta no-bot. Revisa cada commit una sola vez y deja de hacerlo tras BOT_MAX_REVIEWS revisiones del mismo PR. Cada revisión nueva sustituye a la anterior: repite lo que sigue vigente y deja fuera lo arreglado.

Traducciones (etiqueta i18n)

Por defecto el bot solo revisa los textos. Con la etiqueta i18n en el PR, además:

  1. Recoge el .pot de cada addon que toca el PR. Lo exporta Odoo en el job test, cuando los tests pasan, y lo deja en la caché del runner (BOT_POT_DIR/<commit>/).
  2. Pone al día i18n/<addon>.pot y prepara un .po por idioma de BOT_I18N_LANGS (es, eu, ca, gl) con los términos de todo el addon.
  3. Claude rellena los msgstr vacíos. Un .po nuevo que se quede sin ninguna traducción no se crea.
  4. Se descartan las traducciones que no conservan los marcadores (%s, %(nombre)s) del original.
  5. El bot sube el resultado a la rama del PR en un commit [I18N] <addons>: …. Ese commit pasa el CI como cualquier otro, pero no se vuelve a revisar.

Poner la etiqueta en un PR ya revisado lanza otra pasada, esta vez con las traducciones. Los addons que el CI no instala (EXCLUDE en ci.yml) no tienen .pot y se quedan como están.

Riesgo aceptado: el .pot sale de una caché en la que el job test, que ejecuta el código del PR, puede escribir. Un test malicioso podría dejar un .pot falso para el commit de otro PR, y si ese PR lleva la etiqueta, el bot subiría a su rama términos que no salen de su código. Solo afecta a ficheros i18n/, solo con la etiqueta y solo con acceso de escritura al repo, que es privado. Lo de fondo es que todos los jobs comparten issue-bot-cache; si algún día entra código menos fiable, el job test debería tener su propia caché.

Puesta en marcha

Usa la misma imagen, la misma cuenta (claudia_eco) y los mismos secretos que el bot de incidencias: ver su README, que a su vez remite al de ecocentral para el runner. Solo hay que añadir:

  1. La etiqueta i18n en el repo de Forgejo.
  2. La caché que comparten los jobs. Ya está si el runner monta issue-bot-cache:/root/.cache como dice el README del runner; los .pot van en /root/.cache/eco-pr-bot/pot y se borran solos a los 14 días.

El job review corre con el review.py de la propia rama del PR, así que los PR abiertos antes de fusionar este bot no se revisan hasta que incorporen 18.0.

A mano

En una máquina con claude autenticado, python3-polib y el token en ~/.config/gitea-addons-cm.token:

python3 tools/pr_bot/review.py run 217 --dry-run   # enseña lo que publicaría
python3 tools/pr_bot/review.py run 217 --force     # repite un commit ya revisado

Sin --ci-passed comprueba en Forgejo que el commit tiene lint y test en verde. Fuera del runner no hay .pot del CI, así que las traducciones no se tocan aunque el PR lleve la etiqueta.

Configuración

Las variables del bot de incidencias (BOT_FORGEJO_*, BOT_REPO_DIR, BOT_ODOO_SRC, BOT_MODEL, BOT_MAX_BUDGET_USD, BOT_TIMEOUT) valen igual aquí. Propias de este bot:

Variable Por defecto
BOT_MAX_REVIEWS (por PR) 6
BOT_CI_CONTEXTS (separados por ,) CI / lint (pull_request),CI / test (pull_request)
BOT_I18N_LABEL i18n
BOT_I18N_LANGS es,eu,ca,gl
BOT_POT_DIR ~/.cache/eco-pr-bot/pot