🚀 ¿Qué es CI/CD?
📖 Definición
CI/CD son las siglas de Integración Continua (Continuous Integration) y Despliegue Continuo (Continuous Deployment), una metodología de desarrollo que prueba y despliega automáticamente los cambios en el código. CI construye y prueba automáticamente el código cada vez que se fusiona, mientras que CD despliega automáticamente el código probado en entornos de producción. Esto permite detectar errores rápidamente y acortar los ciclos de despliegue.
🎯 Entender mediante analogías
Línea de automatización de fábrica
Desarrollo tradicional (Manual)
Desarrollador 1 → Escribir código
Desarrollador 2 → Escribir código
Desarrollador 3 → Escribir código
↓
Una semana después
↓
El líder fusiona manualmente
↓
¡Conflictos! 😱
↓
Pruebas manuales
↓
¡Errores encontrados! 😱
↓
Despliegue manual
↓
¡Despliegue fallido! 😱
CI/CD (Automatizado)
Desarrollador 1 → Push código → Prueba auto ✅ → Despliegue auto 🚀
Desarrollador 2 → Push código → Prueba auto ✅ → Despliegue auto 🚀
Desarrollador 3 → Push código → Prueba auto ❌ → ¡Alerta inmediata!
(Problemas detectados y corregidos inmediatamente)
Entrega de comida
Despliegue manual = Entrega directa
1. Preparar comida (Desarrollo)
2. Verificar empaque (Pruebas)
3. Confirmar dirección (Configuración)
4. Conducir y entregar (Despliegue)
5. Confirmación del cliente (Verificación)
→ Lento y propenso a errores
CI/CD = Sistema de entrega automática
1. Preparar comida (Desarrollo)
2. Empaque automático (Build automático)
3. Control de calidad automático (Pruebas automáticas)
4. Robot entrega automática (Despliegue automático)
5. Seguimiento en tiempo real (Monitoreo)
→ Rápido y preciso
⚙️ Cómo funciona
1. Pipeline CI/CD
Push de código
↓
┌─────────────────────────────┐
│ CI: Integración Continua │
│ │
│ 1. Checkout código │
│ 2. Instalar dependencias │
│ 3. Lint check │
│ 4. Build │
│ 5. Pruebas unitarias │
│ 6. Pruebas de integración │
│ │
│ ✅ Todo exitoso │
│ ❌ Cualquier fallo → Parar │
└─────────────────────────────┘
↓
┌─────────────────────────────┐
│ CD: Despliegue Continuo │
│ │
│ 1. Desplegar a staging │
│ 2. Pruebas E2E │
│ 3. Desplegar a producción │
│ 4. Health check │
│ 5. Enviar notificaciones │
└─────────────────────────────┘
↓
🎉 ¡Despliegue completo!
2. Enfoque tradicional vs CI/CD
Enfoque tradicional
┌─────────────────────────────────────┐
│ Lunes │
│ - Desarrollador A: Función 1 │
│ - Desarrollador B: Función 2 │
│ - Desarrollador C: Corregir error │
├─────────────────────────────────────┤
│ Martes-Jueves │
│ - Desarrollo continuo individual │
│ - Riesgo de conflictos aumenta 😰 │
├─────────────────────────────────────┤
│ Viernes (Día de integración) │
│ - Fusionar todo el código │
│ - Todo el día resolviendo conflictos 😱│
│ - Ejecutar pruebas → Muchos errores │
│ - Horas extra hasta el fin de semana 😭│
└─────────────────────────────────────┘
Enfoque CI/CD
┌─────────────────────────────────────┐
│ Cada día, cada commit │
│ │
│ Desarrollador A: Push código │
│ → Prueba auto ✅ │
│ → Despliegue auto 🚀 │
│ → En producción en 3 minutos │
│ │
│ Desarrollador B: Push código │
│ → Prueba auto ❌ │
│ → Alerta inmediata │
│ → Corregir inmediatamente (5 min) │
│ │
│ - Conflictos minimizados │
│ - Detección rápida de errores │
│ - Salir a tiempo 😊 │
└─────────────────────────────────────┘
3. Componentes CI/CD
┌──────────────────────────────────────┐
│ Control de versiones │
│ Git, GitHub, GitLab │
└───────────────┬──────────────────────┘
↓ Push
┌──────────────────────────────────────┐
│ Servidor CI │
│ GitHub Actions, Jenkins, CircleCI │
│ - Checkout código │
│ - Build automático │
│ - Pruebas automáticas │
└───────────────┬──────────────────────┘
↓ Pass
┌──────────────────────────────────────┐
│ Repositorio de artefactos │
│ Docker Hub, npm Registry │
└───────────────┬──────────────────────┘
↓ Deploy
┌──────────────────────────────────────┐
│ Despliegue │
│ AWS, Heroku, Vercel, Netlify │
└───────────────┬──────────────────────┘
↓ Monitor
┌──────────────────────────────────────┐
│ Monitoreo │
│ Sentry, DataDog, CloudWatch │
└──────────────────────────────────────┘
💡 Ejemplos prácticos
Ejemplo básico de GitHub Actions
# .github/workflows/ci.yml
# Pipeline CI/CD para proyecto Node.js
name: CI/CD Pipeline
# Trigger: al hacer push a main o crear PR
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
# Variables de entorno
env:
NODE_VERSION: '18'
# Definición de jobs
jobs:
# ========== Job 1: Pruebas ==========
test:
name: Pruebas y Lint
runs-on: ubuntu-latest # Ejecutar en Ubuntu
steps:
# 1. Checkout código
- name: Checkout código
uses: actions/checkout@v3
# 2. Configurar Node.js
- name: Configurar Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm' # Usar caché de npm
# 3. Instalar dependencias
- name: Instalar dependencias
run: npm ci # Más rápido y estable que npm install
# 4. Lint check
- name: Ejecutar lint
run: npm run lint
# 5. Pruebas unitarias
- name: Ejecutar pruebas
run: npm test
# 6. Cobertura de código
- name: Subir cobertura
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
# ========== Job 2: Build ==========
build:
name: Build
needs: test # Solo ejecutar si test tiene éxito
runs-on: ubuntu-latest
steps:
- name: Checkout código
uses: actions/checkout@v3
- name: Configurar Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Instalar dependencias
run: npm ci
# Build de producción
- name: Build de producción
run: npm run build
env:
NODE_ENV: production
# Subir artefactos de build
- name: Subir artefactos de build
uses: actions/upload-artifact@v3
with:
name: build-files
path: dist/
retention-days: 7
# ========== Job 3: Despliegue ==========
deploy:
name: Despliegue a producción
needs: build # Solo ejecutar si build tiene éxito
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' # Solo desplegar rama main
steps:
- name: Checkout código
uses: actions/checkout@v3
# Descargar artefactos de build
- name: Descargar artefactos de build
uses: actions/download-artifact@v3
with:
name: build-files
path: dist/
# Desplegar a Vercel
- name: Desplegar a Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
# Notificación Slack
- name: Notificación Slack
if: always() # Ejecutar independientemente de éxito/fallo
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: '¡Despliegue ${{ job.status }}!'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
Build y despliegue de Docker
# .github/workflows/docker.yml
# Build y despliegue de imagen Docker
name: Docker CI/CD
on:
push:
branches: [ main ]
tags:
- 'v*' # Ejecutar cuando se creen tags como v1.0.0
env:
DOCKER_IMAGE: myapp
DOCKER_REGISTRY: docker.io
jobs:
docker:
name: Build y Push de Docker
runs-on: ubuntu-latest
steps:
- name: Checkout código
uses: actions/checkout@v3
# Configurar metadatos de Docker
- name: Extraer metadatos de Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
# Configurar Docker Buildx (para build multiplataforma)
- name: Configurar Docker Buildx
uses: docker/setup-buildx-action@v2
# Iniciar sesión en Docker Hub
- name: Iniciar sesión en Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
# Construir y hacer push de imagen Docker
- name: Construir y hacer push de imagen Docker
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha # Usar caché de GitHub Actions
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64 # Multiplataforma
# Despliegue (acceder al servidor por SSH y reiniciar contenedor)
- name: Despliegue al servidor
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
docker pull ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:latest
docker stop myapp || true
docker rm myapp || true
docker run -d \
--name myapp \
-p 3000:3000 \
--restart unless-stopped \
${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:latest
docker system prune -af
Validación automática de Pull Request
# .github/workflows/pr.yml
# Validar automáticamente al crear PR
name: Pull Request Check
on:
pull_request:
types: [ opened, synchronize, reopened ]
jobs:
# ========== Verificación de calidad de código ==========
lint:
name: Lint y Format Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configurar Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Instalar dependencias
run: npm ci
# ESLint
- name: Ejecutar ESLint
run: npm run lint
# Prettier
- name: Verificar Prettier
run: npm run format:check
# Verificación de tipos TypeScript
- name: Verificar tipos TypeScript
run: npm run type-check
# ========== Pruebas ==========
test:
name: Pruebas
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20] # Probar en múltiples versiones de Node
steps:
- uses: actions/checkout@v3
- name: Configurar Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Instalar dependencias
run: npm ci
- name: Ejecutar pruebas
run: npm test -- --coverage
- name: Subir resultados de pruebas
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results-${{ matrix.node-version }}
path: coverage/
# ========== Verificación de seguridad ==========
security:
name: Verificación de seguridad
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configurar Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
# npm audit
- name: Auditoría de seguridad npm
run: npm audit --audit-level=moderate
# Escaneo de seguridad Snyk
- name: Escaneo de seguridad Snyk
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
# ========== Pruebas de rendimiento ==========
performance:
name: Pruebas de rendimiento
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configurar Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Instalar dependencias
run: npm ci
- name: Build
run: npm run build
# Lighthouse CI
- name: Lighthouse CI
uses: treosh/lighthouse-ci-action@v9
with:
urls: |
http://localhost:3000
uploadArtifacts: true
# ========== Comentario en PR ==========
comment:
name: Comentario en PR
needs: [lint, test, security]
runs-on: ubuntu-latest
if: always()
steps:
- name: Crear comentario en PR
uses: actions/github-script@v6
with:
script: |
const statuses = {
lint: '${{ needs.lint.result }}',
test: '${{ needs.test.result }}',
security: '${{ needs.security.result }}'
};
let message = '## 🤖 Resultado de verificación CI\n\n';
for (const [job, status] of Object.entries(statuses)) {
const emoji = status === 'success' ? '✅' : '❌';
message += `${emoji} **${job}**: ${status}\n`;
}
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: message
});
Tareas programadas
# .github/workflows/scheduled.yml
# Tareas ejecutadas periódicamente
name: Scheduled Tasks
on:
schedule:
# Ejecutar diariamente a las 9 AM (00:00 UTC)
- cron: '0 0 * * *'
workflow_dispatch: # También permitir ejecución manual
jobs:
# ========== Verificación de actualizaciones de dependencias ==========
dependency-check:
name: Verificar actualizaciones de dependencias
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configurar Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
# Verificar npm outdated
- name: Verificar paquetes desactualizados
run: npm outdated || true
# Alternativa a Dependabot
- name: Verificar paquetes actualizables
run: |
npx npm-check-updates
# ========== Backup ==========
backup:
name: Backup de base de datos
runs-on: ubuntu-latest
steps:
- name: Hacer backup de base de datos
run: |
# En la práctica, ejecutar script de backup de base de datos
echo "Haciendo backup de base de datos..."
# Subir a S3
- name: Subir backup a S3
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Subir a S3
run: |
aws s3 cp backup.sql s3://my-backups/$(date +%Y%m%d).sql
# ========== Monitoreo de rendimiento ==========
performance-monitor:
name: Monitoreo de rendimiento
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Ejecutar Lighthouse
uses: treosh/lighthouse-ci-action@v9
with:
urls: |
https://myapp.com
uploadArtifacts: true
# Notificación Slack si hay degradación de rendimiento
- name: Notificación de degradación de rendimiento
if: failure()
uses: 8398a7/action-slack@v3
with:
status: failure
text: '⚠️ ¡El rendimiento del sitio web ha disminuido!'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
Despliegue multi-entorno
# .github/workflows/multi-env.yml
# Despliegue por entorno: desarrollo, staging, producción
name: Multi-Environment Deployment
on:
push:
branches:
- develop # Entorno de desarrollo
- staging # Entorno de staging
- main # Entorno de producción
jobs:
deploy:
name: Despliegue
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configurar Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Instalar dependencias
run: npm ci
# Build según entorno
- name: Build
run: npm run build
env:
NODE_ENV: ${{
github.ref == 'refs/heads/main' && 'production' ||
github.ref == 'refs/heads/staging' && 'staging' ||
'development'
}}
# Despliegue a entorno de desarrollo
- name: Despliegue a desarrollo
if: github.ref == 'refs/heads/develop'
run: |
echo "Desplegando a entorno de desarrollo..."
# Comando de despliegue al servidor de desarrollo
env:
API_URL: https://api-dev.myapp.com
# Despliegue a entorno de staging
- name: Despliegue a staging
if: github.ref == 'refs/heads/staging'
run: |
echo "Desplegando a entorno de staging..."
# Comando de despliegue al servidor de staging
env:
API_URL: https://api-staging.myapp.com
# Despliegue a entorno de producción
- name: Despliegue a producción
if: github.ref == 'refs/heads/main'
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: task-definition.json
service: myapp-service
cluster: production-cluster
wait-for-service-stability: true
env:
API_URL: https://api.myapp.com
# Health Check después del despliegue
- name: Health Check
run: |
sleep 30 # Esperar 30 segundos
URL=${{
github.ref == 'refs/heads/main' && 'https://myapp.com' ||
github.ref == 'refs/heads/staging' && 'https://staging.myapp.com' ||
'https://dev.myapp.com'
}}
STATUS=$(curl -s -o /dev/null -w "%{http_code}" $URL/health)
if [ $STATUS -eq 200 ]; then
echo "✅ Health Check exitoso"
else
echo "❌ Health Check fallido: $STATUS"
exit 1
fi
# Notificación de éxito del despliegue
- name: Notificación de despliegue
if: success()
uses: 8398a7/action-slack@v3
with:
status: success
text: |
🚀 ¡Despliegue completado!
Entorno: ${{ github.ref_name }}
Commit: ${{ github.sha }}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
🤔 Preguntas frecuentes
P1. ¿Cuáles son los beneficios de introducir CI/CD?
R:
✅ Beneficios principales:
1. Detección rápida de errores
Tradicional: Descubiertos después de una semana
CI/CD: Descubiertos en 5 minutos
→ 90% de reducción en costos de corrección
2. Tiempo de despliegue reducido
Tradicional: 2 horas de despliegue manual
CI/CD: 5 minutos de despliegue automático
→ Múltiples despliegues por día posibles
3. Mejor calidad de código
- Lint y pruebas automáticas
- Revisión de código automatizada
- Menos errores
4. Mayor productividad del desarrollador
- Eliminar tareas manuales
- Automatizar trabajo repetitivo
- Enfocarse solo en desarrollo
5. Mayor estabilidad
- Pruebas automáticas
- Rollback automático
- Proceso de despliegue consistente
6. Mejor colaboración
- Minimizar conflictos de código
- Retroalimentación rápida
- Proceso transparente
Caso práctico:
Empresa A: Antes de CI/CD
- Despliegue: 1 vez al mes
- Tiempo de despliegue: 4 horas
- Tasa de errores: 30%
- Tiempo de detección de errores: Promedio 3 días
Empresa A: Después de CI/CD
- Despliegue: 10 veces al día
- Tiempo de despliegue: 5 minutos
- Tasa de errores: 5%
- Tiempo de detección de errores: Promedio 10 minutos
P2. ¿GitHub Actions vs Jenkins vs CircleCI?
R:
// ========== GitHub Actions ==========
Ventajas:
- Integración perfecta con GitHub
- Gratis (repos públicos, 2000 min/mes gratis)
- Configuración simple (archivos YAML)
- Diversas actions en Marketplace
Desventajas:
- Dependiente de GitHub
- Workflows complejos limitados
Mejor para:
- Usar GitHub
- CI/CD simple
- Necesitar inicio rápido
Ejemplo:
name: CI
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm test
// ========== Jenkins ==========
Ventajas:
- El más antiguo y maduro
- Ecosistema de plugins extenso
- Personalización completa
- Auto-hospedado (sin costo)
Desventajas:
- Configuración compleja
- UI anticuada
- Requiere mantenimiento
- Curva de aprendizaje pronunciada
Mejor para:
- Entorno on-premises
- Pipelines complejos
- Integración con sistemas legacy
// ========== CircleCI ==========
Ventajas:
- Velocidad de build rápida
- Excelente soporte Docker
- Paralelización fuerte
- UI intuitiva
Desventajas:
- De pago (tier gratuito limitado)
- Solo soporte GitHub/GitLab
Mejor para:
- Rendimiento crítico
- Uso intensivo de Docker
- Necesidad de pruebas paralelas
// ========== Tabla comparativa ==========
Característica | GitHub Actions | Jenkins | CircleCI
---------------|----------------|---------|----------
Precio | Gratis(limitado)| Gratis | Pago
Configuración | Fácil | Difícil | Media
Rendimiento | Medio | Alto | Alto
Integración | GitHub perfecto| Genérico| Genérico
Mantenimiento | No necesario | Necesario| No necesario
Docker | Medio | Bueno | Muy bueno
Recomendación:
- Equipo pequeño, usando GitHub → GitHub Actions
- Equipo grande, requisitos complejos → Jenkins
- Rendimiento crítico, con presupuesto → CircleCI
P3. ¿Cómo gestionar claves secretas (Secrets)?
R:
# ========== Configuración de GitHub Secrets ==========
# Repository → Settings → Secrets and variables → Actions
# Uso
- name: Despliegue
env:
API_KEY: ${{ secrets.API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: npm run deploy
# ========== Secrets específicos por entorno ==========
# Repository → Settings → Environments
# Entorno de producción
environment: production
env:
API_URL: ${{ secrets.PROD_API_URL }}
# Entorno de staging
environment: staging
env:
API_URL: ${{ secrets.STAGING_API_URL }}
# ========== Mejores prácticas de seguridad ==========
# ✅ Buen ejemplo
- name: Despliegue
env:
# Obtener de Secrets
AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_KEY }}
run: |
# Usar variables de entorno
aws s3 sync dist/ s3://my-bucket
# ❌ Mal ejemplo
- name: Despliegue
run: |
# Hardcoded en código (¡absolutamente prohibido!)
export AWS_ACCESS_KEY="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
# ========== Crear archivo .env ==========
- name: Crear archivo .env
run: |
cat << EOF > .env
API_URL=${{ secrets.API_URL }}
DATABASE_URL=${{ secrets.DATABASE_URL }}
JWT_SECRET=${{ secrets.JWT_SECRET }}
EOF
# ========== Docker Secrets ==========
- name: Docker Build
env:
DOCKER_BUILDKIT: 1
run: |
docker build \
--secret id=api_key,env=API_KEY \
--secret id=db_url,env=DATABASE_URL \
-t myapp .
# Dockerfile
# syntax=docker/dockerfile:1
FROM node:18
RUN --mount=type=secret,id=api_key \
API_KEY=$(cat /run/secrets/api_key) npm install
# ========== Usar Vault (avanzado) ==========
- name: Obtener secrets de HashiCorp Vault
uses: hashicorp/vault-action@v2
with:
url: https://vault.mycompany.com
token: ${{ secrets.VAULT_TOKEN }}
secrets: |
secret/data/production api_key | API_KEY ;
secret/data/production db_url | DATABASE_URL
P4. ¿Qué sucede cuando las pruebas fallan?
R:
# ========== Comportamiento predeterminado: detener pipeline ==========
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Ejecutar pruebas
run: npm test
# Error en pruebas → se detiene aquí
# Los pasos siguientes no se ejecutan
deploy:
needs: test # Solo se ejecuta si test tiene éxito
runs-on: ubuntu-latest
steps:
- name: Despliegue
run: npm run deploy
# Este paso no se ejecuta si test falla ✅
# ========== Continuar aunque falle ==========
- name: Ejecutar pruebas
run: npm test
continue-on-error: true # Continuar aunque falle
# ⚠️ No recomendado
# ========== Ejecución condicional ==========
- name: Notificar en caso de fallo
if: failure() # Solo cuando falla el paso anterior
uses: 8398a7/action-slack@v3
with:
status: failure
text: '❌ ¡Pruebas fallidas!'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
- name: Desplegar en caso de éxito
if: success() # Solo cuando todos los pasos tienen éxito
run: npm run deploy
- name: Ejecutar siempre
if: always() # Ejecutar independientemente de éxito/fallo
run: npm run cleanup
# ========== Reintentos ==========
- name: Pruebas inestables (reintentar)
uses: nick-invision/retry@v2
with:
timeout_minutes: 10
max_attempts: 3
command: npm run test:e2e
# ========== Ejemplo de manejo de errores ==========
- name: Pruebas
id: test
run: npm test
- name: Crear issue en caso de fallo de pruebas
if: failure() && steps.test.outcome == 'failure'
uses: actions/github-script@v6
with:
script: |
github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: '❌ Pruebas fallidas',
body: 'Las pruebas han fallado. Se requiere revisión.',
labels: ['bug', 'ci']
});
# ========== Rollback ==========
- name: Despliegue
id: deploy
run: npm run deploy
- name: Rollback en caso de fallo del despliegue
if: failure() && steps.deploy.outcome == 'failure'
run: |
echo "¡Despliegue fallido! Haciendo rollback a la versión anterior..."
npm run rollback
P5. ¿Cómo reducir costos?
R:
# ========== Usar caché ==========
- name: Configurar Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm' # Caché de npm (50% más rápido en el build)
- name: Cachear dependencias
uses: actions/cache@v3
with:
path: |
~/.npm
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
# ========== Ejecución condicional ==========
on:
push:
branches: [ main ]
paths: # Solo ejecutar cuando cambien archivos específicos
- 'src/**'
- 'package.json'
# No ejecutar cuando solo cambie README.md → ahorro de costos
# ========== Limitar ejecución concurrente ==========
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true # Cancelar ejecución anterior
# → Prevenir duplicación de ejecuciones innecesarias
# ========== Self-hosted runner ==========
# Usar servidor propio (gratis)
jobs:
build:
runs-on: self-hosted # No consume minutos de GitHub Actions
steps:
- run: npm run build
# ========== Ejecutar solo jobs cortos ==========
- name: Verificar archivos modificados
id: changes
uses: dorny/paths-filter@v2
with:
filters: |
frontend:
- 'frontend/**'
backend:
- 'backend/**'
- name: Pruebas de frontend
if: steps.changes.outputs.frontend == 'true'
run: npm run test:frontend
# Solo ejecutar cuando cambió frontend → ahorro de tiempo
- name: Pruebas de backend
if: steps.changes.outputs.backend == 'true'
run: npm run test:backend
# ========== Ejemplo de costos ==========
# GitHub Actions Free Tier:
# - Repos públicos: Ilimitado
# - Repos privados: 2000 minutos/mes
# Antes de optimización:
# - Pruebas completas en cada commit (20 min)
# - 10x push por día
# - Uso mensual: 10 × 20 × 30 = 6000 min
# - Costo adicional: (6000 - 2000) × $0.008 = $32
# Después de optimización:
# - 10 min ahorrados con caché
# - Solo probar partes modificadas
# - Tiempo promedio de build 5 min
# - Uso mensual: 10 × 5 × 30 = 1500 min
# - Costo: $0 (dentro del Free Tier)
# → ¡$32/mes ahorrados!
🎓 Próximos pasos
Una vez que entienda CI/CD, aprenda estos temas:
- ¿Qué es Git? (Documento en preparación) - Fundamentos de control de versiones
- ¿Qué es Docker? (Documento en preparación) - Despliegue en contenedores
- ¿Qué es TDD? (Documento en preparación) - Desarrollo dirigido por pruebas
Pruébelo
# ========== 1. Comenzar con GitHub Actions ==========
# 1) Crear archivo de workflow en el repositorio
mkdir -p .github/workflows
cat > .github/workflows/ci.yml << 'EOF'
name: CI
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configurar Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm test
EOF
# 2) Commit y push
git add .github/workflows/ci.yml
git commit -m "Add CI workflow"
git push
# 3) Ver pestaña Actions en GitHub
# → ¡Ver el workflow ejecutándose automáticamente!
# ========== 2. Probar localmente (act) ==========
# Usar act para probar GitHub Actions localmente
# Instalar act (macOS)
brew install act
# Ejecutar workflow
act push
# Ejecutar solo job específico
act -j test
# ========== 3. Ejemplos de automatización de despliegue ==========
# Despliegue Vercel
npm install -g vercel
vercel login
vercel --prod
# Despliegue Netlify
npm install -g netlify-cli
netlify login
netlify deploy --prod
# Despliegue Heroku
heroku login
git push heroku main
🎬 Conclusión
CI/CD es una herramienta esencial en el desarrollo moderno:
- CI: Probar e integrar automáticamente los cambios de código
- CD: Desplegar automáticamente el código probado
- Beneficios: Retroalimentación rápida, mayor estabilidad, mayor productividad
- Herramientas: GitHub Actions, Jenkins, CircleCI, etc.
¡Enfóquese en el desarrollo y despliegue más rápido con automatización! 🚀