Deploy desde GitHub

Clona, construye y publica un repo de GitHub en el VPS; redeploy en cada push, historial por fases y vuelta atrás.

  • Categoría: Servidores e infraestructura
  • Estado: Disponible
  • Requisitos: Un VPS activo de la empresa con la función deploy_github disponible; La cuenta de GitHub de quien vincula, conectada a ArduMaker con acceso al repo; Ser miembro de la empresa con acceso a ese VPS (en Git un miembro hace lo mismo que un admin)
  • Herramientas MCP (Solo leen): listar_repos_github_vps, ver_repo_github_vps, listar_commits_repo_github_vps, ver_commit_repo_github_vps, listar_logs_repo_github_vps, ver_log_repo_github_vps, ver_asistente_git_app, ver_config_deploy_app, ver_propietarios_github, ver_operacion_app
  • Herramientas MCP (Escriben o modifican): instalar_app, vincular_repo_github_vps, crear_repo_github_vps, editar_repo_github_vps, redeploy_repo_github, guardar_config_deploy_app, controlar_app
  • Herramientas MCP (Destructivas): eliminar_repo_github_vps, desinstalar_app, publicar_repo_en_dominio

Markdown

¿Qué permite hacer?

El deploy de Git lleva un repositorio de GitHub a un VPS y lo mantiene al día: lo clona, lo construye, lo deja corriendo como servicio de systemd (o contenedor), lo publica con HTTPS y lo vuelve a desplegar en cada push a la rama elegida.

  • Detectar qué es el repo (Node, Django, Python, sitio estático, Docker, Procfile) y proponer comandos de build y arranque.
  • Declarar en un documento de deploy las fases (pre_pull, install, build, release), los procesos (servicios, temporizadores, trabajos puntuales, estáticos, Docker, TCP), las variables de entorno y la publicación.
  • Desplegar automáticamente con cada push, a mano, o un commit puntual de la rama.
  • Volver al commit de un deploy anterior que terminó bien.
  • Ver el historial de deploys con cada fase, sus tiempos y los comandos con su salida (con los secretos tapados).
  • Crear un repositorio nuevo en GitHub desde ArduMaker y desplegarlo.

¿Cuándo conviene usarlo?

  • Una web, una API o un worker que vive en GitHub y tiene que quedar siempre igual a la rama principal.
  • Tareas programadas del proyecto (un timer) que tienen que viajar con el código.
  • Un equipo que quiere ver qué commit está en producción y poder volver atrás sin entrar al servidor.

¿Cómo funciona en ArduMaker?

1. Vincular y clonar

Hay dos puertas. La recomendada es instalarlo como app (instalar_app con app_type: git, repo_id, branch y, si se quiere, port y el documento deploy): queda en el inventario de Apps y la operación es durable. La otra es la pestaña GitHub del VPS (vincular_repo_github_vps), que solo clona y corre en segundo plano.

Al vincular, ArduMaker crea en GitHub un webhook con un secreto propio del repo: cada entrega llega firmada con HMAC-SHA256 y sin nada secreto en la URL. El repo se clona en <carpeta-del-deploy>. La URL con el token de GitHub queda en la carpeta .git, que es de root y no la puede leer la app.

2. Detectar y configurar

ver_asistente_git_app lista los repos que la cuenta de GitHub puede leer, sus ramas y, con repo y rama, lo que detecta leyendo los archivos sin ejecutar nada: framework, comandos de build y arranque y un puerto libre del VPS. Los formatos conocidos son Docker (también Compose), Procfile, Node web, Django, sitio estático de Vite, Python web y manual. Una migración propuesta siempre llega sin confirmar: el documento no valida hasta que una persona la confirma.

Lo que corre cada deploy es siempre un documento: el guardado con guardar_config_deploy_app, o, si el repo nunca guardó uno, el derivado de sus scripts de siempre (pre-pull, post-pull y servicios). Guardar un documento migra el repo: desde ahí manda el documento.

3. Desplegar por fases

Cada deploy corre estas fases en orden y las anota en el historial. Si una falla, las siguientes no corren y el repo queda en error con un texto corto (Deploy failed at the <fase> phase), nunca la salida cruda de un comando.

FaseQué hace
prepararUsuario de Linux de la app, carpeta de la plataforma, archivos de variables, Docker si hace falta
pre_pullphases.pre_pull en la carpeta del clon
pullTrae la rama (o deja la carpeta en el commit pedido)
installphases.install, paso largo
buildphases.build, paso largo; después los contenedores de los procesos docker
releaseLa migración, solo si está confirmada
procesosEscribe o reinicia unidades, temporizadores y trabajos puntuales
publicarServer block del dominio en el nginx del VPS, si el perfil publica
saludVerifica que la app conteste o que las unidades sigan vivas

