🚀 GitHub Actions + CI/CD + Docker

Cheat Sheet para Desarrolladoras

📋 Índice Rápido

🎯 Conceptos Fundamentales

CI/CD

CI (Continuous Integration)
Validación automática del código (tests, linting)
CD (Continuous Deployment)
Despliegue automático a producción
Pipeline
Secuencia automatizada de pasos (build → test → deploy)

Docker

Imagen
Plantilla inmutable con tu aplicación empaquetada
Contenedor
Instancia ejecutable de una imagen
Registry
Almacén de imágenes (Docker Hub, GHCR)
Multi-stage Build
Optimización para imágenes más pequeñas

GitHub Actions

Workflow
Proceso automatizado definido en YAML
Job
Conjunto de steps que se ejecutan en un runner
Step
Tarea individual dentro de un job
Runner
Máquina virtual que ejecuta los jobs
Artifact
Archivo generado durante el workflow (logs, reportes)

📝 Sintaxis YAML Esencial

Estructura Básica de un Workflow

name: CI/CD Pipeline

# ¿Cuándo se ejecuta?
on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

# ¿Qué hace?
jobs:
  build-and-test:
    runs-on: ubuntu-latest
    
    steps:
      # Paso 1: Obtener el código
      - name: Checkout code
        uses: actions/checkout@v3
      
      # Paso 2: Configurar Node.js
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'
      
      # Paso 3: Instalar dependencias
      - name: Install dependencies
        run: npm ci
      
      # Paso 4: Ejecutar tests
      - name: Run tests
        run: npm test

Triggers Comunes

# Al hacer push
on: push

# Solo en ciertas ramas
on:
  push:
    branches: [ main, develop ]

# En pull requests
on:
  pull_request:
    branches: [ main ]

# Manual (desde la UI de GitHub)
on: workflow_dispatch

# Programado (cron)
on:
  schedule:
    - cron: '0 0 * * *'  # Cada día a medianoche

Jobs Secuenciales vs Paralelos

jobs:
  # Job 1: Se ejecuta primero
  test:
    runs-on: ubuntu-latest
    steps:
      - run: npm test
  
  # Job 2: Se ejecuta después de 'test'
  build:
    needs: test  # ← Espera a que 'test' termine
    runs-on: ubuntu-latest
    steps:
      - run: npm run build
  
  # Job 3: Se ejecuta en paralelo con 'test'
  lint:
    runs-on: ubuntu-latest
    steps:
      - run: npm run lint

Matrix Strategy (Paralelización)

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20, 22]
    steps:
      - uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm test

🐳 Comandos Docker

Comandos Básicos

# Ver imágenes locales
docker images
# Ver contenedores corriendo
docker ps
# Ver TODOS los contenedores (incluso detenidos)
docker ps -a
# Construir imagen
docker build -t nombre-imagen:tag .
# Ejecutar contenedor
docker run -p 8080:80 nombre-imagen:tag
# Detener contenedor
docker stop <container-id>
# Eliminar contenedor
docker rm <container-id>
# Eliminar imagen
docker rmi <image-id>

Docker Compose

# Levantar todos los servicios
docker-compose up
# Levantar en segundo plano (detached)
docker-compose up -d
# Reconstruir y levantar
docker-compose up --build
# Detener servicios
docker-compose down
# Detener Y eliminar volúmenes (¡CUIDADO! Borra BD)
docker-compose down -v
# Ver logs en tiempo real
docker-compose logs -f
# Ver logs de un servicio específico
docker-compose logs -f backend
# Ver estado de servicios
docker-compose ps

Limpieza de Docker

# Limpiar imágenes dangling (<none>)
docker image prune -f
# Limpiar contenedores detenidos
docker container prune -f
# Limpiar TODO (imágenes, contenedores, redes, volúmenes)
docker system prune -a --volumes # ¡CUIDADO!
# Ver espacio usado por Docker
docker system df

Dockerfile Multi-Stage (Ejemplo Frontend)

# ===== STAGE 1: BUILD =====
FROM node:20-alpine AS build
WORKDIR /app

# Copiar archivos de dependencias
COPY package*.json ./
RUN npm ci --only=production

# Copiar código y compilar
COPY . .
RUN npm run build

# ===== STAGE 2: PRODUCTION =====
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

🔄 Workflows Comunes

Workflow 1: CI Básico (Test + Lint)

name: CI

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'
          cache: 'npm'  # ← Cache de node_modules
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run linter
        run: npm run lint
      
      - name: Run tests
        run: npm test

Workflow 2: Build y Push de Docker

name: Build and Push Docker Image

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      # Login en GitHub Container Registry
      - name: Login to GHCR
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      # Build y Push
      - name: Build and push
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:latest

Workflow 3: Deploy a Render (Webhook)

name: Deploy to Render

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - name: Trigger Render Deploy
        run: |
          curl -X POST ${{ secrets.RENDER_DEPLOY_HOOK }}

Workflow 4: CI/CD Completo

name: Complete CI/CD

on:
  push:
    branches: [ main ]

jobs:
  # Job 1: Test
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm test
      - run: npm run lint
  
  # Job 2: Build (solo si test pasa)
  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Login to GHCR
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      - name: Build and push
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
  
  # Job 3: Deploy (solo si build pasa)
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to Render
        run: curl -X POST ${{ secrets.RENDER_DEPLOY_HOOK }}

🔐 Secretos y Variables

