# 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 `.env` real (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`): 1. Lee `DEPLOY_WEBHOOK_SECRET` del `.env`. 2. Calcula `hash_hmac('sha256', body, secret)` y compara con header. 3. Decodifica payload, comprueba `ref == "refs/heads/main"`. 4. Lanza `nohup bash deploy-runner.sh > log 2>&1 &`. 5. Responde 202 con path al log. **Excluido de CSRF** en `bootstrap/app.php`: ```php $middleware->validateCsrfTokens(except: ['webhook/*']); ``` --- ## Configuración inicial del server ### 1. SSH key para acceso de emergencia ```bash 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 ```bash # 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 ```bash openssl rand -hex 32 # → "75f02729eecd38c9..." ``` Guardarlo en dos sitios: - `/root/dummybot/.env`: `DEPLOY_WEBHOOK_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`: ```ini [webhook] ALLOWED_HOST_LIST = * SKIP_TLS_VERIFY = false ``` Y reiniciar: `docker restart gitea`. ### 5. Crear el webhook en Gitea ```bash SECRET=$(cat /root/dummybot-webhook-secret) curl -X POST "https://git.anabolictrack.com/api/v1/repos/javiservices/dummybot/hooks" \ -u "javiservices:" \ -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: ```bash 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) ```bash 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 ```bash # 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 ```bash curl -X POST "https://git.anabolictrack.com/api/v1/repos/javiservices/dummybot/hooks/1/tests" \ -u "javiservices:" ``` 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`).