Con la app instalada en Apps, el deploy lo corre el runner de operaciones y se retoma si el worker se cae. Un repo sin app corre el mismo deploy en segundo plano. Varios push seguidos se juntan: como mucho un deploy corriendo y uno en cola por app.

¿Qué cambia realmente?

<pk> es el número del repo en ArduMaker. Con policies.run_as: "root" no se crea el usuario y todo es de root.

QuéDóndeDueño y modo
Usuario de la appardu-app<pk>, de sistema, sin login—
Clon<carpeta-del-deploy>del usuario; .git de root
Carpeta de la plataforma/etc/ardumaker/deploy/ardu-repo<pk>/root:ardu-app<pk> 0750
Variables de ejecución y de build…/run.env, …/build.envardu-app<pk> 0600
Guiones de cada paso y su salida…/install.sh, build.sh, release.sh, <paso>.log0750 / root
Servicio principal/etc/systemd/system/ardu-repo<pk>.serviceroot 0644
Otros procesos y temporizadoresardu-repo<pk>-<proceso>.service / .timerroot 0644
Contenedoresardu-repo<pk>, ardu-repo<pk>-<proceso>—
Server block/etc/nginx/sites-available/<dominio>.conf—

Una unidad que no cambió solo se reinicia; una que cambió se reescribe. Una unidad adoptada (de un repo de antes) se reinicia y nunca se escribe ni se borra. Lo que un deploy creó y el documento ya no tiene se borra en el deploy siguiente.

El documento de deploy

Es un JSON versionado (schema_version: 2). Se lee con ver_config_deploy_app y se reemplaza entero con guardar_config_deploy_app (apply=true lo aplica ya con un reconfigure).

ParteValores
profilestatic, web, worker, scheduled, job, docker, network y code (solo código: en cada push trae el repositorio y nada más; sin instalar, compilar, migrar, procesos ni publicación)
phasespre_pull, install, build y release (command, confirmed, on_update)
processesHasta 20, de tipo service, timer (con schedule), oneoff, static, docker o tcp; puertos de 1024 a 65535, sin repetir
network.publish.modenone, auto_subdomain, domain, port
network.health.typehttp, unit, timer, static
envLista de {name, value} con scope build, run o ambos
policiesbuild_timeout_s 60–7200 (600 por defecto, por paso), memory_max_mb y cpu_quota_pct sin tope por defecto, run_as (app_user o root), rollback_on_failed_health
autodeployenabled: si un push despliega

Variables y secretos

Una variable con nombre de secreto (*_TOKEN, *_PASSWORD, *_KEY…), con secret: true o con una URL que trae usuario y contraseña se guarda cifrada y en el documento queda solo la referencia. Al leer, env muestra solo {name, scope, has_value}, nunca un valor. Mandar una variable sin value conserva la guardada.

  • Los valores se escriben en run.env y build.env (0600) recién en la fase preparar. Un valor con salto de línea se rechaza.
  • Cambiar una variable de build (NEXT_PUBLIC_*, VITE_*) obliga a compilar de nuevo: el reconfigure lo hace con el mismo código y sin release.
  • En el log de cada comando la carga secreta se reemplaza por su tamaño y la salida lleva cada secreto tapado ([redacted]).

Pasos largos, topes y salud

install, build y release corren cada uno en su propia unidad transitoria, con el usuario de la app, /tmp privado y set -e (corta en el primer comando que falla):

sudo systemd-run --unit=ardu-deploy<pk>-<paso> --wait --collect --pipe \
  -p User=ardu-app<pk> -p RuntimeMaxSec=<build_timeout_s> -p NoNewPrivileges=yes -p PrivateTmp=yes \
  -p EnvironmentFile=/etc/ardumaker/deploy/ardu-repo<pk>/build.env \
  /bin/bash /etc/ardumaker/deploy/ardu-repo<pk>/<paso>.sh
SaludQué se comprueba
httpcurl a 127.0.0.1:<puerto><ruta> hasta 25 veces; sin expect vale cualquier respuesta que no sea 5xx
unitCada unidad activa dos veces con 8 s en el medio y sin reinicios nuevos
timerEl .timer habilitado y activo, y su horario válido
staticExiste la carpeta y el nginx del VPS responde sin 5xx para el dominio

Puertos y publicación

La app escucha en un puerto interno del VPS. Si se instala por Apps sin port (o con "auto"), se toma el primer puerto libre. Los puertos de los motores y del agente nunca se publican. La dirección automática es git-<id> dentro de la zona de la plataforma; también se puede usar un dominio propio que apunte al VPS.

La fase publicar escribe el server block en el nginx del VPS; el DNS, el proxy hacia el VPS, el certificado y la comprobación del enlace por HTTPS los hace la publicación de Apps. Si esa parte falla (por ejemplo, el DNS todavía no propagó), el reintento publica sin volver a construir. Un nginx editado a mano no se pisa.

