Proyecto práctico: Crea y ejecuta una pipeline completa para una app demo

Lectura
40 min~5 min lectura

Concepto clave

Una pipeline CI/CD completa es un flujo automatizado que integra desarrollo, pruebas y despliegue en un solo proceso. Imagina una fábrica de coches: cada pieza (código) pasa por una línea de montaje (pipeline) donde se verifica su calidad (tests), se ensambla (build) y finalmente se entrega al cliente (deploy). GitHub Actions actúa como el sistema de control de esa fábrica, orquestando cada etapa automáticamente cuando un desarrollador "empuja" cambios al repositorio.

En el contexto de aplicaciones modernas, esta pipeline no solo compila código, sino que ejecuta pruebas unitarias y de integración, analiza seguridad, genera artefactos y despliega en diferentes entornos (desarrollo, staging, producción). El valor real está en la retroalimentación inmediata: si un test falla, el desarrollador lo sabe en minutos, no días, reduciendo el tiempo de corrección de errores.

Cómo funciona en la práctica

Vamos a construir una pipeline para una aplicación Node.js con React frontend y API backend. El flujo tendrá estas etapas:

  1. Trigger: Se activa al hacer push a la rama main o abrir un pull request.
  2. Checkout: GitHub Actions descarga el código del repositorio.
  3. Setup: Configura Node.js y dependencias.
  4. Lint y Build: Verifica estilo de código y compila.
  5. Tests: Ejecuta pruebas unitarias y de integración.
  6. Security Scan: Analiza vulnerabilidades con CodeQL.
  7. Deploy: Despliega a un entorno de staging (Vercel para frontend, Heroku para backend).

Cada etapa es un job en GitHub Actions, que puede ejecutarse en paralelo o secuencialmente. Si una etapa falla, la pipeline se detiene, previniendo desplegar código defectuoso.

Código en acción

Aquí está el archivo .github/workflows/ci-cd-pipeline.yml que define nuestra pipeline:

name: CI/CD Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  lint-and-build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run ESLint
        run: npm run lint
      
      - name: Build application
        run: npm run build

  test:
    runs-on: ubuntu-latest
    needs: lint-and-build
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests
        run: npm test
        env:
          CI: true

  security-scan:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Initialize CodeQL
        uses: github/codeql-action/init@v2
        with:
          languages: javascript
      
      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v2

  deploy:
    runs-on: ubuntu-latest
    needs: [test, security-scan]
    if: github.ref == 'refs/heads/main'
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Deploy to Vercel (Frontend)
        run: |
          npm install -g vercel
          vercel --prod --token=${{ secrets.VERCEL_TOKEN }}
      
      - name: Deploy to Heroku (Backend)
        run: |
          git push https://heroku:${{ secrets.HEROKU_API_KEY }}@git.heroku.com/${{ secrets.HEROKU_APP_NAME }}.git main

Antes de refactorizar, teníamos jobs separados para frontend y backend. Después, los combinamos en una pipeline unificada con dependencias claras (needs). Esto mejora la eficiencia y visibilidad.

Errores comunes

  • No usar secrets correctamente: Exponer tokens en el código. Solución: Usa GitHub Secrets y referéncialos con ${{ secrets.NOMBRE }}.
  • Jobs sin dependencias: Ejecutar deploy antes de que pasen los tests. Solución: Define needs para controlar el orden.
  • Ignorar el cache: Reinstalar dependencias cada vez, ralentizando la pipeline. Solución: Implementa caching con actions/cache.
  • No manejar fallos: Si un job falla, la pipeline se detiene sin limpieza. Solución: Usa continue-on-error o steps de cleanup.
  • Desplegar en cada PR: Consumir recursos innecesarios. Solución: Usa condiciones como if: github.ref == 'refs/heads/main'.

Checklist de dominio

  1. ¿Puedes crear un workflow que se active en push y pull requests?
  2. ¿Sabes configurar jobs con dependencias usando needs?
  3. ¿Implementas caching para acelerar builds?
  4. ¿Usas GitHub Secrets para manejar credenciales de forma segura?
  5. ¿Incluyes etapas de lint, test, security y deploy?
  6. ¿Manejas condiciones para controlar cuándo ejecutar deploy?
  7. ¿Monitorizas la pipeline con badges en el README?

Implementa una pipeline CI/CD para una app demo de tareas

En este ejercicio, crearás una pipeline completa para una aplicación de lista de tareas (todo app) con frontend en React y backend en Node.js. Sigue estos pasos:

  1. Prepara el entorno:
    • Forkea este repositorio de ejemplo: https://github.com/example/todo-app-demo
    • Clona tu fork localmente y navega al directorio.
  2. Crea el workflow:
    • En tu repositorio, crea un archivo en .github/workflows/ci-cd.yml.
    • Define el trigger para activarse en push a main y pull requests.
  3. Configura los jobs:
    • Job 1: lint-build - Usa actions/checkout, setup-node, instala dependencias con npm ci, ejecuta ESLint y build.
    • Job 2: test - Depende de lint-build, ejecuta tests con npm test.
    • Job 3: deploy-staging - Depende de test, despliega a Vercel (frontend) y Heroku (backend) solo en la rama main.
  4. Agrega mejoras:
    • Implementa caching para node_modules usando actions/cache.
    • Añade un step de security scan con CodeQL para JavaScript.
  5. Prueba la pipeline:
    • Haz un push a tu rama main y verifica que todos los jobs pasen en la pestaña Actions de GitHub.
    • Abre un pull request y confirma que la pipeline se ejecute.

Entrega: Sube el archivo YAML a tu repositorio y comparte el link en la discusión del curso.

Pistas
  • Usa el ejemplo de código de la lección como base, adaptando los nombres de los jobs y steps.
  • Para caching, investiga la acción actions/cache con la clave 'node-modules-${{ hashFiles('package-lock.json') }}'.
  • Configura los secrets VERCEL_TOKEN y HEROKU_API_KEY en Settings > Secrets and variables > Actions de tu repositorio.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.