Files
deploy_server/app.md
T
egutierrez d231f04a26 refactor: usar sqlite_apply_migrations_go_infra del registry
Sustituye applyMigrations + splitSQLStatements + isIdempotentError locales
por infra.ApplyMigrations del registry. ~55 LOC eliminadas.

- store.go: import fn-registry/functions/infra, llama infra.ApplyMigrations
- go.mod: declara dep fn-registry con replace local
- app.md: declara sqlite_apply_migrations_go_infra en uses_functions

Sin cambio funcional (mismo parser naive de `;`, mismas reglas idempotentes).
Bug fixes futuros se aplicaran automaticamente al consumir registry.
2026-05-09 12:40:10 +02:00

11 KiB

name, lang, domain, description, tags, uses_functions, uses_types, framework, entry_point, dir_path
name lang domain description tags uses_functions uses_types framework entry_point dir_path
deploy_server go infra Servidor de deploy continuo para apps del registry. Recibe webhooks de Gitea, gestiona targets de deploy en operations.db y orquesta deploys a VPS remotos via SSH. Soporta tres estrategias: systemd, systemd-remote y docker-compose.
service
deploy
ci
cd
webhook
gitea
ssh
vps
docker-compose
systemd
sqlite_apply_migrations_go_infra
net/http main.go apps/deploy_server

Estrategias de deploy

Tres estrategias disponibles segun el tipo de app:

Estrategia Flujo Para que
systemd (default) Build local + rsync + systemctl restart Apps Go compiladas localmente y subidas al VPS
systemd-remote SSH: git pull + build remoto + systemctl restart Apps Go cuyo source vive en el VPS (build in-situ)
docker-compose SSH: git pull + docker compose pull + up -d Stacks Docker Compose en el VPS

systemd (build local + rsync)

SSH check → build local (build_cmd en source_dir) → rsync al VPS
→ chmod +x binario → systemctl restart → health check

systemd-remote (build remoto)

SSH check → git pull origin <branch> en remote_dir
→ build remoto (build_cmd via SSH) → systemctl restart → health check

docker-compose

SSH check → git pull origin <branch> en remote_dir
→ docker compose [-f extra...] pull → docker compose up -d → health check

Uso

# Servidor (escucha webhooks y expone API)
./deploy_server serve --port 9090

# CLI: gestionar targets de deploy
./deploy_server target add --app my_app --host produccion --port 8080 --health /api/health \
  --build "CGO_ENABLED=0 GOOS=linux go build -o my_app ." --strategy systemd
./deploy_server target list
./deploy_server target remove my_app

# Target con strategy systemd-remote (build en el VPS)
./deploy_server target add --app agents_and_robots --host localhost \
  --remote-dir /home/ubuntu/CodeProyects/agents_and_robots \
  --binary launcher --build "bash build.sh" \
  --strategy systemd-remote --branch master

# Target con strategy docker-compose
./deploy_server target add --app element_matrix_chat --host localhost \
  --remote-dir /home/ubuntu/CodeProyects/element_matrix_chat \
  --strategy docker-compose --branch master \
  --compose-files "docker-compose.livekit.yml"

# CLI: deploy manual
./deploy_server deploy my_app                  # deploy a todos los hosts del target
./deploy_server deploy my_app --host produccion # deploy a un host específico

# CLI: setup inicial de una app en un VPS
./deploy_server setup my_app --host produccion

# CLI: estado de servicios remotos
./deploy_server status my_app       # systemd: systemctl status / docker-compose: docker compose ps
./deploy_server status --all

Flags de target add

