750b7abcd5
- .claude/CLAUDE.md - .claude/agents/fn-recopilador/SKILL.md - .claude/rules/INDEX.md - .claude/rules/cpp_apps.md - bash/functions/infra/build_cpp_windows.sh - cpp/CMakeLists.txt - cpp/PATTERNS.md - cpp/framework/app_base.cpp - cpp/framework/app_base.h - dev/issues/README.md - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
155 lines
7.5 KiB
Markdown
155 lines
7.5 KiB
Markdown
---
|
|
name: dockerize_app
|
|
kind: pipeline
|
|
lang: bash
|
|
domain: pipelines
|
|
version: "1.0.0"
|
|
purity: impure
|
|
signature: "dockerize_app(app_name: string, [--domain DOMAIN], [--port PORT], [--ssh-host HOST], [--remote-dir DIR], [--basic-auth USER:PASS], [--no-auth], [--no-gzip], [--env KEY=VAL]..., [--volume NAME], [--build-cmd CMD], [--standalone], [--dry-run]) -> json"
|
|
description: "Empaqueta una app Go del registry para deploy a VPS organic-machine via Docker + Traefik + Coolify. Genera Dockerfile multi-stage, docker-compose.yml, traefik-dynamic.yml con basicAuth opcional y gzip, sube via rsync al VPS y arranca el stack remoto. Replica el patron de apps/registry_api/."
|
|
tags: ["docker", "traefik", "coolify", "deploy", "pipeline", "launcher"]
|
|
uses_functions:
|
|
- generate_dockerfile_go_infra
|
|
- bcrypt_htpasswd_go_infra
|
|
- generate_compose_traefik_go_infra
|
|
- generate_traefik_dynamic_go_infra
|
|
- rsync_deploy_bash_infra
|
|
- docker_compose_remote_deploy_bash_infra
|
|
- health_check_http_go_infra
|
|
- gitea_create_repo_bash_infra
|
|
- gitea_push_directory_bash_infra
|
|
uses_types:
|
|
- ComposeTraefikConfig_go_infra
|
|
- TraefikDynamicConfig_go_infra
|
|
returns: []
|
|
returns_optional: false
|
|
error_type: "error_go_core"
|
|
imports: []
|
|
params:
|
|
- name: app_name
|
|
desc: "Nombre o ID parcial de la app en registry.db (ej: kanban, deploy_server). Se busca con LIKE '<app_name>%' OR name='<app_name>'."
|
|
- name: domain
|
|
desc: "Dominio publico completo para el router Traefik (ej: kanban.organic-machine.com). Obligatorio."
|
|
- name: port
|
|
desc: "Puerto interno del contenedor Docker (default: 8080). Debe coincidir con el puerto en que la app escucha."
|
|
- name: ssh-host
|
|
desc: "Alias o IP del host SSH destino (default: organic-machine.com). Debe estar en ~/.ssh/config o ser accesible con key auth."
|
|
- name: remote-dir
|
|
desc: "Ruta absoluta en el VPS donde se desplegara la app (default: /home/ubuntu/coolify-apps/<app_name>). En modo rsync apunta al subdir de la app dentro del build root."
|
|
- name: basic-auth
|
|
desc: "Credenciales para basicAuth de Traefik en formato USER:PASS. Obligatorio si auth esta ON (defecto). Se hashea con bcrypt via htpasswd o python3+bcrypt."
|
|
- name: no-auth
|
|
desc: "Flag para deshabilitar basicAuth. Por defecto auth esta habilitado; se requiere --basic-auth USER:PASS si no se pasa --no-auth."
|
|
- name: no-gzip
|
|
desc: "Flag para deshabilitar el middleware gzip de Traefik. Por defecto gzip esta habilitado."
|
|
- name: env
|
|
desc: "Variable de entorno KEY=VAL a incluir en el .env y en la seccion environment del docker-compose.yml. Repetible para multiples vars."
|
|
- name: volume
|
|
desc: "Nombre de un Docker volume que se monta en /data dentro del contenedor. Se declara en la seccion volumes del compose."
|
|
- name: build-cmd
|
|
desc: "Comando de build personalizado (documentado para uso futuro; Phase 1 usa el Dockerfile multi-stage generado)."
|
|
- name: standalone
|
|
desc: "Modo standalone: crea repo Gitea dataforge/<app> y usa git clone/pull en el VPS en vez de rsync. Requiere GITEA_URL y credenciales Gitea configuradas."
|
|
- name: dry-run
|
|
desc: "Imprime los artefactos generados (Dockerfile, docker-compose.yml, traefik-dynamic.yml, .env) a stderr y retorna JSON con status=dry-run sin ejecutar ningun comando remoto ni escribir ficheros en la app."
|
|
output: "JSON a stdout: {status, app, domain, remote_dir, container_id, duration_seconds, auth_enabled, gzip_enabled, http_code, url}. status='ok' si el health check responde HTTP 200/401, 'failed' si hay timeout, 'dry-run' en modo dry-run."
|
|
tested: false
|
|
tests: []
|
|
test_file_path: ""
|
|
file_path: "bash/functions/pipelines/dockerize_app.sh"
|
|
---
|
|
|
|
## Ejemplo
|
|
|
|
```bash
|
|
# Deploy completo con basicAuth
|
|
cd /home/lucas/fn_registry
|
|
bash bash/functions/pipelines/dockerize_app.sh kanban \
|
|
--domain kanban.organic-machine.com \
|
|
--port 8421 \
|
|
--basic-auth lucas:supersecret \
|
|
--env KANBAN_DB=/data/kanban.db \
|
|
--volume kanban_data
|
|
|
|
# Salida esperada:
|
|
# {"status":"ok","app":"kanban","domain":"kanban.organic-machine.com","remote_dir":"...","container_id":"abc123","duration_seconds":45,"auth_enabled":true,"gzip_enabled":true,"http_code":"401","url":"https://kanban.organic-machine.com"}
|
|
|
|
# Deploy sin auth (app publica)
|
|
bash bash/functions/pipelines/dockerize_app.sh registry_api \
|
|
--domain registry.organic-machine.com \
|
|
--port 8080 \
|
|
--no-auth \
|
|
--env REGISTRY_API_TOKEN=mytoken
|
|
|
|
# Dry-run: ver YAMLs sin tocar nada
|
|
bash bash/functions/pipelines/dockerize_app.sh kanban \
|
|
--dry-run \
|
|
--domain kanban.organic-machine.com \
|
|
--port 8421 \
|
|
--basic-auth lucas:test123
|
|
|
|
# Standalone: repo Gitea + git clone en VPS
|
|
bash bash/functions/pipelines/dockerize_app.sh deploy_server \
|
|
--domain deploy.organic-machine.com \
|
|
--port 9090 \
|
|
--basic-auth lucas:secret \
|
|
--standalone
|
|
```
|
|
|
|
## Pasos internos
|
|
|
|
| Paso | Descripcion |
|
|
|------|-------------|
|
|
| 1 | Valida la app en registry.db (SQL sobre tabla apps) |
|
|
| 2 | Valida conectividad SSH (BatchMode, ConnectTimeout=5) |
|
|
| 3 | Genera hash bcrypt via htpasswd o python3+bcrypt |
|
|
| 4 | Genera Dockerfile multi-stage Go (heredoc, patron generate_dockerfile_go_infra) |
|
|
| 5 | Genera docker-compose.yml con Traefik labels y red coolify |
|
|
| 6 | Genera traefik-dynamic.yml con routers HTTP/HTTPS, basicAuth, gzip, certResolver letsencrypt |
|
|
| 7 | Genera/actualiza .env con merge no destructivo |
|
|
| 8 | Rsync del repo completo al VPS (o git clone en standalone) |
|
|
| 9 | Crea directorio remoto de deploy |
|
|
| 10 | Sube traefik-dynamic.yml a /data/coolify/proxy/dynamic/<name>.yml via SSH+sudo tee |
|
|
| 11 | Sube docker-compose.yml, Dockerfile y .env al remote_dir via scp |
|
|
| 12 | Crea red Docker coolify si no existe; `docker compose up -d --build` remoto |
|
|
| 13 | Health check: 10 intentos, 3s intervalo, acepta HTTP 200 y 401 |
|
|
|
|
## Decision de implementacion
|
|
|
|
Los YAMLs se generan con heredocs bash inline, replicando la logica de
|
|
`generate_dockerfile_go_infra`, `generate_compose_traefik_go_infra` y
|
|
`generate_traefik_dynamic_go_infra`. Esta decision evita crear un nuevo
|
|
binario `cmd/dockerize_helpers/` y mantiene el pipeline completamente
|
|
self-contained, siguiendo el patron de `setup_registry_api_bash_infra`.
|
|
Las funciones Go quedan referenciadas en `uses_functions` como fuente de
|
|
verdad documental del patron que replica.
|
|
|
|
## Requisitos en el host local
|
|
|
|
- `ssh` y `rsync` instalados
|
|
- `htpasswd` (apache2-utils) o `python3` + modulo `bcrypt` para generar hash
|
|
- Acceso SSH sin password al host destino (key en ~/.ssh/config)
|
|
- `sqlite3` CLI para leer registry.db
|
|
|
|
## Requisitos en el VPS
|
|
|
|
- Docker + docker compose (v2)
|
|
- Coolify con Traefik corriendo y `/data/coolify/proxy/dynamic/` accesible via sudo
|
|
- Red Docker `coolify` (se crea automaticamente si no existe)
|
|
- Usuario SSH con sudo sin password para: `mkdir`, `tee` en `/data/coolify/proxy/dynamic/`
|
|
|
|
## Codigos de salida
|
|
|
|
| Codigo | Significado |
|
|
|--------|-------------|
|
|
| 0 | Exito: stack activo y health check OK |
|
|
| 1 | Error: app no encontrada, SSH inavalcable, fallo de build, health check timeout |
|
|
|
|
## Notas
|
|
|
|
- Phase 1 soporta exclusivamente apps `lang: go`. Soporte para Python y Bash en fases futuras.
|
|
- El Dockerfile se genera solo si no existe en el directorio de la app. Si ya existe (con tweaks manuales), se preserva y se avisa por stderr.
|
|
- El build context del Dockerfile es `../../` relativo a `apps/<app>/` para que el multi-stage copie el `go.mod` del root del registry y compile correctamente.
|
|
- El nombre del router Traefik reemplaza `_` por `-` (ej: `registry_api` → `registry-api`).
|
|
- En modo `--standalone`, se requieren las variables `GITEA_URL`, `GITEA_TOKEN` (o `GITEA_USER`/`GITEA_PASS`) configuradas para `gitea_create_repo` y `gitea_push_directory`.
|