GH Actions push-trigger watchdog — auto-recuperación del footgun de runs silenciados
El problema
GitHub Actions tiene un footgun documentado pero no reconocido oficialmente: los push triggers se silencian de forma intermitente. Un commit llega a main, el repositorio está sano, Actions está habilitado, pero no se crea ningún workflow run. El síntoma es silencio total — no hay run fallido, no hay error, simplemente no ocurre nada.
Se ha observado al menos dos veces en los repos de CreaRackSL (commit 518ce14, 2026-04-30). El workaround manual era: Settings → Actions → Disable → Save → Allow all → Save. Esto es lento, requiere acceso a la UI y no escala.
Solución implementada
scripts/gh-actions-watchdog.sh — script bash instalado en Hetzner OPS (crearack-ops, NetBird 100.96.245.233; migrado de STAGE en s193, STAGE limpiado en F3/s201) que corre cada 5 minutos vía cron.
Lógica
Para cada (repo, workflow) en TARGETS:
1. Obtener el SHA del último commit en main (GitHub API)
2. Calcular la antigüedad del commit
3. Si antigüedad ∈ [5 min, 2 h]:
a. Consultar workflow runs para ese SHA
b. Si total_count == 0 → dispatch workflow_dispatch en ese workflow
4. Log del resultado
Ventana de detección (5 min – 2 h):
- El límite inferior (5 min) da tiempo al push trigger nativo para actuar. No queremos duplicar runs normales.
- El límite superior (2 h) evita disparar en commits viejos cuando el watchdog llevaba tiempo apagado.
Un workflow solo pertenece a TARGETS si existe, tiene workflow_dispatch, está habilitado y corre en push a main. Si no corre en push a main, “sin runs” es el estado normal y el watchdog dispararía tras CADA merge — exactamente el footgun inverso al que el script existe para arreglar (ver más abajo).
Repos y workflows monitorizados
| Repo | Workflow |
|---|---|
CreaRackSL/CreaRack-Pro | ci.yml |
25-09-2026 (tarea #377, decisión de Edu, commit
2a3829e6): se retiraCreaRackSL/CreaRackSL-workspacedeTARGETS. Su único workflow con push-trigger,post-merge-ingest.yml, lleva desactivado desde la sesión 274 — dejarlo en la lista hacía que el watchdog disparase unworkflow_dispatchcon HTTP 422 cada 5 minutos durante las 2 horas siguientes a CADA merge al workspace (unas 250 veces desde el 04-07-2026: el “CI sobre main cada ~20 min” que nadie sabía de dónde venía).cf-pages-deploy.ymlya había salido antes, al migrar ese deploy al cron de OPS (dispatch 422, visto 04-07). Conci.ymlde CreaRack-Pro recuperando su triggerpush: main(Pro #619), el watchdog vuelve a su papel original: rescatar únicamente pushes de Pro que GitHub no convirtió en runs.
Instalación
mkdir -p /opt/gh-actions-watchdog && chmod 700 /opt/gh-actions-watchdog
# PAT fine-grained con Actions: write en el repo vigilado
echo "<GH_PAT>" > /opt/gh-actions-watchdog/.token
chmod 600 /opt/gh-actions-watchdog/.token
# Script versionado en el repo workspace
scp scripts/gh-actions-watchdog.sh root@100.96.245.233:/opt/gh-actions-watchdog/watchdog.sh
chmod +x /opt/gh-actions-watchdog/watchdog.sh
# Cron cada 5 min
cat > /etc/cron.d/gh-actions-watchdog <<'EOF'
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
*/5 * * * * root /opt/gh-actions-watchdog/watchdog.sh >> /opt/gh-actions-watchdog/watchdog.log 2>&1
EOF
chmod 644 /etc/cron.d/gh-actions-watchdog
Dependencias en el host
| Requisito | Detalle |
|---|---|
curl | Para llamadas a GitHub API |
jq | Para parsear JSON de respuestas |
/opt/gh-actions-watchdog/.token | PAT GitHub fine-grained, Actions: write, Metadata: read en el repo vigilado. Generarlo en https://github.com/settings/tokens |
El script hace no-op limpio si el token está vacío o falta — no hay riesgo de error de cron ruidoso.
Rotación de logs
El script rota su propio log cuando supera 5 MB: mueve watchdog.log a watchdog.log.1 y crea uno nuevo. Solo conserva una generación anterior (suficiente para diagnóstico post-incidente).
Señales de alarma en el log
tail -f /opt/gh-actions-watchdog/watchdog.log
| Patrón | Significado |
|---|---|
trigger: CreaRackSL/CreaRack-Pro commit abc12345 age=Xs sin runs → dispatch ci.yml | Footgun detectado y auto-recuperado |
fail: ... dispatch returned HTTP 403 | PAT sin permisos o expirado — regenerar token |
fail: ... dispatch returned HTTP 404 | Workflow filename incorrecto en TARGETS |
skip: token file empty | El PAT no está configurado en el host |
warn: ... could not fetch latest commit | Error transitorio de red o rate-limit de GitHub API |
Si aparecen disparos frecuentes (>1 por hora) sobre un repo que SÍ tiene su workflow corriendo en push a main, puede indicar un problema sistémico con Actions — escalar manualmente. Si el repo vigilado NO corre ese workflow en push a main, el disparo frecuente es la causa descrita arriba (25-09-2026): hay que sacarlo de TARGETS, no escalar.
Limitaciones conocidas
- No cubre todos los workflows: solo los listados en
TARGETS. Si se añaden nuevos workflows push-triggered hay que añadirlos al array — y quitar los que dejen de correr en push a main (ver 25-09-2026 arriba). workflow_dispatchrequiere que el workflow tenga el trigger configurado: todos los workflows enTARGETSdeben teneron: [push, workflow_dispatch].- Rate limit GitHub API: 5.000 req/h con PAT autenticado. Muy por debajo del límite con 1 repo × 2 calls × 12 veces/h.
- El token vacío es intencional: el script no falla ni genera ruido hasta que el PAT esté disponible.
Contexto del footgun
El patrón “push trigger que se pausa silenciosamente” parece estar relacionado con algún mecanismo de throttling o estado interno de GitHub que se desincroniza. No hay documentación oficial de GitHub al respecto (a 2026-04-30). El toggle manual de disable/enable en Settings resetea ese estado. Este watchdog replica ese reset de forma automatizada via workflow_dispatch.
Ver [[concept—infra—staging-server]] § 2.6 para el inventario histórico del servidor donde corría este script antes de la migración a OPS.
Véase también
- [[concept—infra—staging-server]]