Flag Default Descripcion
--app (requerido) Nombre de la app (debe coincidir con el nombre del repo en Gitea para webhooks)
--host (requerido) Alias SSH de ~/.ssh/config (o localhost si deploy_server corre en el mismo host)
--remote-dir /opt/apps/ Directorio en el host remoto donde vive la app
--binary Nombre del binario (solo para strategies systemd/systemd-remote)
--build "" Comando de build (local para systemd, remoto para systemd-remote)
--user "" Usuario systemd del servicio
--port 0 Puerto del servicio (para health checks)
--health "" Path del health check (ej: /api/health)
--env {} Variables de entorno como JSON
--strategy systemd Estrategia: systemd, systemd-remote, docker-compose
--source-dir "" Directorio local del source relativo al registry root (override de apps/)
--branch main Branch de git para strategies remotas (git pull origin )
--compose-files "" Archivos compose adicionales separados por coma (ej: "docker-compose.livekit.yml")

API

POST /webhook/push         — recibe push de Gitea, detecta app afectada, despliega
GET  /api/targets          — lista todos los targets de deploy
GET  /api/targets/:app     — detalle de un target
POST /api/deploy/:app      — trigger manual de deploy
GET  /api/status/:app      — estado del servicio remoto (systemctl status o docker compose ps)
GET  /api/health           — health check del propio deploy_server
GET  /api/logs             — ultimos 20 deploy logs (todas las apps)
GET  /api/logs/:app        — ultimos 20 deploy logs de una app

Webhook de Gitea

El endpoint POST /webhook/push recibe payloads de Gitea en formato JSON. El matching funciona en dos pasos:

  1. Por paths: analiza los archivos modificados en los commits. Si algun archivo empieza con apps/<nombre>/, extrae <nombre> como app afectada.
  2. Fallback por nombre de repo: si no hay match por paths, compara repository.name del payload contra los targets registrados.

El fallback es el mecanismo principal para repos externos (como agents_and_robots o element_matrix_chat) que no viven dentro de un monorepo con estructura apps/.

Seguridad del webhook

  • Si DEPLOY_WEBHOOK_SECRET esta seteada, se valida el header X-Gitea-Signature (HMAC-SHA256).
  • Si no esta seteada, se acepta cualquier request (solo para desarrollo).
  • El secret debe coincidir exactamente entre la env var y la configuracion del webhook en Gitea.

Configuracion en Gitea

Para crear un webhook en Gitea que apunte al deploy_server:

source bash/functions/infra/gitea_create_webhook.sh
export GITEA_URL="https://tu-gitea.ejemplo.com"
export GITEA_TOKEN="<token-api>"
gitea_create_webhook "<owner>" "<repo>" "http://<ip>:9090/webhook/push" "<webhook_secret>"

Gitea en Docker: si Gitea corre en un container Docker, 127.0.0.1 apunta al container, no al host. Usar la gateway de la red Docker del container de Gitea:

# Encontrar la gateway
docker inspect <gitea_container> --format '{{range .NetworkSettings.Networks}}{{.Gateway}}{{end}}'
# Resultado: 10.0.17.1 (ejemplo)
# URL del webhook: http://10.0.17.1:9090/webhook/push

Gitea ALLOWED_HOST_LIST: Gitea restringe por defecto las URLs de webhooks. Configurar en app.ini:

[webhook]
ALLOWED_HOST_LIST = *

O listar las IPs/subredes permitidas. Reiniciar Gitea tras el cambio.

Firewall (UFW): si el host tiene UFW activo, permitir tráfico desde redes Docker al puerto del deploy_server:

sudo ufw allow from 10.0.0.0/8 to any port 9090 comment 'deploy_server from Docker'

Despliegue del propio deploy_server

deploy_server usa github.com/mattn/go-sqlite3 (CGO). Para compilar:

# Local (Linux nativo o WSL)
CGO_ENABLED=1 go build -o deploy_server .

# Cross-compile para Linux desde otra plataforma
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o deploy_server_linux .

Instalacion en un VPS

# 1. Subir binario
scp deploy_server_linux <host>:/home/ubuntu/CodeProyects/deploy_server/deploy_server