Push, commits puntuales y vuelta atrás

  • Un push de otra rama se ignora. Con autodeploy apagado, una entrega repetida de GitHub o un deploy ya corriendo, el push anota los commits pero no despliega, y la respuesta dice por qué.
  • redeploy con commit_sha deja la carpeta en ese commit de la rama y corre un update normal, con release si está confirmada.
  • rollback con run_id vuelve al commit de un deploy que terminó bien (git reset --hard <sha>) con la configuración de ahora. Se rechaza un deploy de otro repo, uno fallido o el que ya está desplegado.
  • Con rollback_on_failed_health: true, un deploy disparado por push que falla en salud vuelve una vez al último commit sano y apaga el autodeploy, dejando el motivo escrito.

Desvincular un repo

Borrar el repo quita lo que el documento creó: unidades y temporizadores, contenedores, la carpeta de la plataforma, el usuario de la app con su carpeta y la entrada de safe.directory; después la unidad y el contenedor principales, el clon y el webhook de GitHub. Lo adoptado no se toca.

Esquema

  1. GitHub: push a la rama seguida; webhook firmado
  2. Backend de ArduMaker: registra commits y encola el deploy
  3. Agente del VPS: ejecuta cada fase
  4. Clon + build: systemd-run como ardu-app<pk>
  5. `ardu-repo<pk>.service`: la app en 127.0.0.1:<puerto>
  6. nginx del VPS: server block del dominio
  7. Internet: HTTPS en el dominio publicado
De un push a la app publicada — El certificado lo pone la plataforma delante del VPS; el nginx del VPS reparte el dominio al puerto interno de la app.

Resultado esperado

El repo queda pulled (u online tras la instalación) con last_commit_sha igual al commit que está en la carpeta. ver_repo_github_vps trae last_runs: origen (webhook, manual o mcp), commits, resultado y cómo le fue a cada fase. Si se publicó, ver_app muestra la URL HTTPS solo cuando el enlace se verificó.

systemctl status ardu-repo<pk>.service
curl -I http://127.0.0.1:<puerto>/

Errores y resolución

ErrorCausa¿Reintentar?
github_no_vinculadoQuien pide no tiene GitHub conectado o el token vencióSí, tras volver a conectar GitHub
repo_inaccesible / rama_inexistenteLa cuenta no lee el repo o la rama no existeNo
clon_fallido / github_no_respondeFalló el clon (no deja nada) o GitHub no contestóSí
puerto_ocupadoOtra app del VPS ya usa ese puertoCon otro puerto
deploy_en_cursoYa había otro deploy corriendo en ese repoSí, cuando termine
preparacion_fallida, pull_fallido, build_fallido, release_fallida, app_no_arranco, salud_fallida, nginx_del_vps_invalidoLa fase que fallóA mano, nunca solo
config_invalidaEl documento no valida; errors por ruta (processes.web.port)Corrigiendo el documento
  • preparar failed - missing value for <VARIABLE>: falta el valor de una variable; el deploy se frena antes de tocar el VPS.
  • Aviso limits_not_applied: el VPS rechazó los topes de memoria o CPU y el paso corrió sin ellos.
  • Aviso health_check_failed: en un repo de configuración heredada la salud que no pasa es un aviso, no un error.
  • La app contesta solo en [::1]: el nginx del VPS llega por 127.0.0.1; hay que escuchar en 127.0.0.1 o 0.0.0.0.

Dónde mirar: ver_repo_github_vps (últimos deploys y fases), listar_logs_repo_github_vps y ver_log_repo_github_vps (comando y salida, con el token y los secretos tapados), y ver_operacion_app para la operación de Apps.

Relación con MCP

El deploy de Git tiene acceso MCP completo; referencia en /mcp/docs y en /mcp/deploy. Las escrituras las puede hacer cualquier miembro con acceso al VPS, salvo un agente autorizado solo lectura.

TipoHerramientas
Lecturalistar_repos_github_vps, ver_repo_github_vps, listar_commits_repo_github_vps, ver_commit_repo_github_vps, listar_logs_repo_github_vps, ver_log_repo_github_vps, ver_asistente_git_app, ver_config_deploy_app, ver_propietarios_github
Modificaninstalar_app, vincular_repo_github_vps, crear_repo_github_vps, editar_repo_github_vps, guardar_config_deploy_app, redeploy_repo_github, controlar_app, publicar_repo_en_dominio
Destructivaseliminar_repo_github_vps, desinstalar_app

controlar_app sobre una app git acepta redeploy (con commit_sha opcional), rollback (con run_id) y reconfigure (aplica el documento sin traer código). publicar_repo_en_dominio hace en una llamada dominio, verificación, activación con SSL, server block e instalación; con simular=true muestra el plan y con desde retoma donde falló.

Funciones relacionadas