8.2 KiB
Despliegue automático
DummyBot se despliega automáticamente a producción cuando se hace push a la rama
main del repositorio.
push a main
→ Gitea dispara webhook "deploy"
→ POST http://dummybot_expose:80/webhook/deploy (red Docker interna)
→ Laravel valida HMAC-SHA256 (X-Hub-Signature-256)
→ exec('nohup deploy-runner.sh &')
→ git pull + composer install + npm ci + npm run build
→ php artisan migrate --force
→ php artisan optimize
→ Healthcheck GET /up
→ Log en storage/logs/deploy-YYYYMMDD-HHMMSS.log
Tiempo total: ~25-35 segundos desde push hasta server actualizado.
Arquitectura
┌─────────────────────┐
│ git.anabolictrack │ Gitea 1.22.6
│ (rootless, 3000) │ - Repos: dummybot, dummybot-firmware
└──────────┬──────────┘
│ webhook push event
│ POST http://dummybot_expose:80/webhook/deploy
▼
┌─────────────────────┐
│ 157.180.77.232:80 │ nginx-proxy-manager (172.20.0.2)
└──────────┬──────────┘
│ red "proxy" (172.20.0.0/16)
▼
┌─────────────────────┐ ┌──────────────────────┐
│ dummybot_expose │───▶│ dummybot_web (nginx) │
│ socat :80→web:80 │ │ /var/www/public │
│ (172.20.0.8) │ └──────────┬───────────┘
└─────────────────────┘ │
▼
┌──────────────────────┐
│ dummybot_app (php-fpm)│
│ php artisan │
│ /var/www │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ dummybot_db (mysql) │
└──────────────────────┘
Red Docker proxy (172.20.0.0/16): comparten gitea, dummybot_web,
dummybot_expose, nginx-proxy-manager. Gitea hace POST al webhook usando
http://dummybot_expose:80/webhook/deploy — resolución DNS por nombre de
container, no atraviesa UFW ni la interfaz pública.
Volúmenes clave:
/root/dummybot:/var/www(en el container) — el repo clonado, mismo path en host y container./root/dummybot.env— backup del.envreal (no se commitea)./root/dummybot/storage/logs/— logs de deploy.
Webhook de Gitea
Endpoint: POST /webhook/deploy
Auth: HMAC-SHA256 con secreto compartido en header X-Hub-Signature-256.
Trigger: push a rama main.
Branch filter: main (configurado en Gitea).
Validación en Laravel (app/Http/Controllers/WebhookController.php):
- Lee
DEPLOY_WEBHOOK_SECRETdel.env. - Calcula
hash_hmac('sha256', body, secret)y compara con header. - Decodifica payload, comprueba
ref == "refs/heads/main". - Lanza
nohup bash deploy-runner.sh > log 2>&1 &. - Responde 202 con path al log.
Excluido de CSRF en bootstrap/app.php:
$middleware->validateCsrfTokens(except: ['webhook/*']);
Configuración inicial del server
1. SSH key para acceso de emergencia
ssh-keygen -t ed25519 -C "deploy-key" -f ~/.ssh/dummybot_deploy -N ""
ssh-copy-id -i ~/.ssh/dummybot_deploy.pub root@157.180.77.232
2. Respaldo del .env
# En el server, una vez esté configurado
cp /root/dummybot/.env /root/dummybot.env
El deploy restaura .env desde /root/dummybot.env en cada push.
3. Generar secreto del webhook
openssl rand -hex 32 # → "75f02729eecd38c9..."
Guardarlo en dos sitios:
/root/dummybot/.env:DEPLOY_WEBHOOK_SECRET=<secret>- Configuración del webhook en Gitea (al crearlo).
4. Configurar Gitea para permitir webhooks a hosts internos
Gitea 1.22 tiene whitelist de hosts (webhook.ALLOWED_HOST_LIST). Por defecto
está vacía y rechaza cualquier host. Editar /etc/gitea/app.ini:
[webhook]
ALLOWED_HOST_LIST = *
SKIP_TLS_VERIFY = false
Y reiniciar: docker restart gitea.
5. Crear el webhook en Gitea
SECRET=$(cat /root/dummybot-webhook-secret)
curl -X POST "https://git.anabolictrack.com/api/v1/repos/javiservices/dummybot/hooks" \
-u "javiservices:<token>" \
-H "Content-Type: application/json" \
-d "{
\"type\": \"gitea\",
\"config\": {
\"url\": \"http://dummybot_expose:80/webhook/deploy\",
\"content_type\": \"json\",
\"secret\": \"$SECRET\"
},
\"events\": [\"push\"],
\"active\": true,
\"branch_filter\": \"main\"
}"
Importante: la URL debe usar el nombre del container (dummybot_expose)
no la IP pública. Si el container se renombra, hay que actualizar el webhook.
6. Primer deploy manual
Como el endpoint /webhook/deploy no existe hasta que se despliega por primera
vez, hay que hacerlo manualmente:
ssh root@157.180.77.232
cd /root/dummybot
git fetch origin main
git reset --hard origin/main
cp /root/dummybot.env .env
docker compose exec -T app php artisan migrate --force
docker compose exec -T app php artisan config:clear route:clear view:clear optimize
docker compose up -d --remove-orphans
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:18080/up # debe ser 200
Tras este primer deploy, los siguientes son automáticos.
Verificación
Probar manualmente (sin push)
SECRET=$(cat /root/dummybot-webhook-secret)
BODY='{"ref":"refs/heads/main","pusher":{"name":"manual"}}'
SIG="sha256=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')"
curl -X POST "http://157.180.77.232:18080/webhook/deploy" \
-H "Content-Type: application/json" \
-H "X-Hub-Signature-256: $SIG" \
-d "$BODY"
Respuesta esperada: {"ok":true,"status":"deploy started","log":"deploy-..."} con HTTP 202.
Ver logs
# Logs de deploy
ls -lat /root/dummybot/storage/logs/deploy-*.log | head
# Log de un deploy concreto
tail -50 /root/dummybot/storage/logs/deploy-20260618-102042.log
# Laravel general
tail -f /root/dummybot/storage/logs/laravel.log
Probar webhook desde Gitea
curl -X POST "https://git.anabolictrack.com/api/v1/repos/javiservices/dummybot/hooks/1/tests" \
-u "javiservices:<token>"
HTTP 204 = OK. Si hay error de entrega, ver logs del container gitea:
docker logs gitea --tail 50.
Troubleshooting
| Síntoma | Causa | Solución |
|---|---|---|
| Push no dispara deploy | webhook.ALLOWED_HOST_LIST no configurado |
Editar /etc/gitea/app.ini, reiniciar gitea |
| HTTP 419 al webhook | CSRF no excluido | Revisar bootstrap/app.php |
| HTTP 401 al webhook | Firma inválida | Verificar DEPLOY_WEBHOOK_SECRET en server y Gitea |
| Webhook devuelve "skipped: branch != main" | Bug substr($ref, 10) debe ser 11 |
Actualizar WebhookController.php |
| Deploy log dice "Deploy OK" pero server tiene código viejo | php artisan optimize no recarga |
Reiniciar container: docker compose restart app |
docker compose up falla con "container name already in use" |
Container huérfano de un deploy anterior | docker rm -f dummybot_expose y reintentar |
Por qué webhook y no Actions
Gitea 1.22.6 tiene Actions parcial:
- ✓ Registro de runners funciona
- ✓ API de secrets/variables funciona
- ✗ API de dispatch devuelve 404
- ✗ Ejecución automática de workflows no se dispara
Gitea Actions se estabilizó en 1.23+. El webhook de Gitea es la solución estándar pre-Actions y funciona en cualquier versión.
Si en el futuro se actualiza Gitea, el workflow .gitea/workflows/deploy.yml
ya está listo y se puede activar configurando el secreto DEPLOY_SSH_KEY en
Gitea (la SSH key pública ya está en el server en
/root/.ssh/authorized_keys).