Cómo Añadir Secretos en GitHub

  1. Ve a tu repositorio en GitHub
  2. Settings → Secrets and variables → Actions
  3. Click en New repository secret
  4. Nombre: OPENAI_API_KEY
  5. Valor: sk-proj-tu-api-key-aqui
  6. Add secret

Usar Secretos en Workflows

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Use secret
        env:
          API_KEY: ${{ secrets.OPENAI_API_KEY }}
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
        run: |
          echo "API Key configured"
          # El secreto NO se imprime en logs

Variables de Entorno en Docker

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Build with secrets
        run: |
          docker build \
            --build-arg API_KEY=${{ secrets.OPENAI_API_KEY }} \
            -t myapp:latest .

Archivo .env Local vs GitHub Secrets

Archivo Uso Se sube a Git?
.env Desarrollo local ❌ NO (en .gitignore)
.env.example Plantilla para equipo ✅ SÍ (sin valores reales)
GitHub Secrets CI/CD y Producción ✅ Cifrados en GitHub

🚨 Troubleshooting

Error: "Workflow does not have permission to access secrets"

Solución:

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write  # ← Añade permisos
Error: "Docker build failed"

Pasos de debug:

# 1. Construir localmente primero
docker build -t test .

# 2. Ver logs detallados
docker build --progress=plain -t test .

# 3. Verificar .dockerignore
cat .dockerignore
Error: "npm ERR! code ELIFECYCLE"

Causa común: Tests fallando

Solución:

# Ejecutar tests localmente
npm test

# Ver qué test falla específicamente
npm test -- --verbose
Error: "Cannot find module"

Causa común: Cache corrupto

Solución en workflow:

- name: Clear cache and install
  run: |
    npm ci --cache .npm --prefer-offline
Workflow se queda "pending" indefinidamente

Causa común: Runner sin disponibilidad

Solución:

Check Verde no Aparece

Causa: Branch protection no configurada

Solución:

📚 Recursos y Documentación

Documentación Oficial

Aprende Más

Ejemplos de Workflows

Comunidad y Ayuda

🎯 Comandos Para el Día a Día

Setup Inicial de Proyecto con Docker

# 1. Clonar proyecto
git clone https://github.com/tu-usuario/tu-proyecto
cd tu-proyecto

# 2. Copiar variables de entorno
cp .env.example .env
nano .env  # Editar con tus valores

# 3. Levantar con Docker
docker-compose up --build

# 4. En otra terminal, verificar
docker-compose ps

Workflow de Desarrollo

# Morning routine
git pull origin main
docker-compose up -d

# Hacer cambios en código...

# Verificar que funciona localmente
docker-compose logs -f

# Commit y push
git add .
git commit -m "feat: add new feature"
git push origin feature-branch

# GitHub Actions se ejecuta automáticamente
# Ver en: https://github.com/tu-repo/actions

Antes de Hacer Merge

# Verificar que tests pasan localmente
npm test

# Verificar que Docker builda correctamente
docker-compose build

# Verificar que no hay secretos expuestos
git diff

# Hacer merge solo si check verde ✅

💡 Tips Pro

1. Cache de npm para Builds Rápidos
- name: Setup Node.js
  uses: actions/setup-node@v3
  with:
    node-version: '20'
    cache: 'npm'  # ← Ahorra 2-3 minutos por build
2. Fail Fast en Tests
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: npm run lint      # Si falla aquí...
      - run: npm test          # ...esto no se ejecuta
      - run: docker build .    # ...ni esto
3. Branch Protection Rules

Settings → Branches → Branch protection rules

4. Optimización de Dockerfile
# ❌ MAL: Todo en una capa
FROM node:20
COPY . .
RUN npm install && npm run build

# ✅ BIEN: Capas separadas (mejor cache)
FROM node:20
COPY package*.json ./
RUN npm ci  # ← Se cachea si package.json no cambia
COPY . .
RUN npm run build
5. Variables de Entorno Organizadas
# En .env
POSTGRES_USER=postgres
POSTGRES_PASSWORD=secure_password_123
POSTGRES_DB=techpath_dev

# En docker-compose.yml
environment:
  - POSTGRES_USER=${POSTGRES_USER}
  - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
  - POSTGRES_DB=${POSTGRES_DB}

🎓 Glosario Rápido

Término Significado
Runner Servidor que ejecuta tus workflows
Job Conjunto de pasos que se ejecutan juntos
Step Comando individual dentro de un job
Action Bloque reutilizable de código (de Marketplace)
Artifact Archivo generado durante el workflow
Matrix Ejecutar job en múltiples configuraciones
Cache Guardar dependencias entre builds
Secret Variable cifrada (API keys, contraseñas)
Trigger Evento que inicia un workflow
YAML Lenguaje de configuración de workflows

📞 ¿Necesitas Ayuda?

Errores Comunes y Soluciones Rápidas

Error Solución Rápida
Tests fallan en Actions pero no local Verifica variables de entorno
Docker build falla Revisa .dockerignore
Workflow no se ejecuta Verifica sintaxis YAML
Secretos no funcionan Verifica permisos del workflow
Check verde no aparece Configura Branch Protection

Recursos de Emergencia

✨ Mensaje Final

Recordad:

"El check verde ✅ es vuestra reputación profesional. Si GitHub Actions lo aprueba, vuestro código es sólido."

No memoricéis comandos, entended los conceptos:

Vosotras sois arquitectas, no solo programadoras. 🚀