- app.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11 KiB
name, lang, domain, version, description, tags, uses_functions, uses_types, framework, entry_point, dir_path, service
| name | lang | domain | version | description | tags | uses_functions | uses_types | framework | entry_point | dir_path | service | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| deploy_server | go | infra | 0.1.0 | 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. |
|
|
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:
- Por paths: analiza los archivos modificados en los commits. Si algun archivo empieza con
apps/<nombre>/, extrae<nombre>como app afectada. - Fallback por nombre de repo: si no hay match por paths, compara
repository.namedel 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_SECRETesta seteada, se valida el headerX-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), red10.0.17.0/24, gateway10.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 9090para 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-userypass 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 campohosten el target es el alias SSH. - Para strategy
docker-compose: usadocker compose(v2, sin guion). Verificar condocker compose version. - Para strategy
systemd-remote: elbuild_cmdse ejecuta via SSH en el remote_dir, no localmente. - El webhook matching por
repository.namepermite 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.
Capability growth log
Una linea por bump SemVer. Bump-type segun .claude/commands/version.md:
-
major: breaking observable (CLI args, schema BBDD propia, formato wire). -
minor: feature aditiva (nuevo panel, endpoint, opcion). -
patch: bugfix sin cambio observable. -
v0.1.0 (2026-05-18) — baseline.