# 2. Crear systemd unit
sudo tee /etc/systemd/system/deploy_server.service << 'EOF'
[Unit]
Description=deploy_server CI/CD
After=network.target

[Service]
Type=simple
ExecStart=/home/ubuntu/CodeProyects/deploy_server/deploy_server serve --port 9090
WorkingDirectory=/home/ubuntu/CodeProyects/deploy_server
User=ubuntu
Group=ubuntu
Environment=DEPLOY_WEBHOOK_SECRET=<secret>
Environment=PATH=/usr/local/go/bin:/usr/bin:/bin
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

# 3. Activar
sudo systemctl daemon-reload && sudo systemctl enable --now deploy_server

# 4. Verificar
curl -s http://127.0.0.1:9090/api/health

SSH a localhost

Cuando deploy_server y las apps estan en el mismo VPS, el deploy usa SSH a localhost. Requisitos:

# Generar key si no existe
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""
cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys
# Aceptar host key
ssh-keyscan localhost >> ~/.ssh/known_hosts
# Verificar
ssh -o BatchMode=yes localhost true

Despliegue actual: organic-machine.com

deploy_server corre en el VPS organic-machine.com como servicio systemd (deploy_server.service), puerto 9090.

Targets registrados

App Estrategia Host Remote dir Branch Build
agents_and_robots systemd-remote localhost /home/ubuntu/CodeProyects/agents_and_robots master bash build.sh
element_matrix_chat docker-compose localhost /home/ubuntu/CodeProyects/element_matrix_chat master
registry_api docker-compose localhost /opt/fn-registry-build/apps/registry_api master docker compose build+up

Webhooks activos

Ambos repos en Gitea (egutierrez/agents_and_robots, egutierrez/element_matrix_chat) tienen webhooks push apuntando a http://10.0.17.1:9090/webhook/push (gateway de la red Docker de Gitea).

Tiempos de deploy medidos

App Trigger Duracion
agents_and_robots webhook ~8.5s (git pull + tests + compile 4 binarios + restart)
element_matrix_chat webhook ~2.7s (git pull + docker compose pull + up -d)

Servicios en el VPS

Servicio Tipo Estado
deploy_server systemd (deploy_server.service) enabled, active, :9090
agents_and_robots systemd (agents_and_robots.service) enabled, active (launcher)
registry_api Docker (registry-api container) running, :8420, HTTPS via Traefik en registry.organic-machine.com

Infraestructura relevante

  • Gitea: corre en Docker (container gitea-dgg044oo04woo4ggcsws4gk0), red 10.0.17.0/24, gateway 10.0.17.1
  • Coolify: proxy principal en puertos 80/443/8080, gestiona servicios Docker
  • UFW: policy DROP, regla allow from 10.0.0.0/8 to port 9090 para que Docker alcance deploy_server
  • Webhook secret: guardado en pass agentes/deploy-webhook-secret
  • Gitea token: guardado en pass agentes/egutierrez-token
  • Registry API basicAuth: guardado en pass registry/basicauth-user y pass registry/basicauth-pass
  • Registry API token: guardado en pass registry/api-token

Registro en operations.db

Dos tablas:

  • deploy_targets (PK: app + host): configuracion de cada target con strategy, branch, compose_files, etc.
  • deploy_logs: un registro por cada deploy con app, host, status (success/failure), trigger (manual/api/webhook), error, duration_ms, started_at.

Notas

  • Los hosts SSH se resuelven via ~/.ssh/config — el campo host en el target es el alias SSH.
  • Para strategy docker-compose: usa docker compose (v2, sin guion). Verificar con docker compose version.
  • Para strategy systemd-remote: el build_cmd se ejecuta via SSH en el remote_dir, no localmente.
  • El webhook matching por repository.name permite que repos externos (no del monorepo) disparen deploys si el nombre del repo coincide con el nombre del target.
  • deploy_server no se auto-despliega. Actualizaciones: cross-compile local + scp + sudo systemctl restart deploy_server.