Tabla de Contenidos
¡Esta es una revisión vieja del documento!
6. Despliegue en el proyecto
Objetivo del tema: /git-flow publicar = producción actualizada, sin entrar nunca al servidor. Y en cada push, GitHub compila y pasa todos los tests. develop no se despliega en ningún sitio: es local.
Lo que ya sabéis: qué es GitHub Actions y un primer workflow con VPN y SSH, en Despliegue 5; cómo funciona nginx-proxy con VIRTUAL_HOST, en Despliegue 4.c. Lo nuevo aquí es sustituir la VPN y el SSH por un runner dentro del servidor.
El servidor
Un VPS compartido por toda la clase, con tres cosas montadas por el profesor:
- Docker.
- nginx-proxy en la red
nginx-proxy-network, escuchando en el 80. Cada aplicación se publica sola por suVIRTUAL_HOST:<alumno>.proyectosdaw.cip.fpmislata.com, que ya está encompose.yamldesde el tema 3. - Un runner autoalojado de GitHub por alumno, registrado contra su repositorio. Un runner es un proceso que espera trabajos de GitHub Actions y los ejecuta en la máquina donde está instalado. Como está en el servidor, un workflow que se ejecute en él puede hacer
docker composedirectamente: ni SSH, ni VPN, ni registro de imágenes. El profesor lo registra con el token que da GitHub en Settings → Actions → Runners → New self-hosted runner; es la única vez que alguien entra al servidor.
docker ps cada uno ve los contenedores de los demás. Es un servidor de clase y se asume. Lo que separa a cada alumno es el nombre de proyecto de Compose (el del repositorio) y su subdominio.
manage.sh
Una única entrada para operar la aplicación en el servidor, en el repositorio:
#!/bin/bash set -e cd "$(dirname "$0")" compose="docker compose -f compose.yaml" case "$1" in deploy) $compose down; $compose build; $compose up -d ;; status) $compose ps ;; logs) $compose logs --tail=200 ;; *) echo "uso: $0 deploy|status|logs"; exit 1 ;; esac
-f compose.yaml: solo producción, sin el override de desarrollo.downsin-v: se paran los contenedores pero el volumendb-datase queda. Un despliegue nunca borra la base de datos; Flyway aplica las migraciones nuevas al arrancar.- Los workflows solo llaman a
manage.sh, así que lo mismo que hace GitHub se puede lanzar a mano desde el servidor si un día hace falta.
Secretos
La contraseña de la base de datos de producción no está en ningún fichero: está en Settings → Secrets and variables → Actions del repositorio como DB_PASSWORD. El workflow la pasa como variable de entorno a manage.sh y Compose la lee en ${DB_PASSWORD}. En desarrollo esa misma variable viene del .env del worktree.
Los workflows
Un solo fichero para CI y CD:
name: CI/CD
on:
push:
branches: [develop, main]
workflow_dispatch:
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { distribution: temurin, java-version: 25 }
- uses: actions/setup-node@v4
with: { node-version: 22 }
- name: Levantar la aplicación
env: { DB_PASSWORD: ci }
run: |
docker network create nginx-proxy-network
docker compose up -d --build --wait
- name: Tests del backend (unitarios y ArchUnit)
working-directory: backend
env: { DB_PORT: 3306, DB_PASSWORD: ci }
run: mvn -B test
- name: Tests E2E
working-directory: frontend
run: |
npm ci
npx playwright install --with-deps chromium
npx playwright test
deploy:
needs: ci
if: github.ref == 'refs/heads/main'
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- name: Desplegar
env:
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
run: ./manage.sh deploy
- name: Smoke test
run: curl -fsS http://lorenzo.proyectosdaw.cip.fpmislata.com/api/clientes > /dev/null
Lo que hay que entender:
cicorre en un runner de GitHub (ubuntu-latest), en cada push adevelopy amain. Levanta la aplicación con el mismocompose.yaml+compose.override.yamldel portátil (sin.env: valen los puertos por defecto y el perfildatos-prueba), y pasa los tests exactamente igual que en el worktree.–waitespera a loshealthcheck.deploycorre en el runner del servidor (self-hosted), solo enmainy solo siciha pasado (needs). Hacecheckoutdel commit ymanage.sh deploy: la imagen se construye en el servidor con ese código.- El smoke test es el último paso: una petición a la URL de producción que tiene que responder 200. Si el despliegue ha dejado la aplicación caída, el workflow sale en rojo y se ve en el acto.
workflow_dispatchpermite relanzarlo a mano desde la pestaña //Actions// (siempre sobremain).
Y dos workflows pequeños de operación, status.yml y logs.yml, solo con workflow_dispatch y un paso ./manage.sh status o ./manage.sh logs en self-hosted. Son la forma de ver qué pasa en producción sin acceso al servidor: se lanzan desde Actions y la salida queda en el log del workflow.
La IA y los workflows
El YAML lo escribe el agente a partir de lo de arriba. Donde de verdad ayuda es cuando algo falla: un workflow rojo tiene un log largo y la causa está en tres líneas. La skill /ci-revisar sigue la ejecución y, si falla, la lee y propone el arreglo:
gh run list --limit 5 gh run watch # sigue la última ejecución gh run view <id> --log-failed # solo los pasos que han fallado
Con –log-failed el agente recibe solo lo que importa, no el log entero (tema 4: no pegarle logs enteros).
Ejercicios
Ejercicio 1
- Crea un issue «CI» e inícialo.
- Escribe
manage.shcondeploy,statusylogsy dale permisos de ejecución. - Añade el secreto
DB_PASSWORDen Settings → Secrets and variables → Actions. - Escribe
.github/workflows/ci-cd.ymlsolo con el jobci. Finaliza el issue y mira en //Actions// cómo se ejecuta en el push adevelop. Si sale en rojo, arréglalo hasta que esté en verde.
Ejercicio 2
- Pide al profesor que registre tu runner en el servidor. Comprueba en Settings → Actions → Runners que aparece como Idle.
- Crea un issue «Despliegue» e inícialo. Añade el job
deployal workflow con elneeds: ci, elifsobremainy el smoke test contra tu URL. - Finaliza el issue y
/git-flow publicar. Sigue la ejecución en //Actions// y comprueba que la aplicación responde enhttp:<alumno>.proyectosdaw.cip.fpmislata.com. ==== Ejercicio 3 ==== * Añadestatus.ymlylogs.ymlconworkflow_dispatchsobreself-hosted. * Lánzalos desde Actions y lee la salida demanage.sh statusen el log. ==== Ejercicio 4 ==== * Escribe la skill/ci-revisar:gh run watchde la última ejecución y, si falla,gh run view –log-failed, diagnóstico y propuesta de arreglo.allowed-toolssolo congh run. * Rompe un test E2E a propósito, finaliza el issue y lanza/ci-revisar. Tiene que decirte qué test ha fallado y por qué. Arregla el test y vuelve a finalizar. * Haz un cambio visible en la aplicación, finaliza y publica. Comprueba en producción que el cambio está y que los datos que había siguen estando. Fuera de este tema, por ser de un sistema real y no de un proyecto de clase: entornos dedevypre'', copias de seguridad, copiar datos de producción, tareas programadas y HTTPS (el proxy sirve HTTP).
