Tabla de Contenidos

7. Despliegue en el proyecto

Objetivo del tema: /git-flow publish = 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:

Todos los runners comparten el Docker del servidor, así que con 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.

Los mismos scripts

En el servidor no hay nada nuevo que aprender: la aplicación se opera con el mismo script del tema 4, app.sh, con sus cuatro subcomandos. Lo único que cambia es una variable de entorno que fija el workflow:

COMPOSE_FILE=compose.yaml

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 app.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
        run: mvn -B test

      - name: Tests de API y E2E
        working-directory: frontend
        run: |
          npm ci
          npx playwright install --with-deps chromium
          npm run test:api
          npm run test:e2e

  deploy:
    needs: ci
    if: github.ref == 'refs/heads/main'
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4
      - name: Desplegar
        env:
          COMPOSE_FILE: compose.yaml
          DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
        run: ./app.sh start
      - name: Smoke test
        run: curl -fsS http://<alumno>.proyectosdaw.cip.fpmislata.com/api/clientes > /dev/null

Lo que hay que entender:

Y dos workflows pequeños de operación, status.yml y logs.yml, solo con workflow_dispatch y un paso ./app.sh status o ./app.sh logs (con el mismo COMPOSE_FILE) 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. Y no hay skill para cuando CI sale en rojo, porque no debería salir en rojo: pasa los mismos comandos que /git-flow finish acaba de pasar en el worktree. Si sale, es que algo distingue el runner del portátil (un fichero que no está en git, una variable, un puerto) o que ha fallado el despliegue o el smoke test. Un workflow rojo tiene un log largo y la causa está en tres líneas: se abre el paso fallido en Actions, se copia su log y se le pregunta al agente en una conversación nueva. Desde la terminal, gh run view –log-failed da solo los pasos que han fallado, que es lo que hay que pegarle (tema 5: no pegarle logs enteros).

Ejercicios

Ejercicio 1

Ejercicio 2

Ejercicio 3

Ejercicio 4

Fuera de este tema, por ser de un sistema real y no de un proyecto de clase: entornos de dev y pre, copias de seguridad, copiar datos de producción, tareas programadas y HTTPS (el proxy sirve HTTP).