Módulo 4: Deployment Automation

Proyecto: Workflow de Deployment Completo

Proyecto: Workflow de Deployment Completo

Descripción del proyecto

Este proyecto integra todo lo aprendido en el Módulo 4: changelog automático, readiness validation, flujo staging → producción con approval gates. Vas a construir un workflow de deployment end-to-end funcional, desde el merge a main hasta el deploy a producción, con notificaciones al equipo en cada etapa.

No es un ejercicio teórico. El entregable es un repositorio con:

  1. Pipeline funcional (probado al menos en staging mock)
  2. Documentación operacional para el equipo
  3. Análisis de costos y tiempos
  4. Plan de extensión para tu caso real

Al completar, vas a tener un template de deployment que puedes adaptar a cualquier proyecto profesional que toques.


Objetivo del Proyecto

Construir un workflow de GitHub Actions que ejecute el ciclo completo de deployment con Claude Code asistiendo en cada paso crítico.

Al completar:

  • ✅ Pipeline ejecutable desde merge a main hasta producción
  • ✅ Changelog generado automáticamente con cada release
  • ✅ Readiness validation antes de cada deploy
  • ✅ Deploy automático a staging con smoke tests
  • ✅ Approval gate humano antes de producción
  • ✅ Análisis pre-promoción que informa al reviewer
  • ✅ Notificaciones post-deploy al equipo
  • ✅ Documentación operacional completa

Especificaciones Técnicas

Estructura del proyecto

deployment-pipeline-project/
├── .github/
│   └── workflows/
│       ├── release-changelog.yml        # cápsula 02
│       ├── readiness-validation.yml     # cápsula 03
│       └── staged-deployment.yml        # cápsula 04 + integración
├── scripts/
│   ├── generate_changelog.py            # cápsula 02
│   ├── validate_readiness.py            # cápsula 03
│   ├── validate_staging.py              # cápsula 04
│   ├── deploy.sh                         # mock deploy script
│   ├── smoke_tests.sh                    # mock smoke tests
│   ├── post_deploy_notification.py      # nuevo
│   └── platform_helpers.py               # helpers comunes
├── docs/
│   ├── DEPLOYMENT.md                    # cómo opera el pipeline
│   ├── RUNBOOK.md                       # qué hacer si X falla
│   └── METRICS.md                       # análisis de costos
├── CLAUDE.md
├── CHANGELOG.md
└── README.md

Setup inicial

mkdir deployment-pipeline-project && cd deployment-pipeline-project
git init
git checkout -b main

# Configurar GitHub
gh repo create my-deployment-project --public

# Configurar environments en GitHub Settings:
# - staging (sin required reviewers)
# - production (con required reviewers + branch protection)

El Workflow Integrado

# .github/workflows/staged-deployment.yml
name: Staged Deployment

on:
  push:
    branches: [main]

permissions:
  contents: read
  pull-requests: write
  deployments: write

concurrency:
  group: deploy-${{ github.ref }}
  cancel-in-progress: false

env:
  PYTHON_VERSION: '3.11'

jobs:
  # ============================================
  # JOB 1: Readiness Validation
  # ============================================
  validate:
    runs-on: ubuntu-latest
    outputs:
      readiness_status: ${{ steps.validate.outputs.status }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}
          cache: 'pip'
      
      - run: pip install anthropic requests
      
      - name: Run readiness validation
        id: validate
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_REPOSITORY: ${{ github.repository }}
        run: |
          python scripts/validate_readiness.py
          if [ $? -eq 0 ]; then
            echo "status=ready" >> $GITHUB_OUTPUT
          else
            echo "status=blocked" >> $GITHUB_OUTPUT
            exit 1
          fi
      
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: readiness-report
          path: readiness_report.json

  # ============================================
  # JOB 2: Generate Changelog
  # ============================================
  generate-changelog:
    needs: validate
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}
          cache: 'pip'
      
      - run: pip install anthropic requests
      
      - name: Generate changelog from last release
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_REPOSITORY: ${{ github.repository }}
          NEW_TAG: ${{ github.sha }}
        run: python scripts/generate_changelog.py
      
      - uses: actions/upload-artifact@v4
        with:
          name: changelog
          path: CHANGELOG_*.md

  # ============================================
  # JOB 3: Deploy a Staging
  # ============================================
  deploy-staging:
    needs: [validate, generate-changelog]
    runs-on: ubuntu-latest
    environment: staging
    outputs:
      deploy_url: ${{ steps.deploy.outputs.url }}
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to staging
        id: deploy
        env:
          DATABASE_URL: ${{ secrets.STAGING_DATABASE_URL }}
        run: |
          ./scripts/deploy.sh staging
          echo "url=https://staging.example.com" >> $GITHUB_OUTPUT
      
      - name: Smoke tests
        run: ./scripts/smoke_tests.sh https://staging.example.com
      
      - name: Validate staging metrics with Claude
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_REPOSITORY: ${{ github.repository }}
        run: python scripts/validate_staging.py

  # ============================================
  # JOB 4: Deploy a Producción (con approval gate)
  # ============================================
  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: production  # ← required reviewers configurados
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to production
        env:
          DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
        run: ./scripts/deploy.sh production
      
      - name: Smoke tests on production
        run: ./scripts/smoke_tests.sh https://app.example.com
      
      - name: Notify team
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
        run: python scripts/post_deploy_notification.py

Script Mock: deploy.sh

#!/bin/bash
# scripts/deploy.sh — mock deploy para testing del pipeline
set -euo pipefail

ENV="${1:?'Usage: deploy.sh <staging|production>'}"

echo "[deploy.sh] Deploying to $ENV..."
echo "[deploy.sh] Pulling latest image..."
sleep 2
echo "[deploy.sh] Running migrations..."
sleep 1
echo "[deploy.sh] Updating containers..."
sleep 2
echo "[deploy.sh] Health check..."
sleep 1
echo "[deploy.sh] Deploy to $ENV complete."

# En producción real, este script invocaría:
# kubectl rollout restart deployment/app -n $ENV
# o
# terraform apply -auto-approve -var "env=$ENV"
# o el mecanismo específico de tu plataforma

Haz el script ejecutable: chmod +x scripts/deploy.sh


Script Mock: smoke_tests.sh

#!/bin/bash
# scripts/smoke_tests.sh — mock smoke tests
set -euo pipefail

URL="${1:?'Usage: smoke_tests.sh <url>'}"

echo "[smoke] Testing $URL..."

# En producción real, ejecutar requests reales:
# curl -f $URL/health || exit 1
# curl -f $URL/api/status || exit 1

# Mock: simular tests
PASSED=0
FAILED=0
SKIPPED=0

for test in health api status auth payments; do
    sleep 0.5
    if [ $((RANDOM % 10)) -lt 9 ]; then
        echo "  ✓ $test"
        PASSED=$((PASSED + 1))
    else
        echo "  ✗ $test (FAILED)"
        FAILED=$((FAILED + 1))
    fi
done

echo "{\"passed\": $PASSED, \"failed\": $FAILED, \"skipped\": $SKIPPED}" > smoke_results.json

if [ "$FAILED" -gt 0 ]; then
    echo "[smoke] $FAILED tests failed"
    exit 1
fi

echo "[smoke] All $PASSED tests passed"

Script Nuevo: post_deploy_notification.py

"""scripts/post_deploy_notification.py

Notifica al equipo del deploy exitoso con resumen útil.
"""
import json
import os
import sys
from pathlib import Path
import requests
from anthropic import Anthropic


def get_changelog() -> str:
    """Leer el último changelog generado."""
    changelogs = sorted(Path(".").glob("CHANGELOG_*.md"))
    if changelogs:
        return changelogs[-1].read_text()
    return "No changelog disponible"


def generate_notification_summary(changelog: str) -> str:
    """Generar resumen ejecutivo para Slack/email."""
    prompt = f"""Genera un mensaje breve (máximo 150 palabras) para notificar al
equipo de un deploy a producción exitoso. El mensaje debe:

1. Empezar con "🚀 Deployed to production"
2. Resumir los cambios principales del changelog en 2-3 bullets
3. Mencionar si hay breaking changes
4. Cerrar con "Monitoring en curso. Revisar [link a dashboard]."

CHANGELOG:
{changelog[:3000]}

Output: solo el mensaje, sin texto adicional.
"""
    
    client = Anthropic()
    response = client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=500,
        messages=[{"role": "user", "content": prompt}],
    )
    
    return response.content[0].text.strip()


def send_slack_notification(message: str, webhook: str):
    """Enviar a Slack via webhook."""
    payload = {
        "text": message,
        "username": "Deploy Bot",
        "icon_emoji": ":rocket:",
    }
    
    r = requests.post(webhook, json=payload, timeout=10)
    if r.status_code != 200:
        print(f"WARNING: Slack notification failed: {r.status_code}", file=sys.stderr)


def main() -> int:
    changelog = get_changelog()
    summary = generate_notification_summary(changelog)
    
    print("--- Notification preview ---")
    print(summary)
    print("---")
    
    webhook = os.environ.get("SLACK_WEBHOOK")
    if webhook:
        send_slack_notification(summary, webhook)
        print("Notificación enviada a Slack")
    else:
        print("SLACK_WEBHOOK no configurado, skipping notification")
    
    return 0


if __name__ == "__main__":
    sys.exit(main())

Documentación: docs/DEPLOYMENT.md

# Deployment Pipeline

Este documento describe el flujo de deployment del proyecto.

## Cuándo se dispara

- **Trigger:** push a `main` (típicamente vía merge de PR)
- **Branches:** solo `main` deploya. Otros branches solo corren CI.

## Stages

### 1. Validate (~30s)
- Tests pasan
- Linting + type checking
- Readiness check (env vars, migrations, deps)
- Análisis con Claude Code para detectar issues

### 2. Generate Changelog (~30s)
- Lista commits desde el último deploy
- Genera changelog estructurado
- Sube como artifact

### 3. Deploy Staging (~3-5 min)
- Deploy automático
- Smoke tests
- Validación con Claude Code de métricas

### 4. Approval Gate (variable)
- Reviewer humano evalúa el deploy
- Tiene info pre-cargada (análisis del Job 3)
- Click "Approve" o investigar

### 5. Deploy Production (~3-5 min)
- Deploy
- Smoke tests
- Notificación al equipo

## Tiempo total esperado

- Sin issues: 8-15 minutos
- Con investigación humana: variable

## Quién aprueba

Reviewers configurados en environment `production`:
- @tech-lead
- @sre-lead

Cualquiera puede aprobar.

## Cuándo NO deployar

- Fines de semana (sin SRE on-call)
- Antes de eventos críticos del negocio
- Cuando staging muestra issues persistentes

Documentación: docs/RUNBOOK.md

# Runbook: Qué Hacer Cuando X Falla

## Validate Failure

**Síntomas:** Job `validate` falla con readiness check error.

**Acción:**
1. Descargar artifact `readiness-report.json`
2. Identificar el check que falló
3. Si es env var faltante en producción: agregarla en Settings → Environments → production
4. Si es migration: verificar que esté aplicada y luego re-disparar

## Staging Deploy Failure

**Síntomas:** Job `deploy-staging` falla.

**Acción:**
1. Ver logs del job
2. Si error de infraestructura: re-run el job
3. Si error de aplicación: revertir el commit en `main`, investigar offline
4. NO promocionar a producción mientras staging falle

## Smoke Tests Falla en Staging

**Síntomas:** Smoke tests reportan failures.

**Acción:**
1. Verificar URL/endpoints accesibles
2. Si endpoint específico falla: review del cambio que afecta ese endpoint
3. Si TODO falla: probable issue de infraestructura
4. NO aprobar producción

## Approval Gate "Stuck"

**Síntomas:** Job production lleva mucho esperando approval.

**Acción:**
1. Verificar que los reviewers están notificados
2. Si urgente: contactar a un reviewer directamente
3. Si nadie disponible: revertir el merge y reintentar

## Production Deploy Failure

**Síntomas:** Producción falla post-deploy.

**Acción:**
1. **Inmediato:** disparar rollback (manual o automático según setup)
2. Verificar métricas en monitoring dashboard
3. Postmortem
4. NO re-deployar el mismo cambio sin investigación

Análisis: docs/METRICS.md

# Pipeline Metrics

Datos del pipeline después de [N runs].

## Tiempos promedio

| Stage | Promedio | P95 |
|-------|----------|-----|
| Validate | 35s | 45s |
| Generate changelog | 25s | 40s |
| Deploy staging | 4 min | 6 min |
| Smoke tests | 90s | 2 min |
| Approval wait | 12 min | 1 hour |
| Deploy production | 4 min | 6 min |
| **Total (sin wait)** | **11 min** | **17 min** |

## Costos estimados

Por run:
- Anthropic API: ~$0.05 (varios calls a Claude)
- GitHub Actions runner: ~$0.04 (5 min × $0.008)
- **Total: ~$0.09 por run**

Mensual (asumiendo 50 deploys/mes):
- ~$4.50/mes en CI/CD

## Frequency

- Deploys/semana promedio: 12
- Pasos manuales antes vs ahora:
  - Antes: 8 (changelog manual, readiness manual, comunicación, etc.)
  - Ahora: 1 (click de approval)

## Time saved

- Por deploy: ~25 min de tiempo humano
- Mensual: ~25 min × 50 deploys = ~20 hours

ROI: ~$2K/mes ahorrados (con cost de developer ~$100/h)

Entregables del Proyecto

  1. Repositorio funcional con:
    • Workflow ejecutándose end-to-end (al menos staging)
    • Scripts (Python + bash)
    • Configuración de environments
  2. Documentación:
    • docs/DEPLOYMENT.md
    • docs/RUNBOOK.md
    • docs/METRICS.md
  3. Demostración:
    • Screenshot/video del pipeline corriendo
    • Pipeline completo (todos los jobs verdes)
    • Approval gate funcionando
  4. Plan de extensión documentado:
    • Cómo aplicarlo a tu proyecto real
    • Qué scripts mock reemplazar con reales
    • Qué adaptaciones específicas necesita

Rúbrica de Evaluación (100 puntos)

Pipeline Funcional (40 pts)

  • ✅ (10 pts) Validate corre y bloquea correctamente
  • ✅ (5 pts) Changelog se genera automáticamente
  • ✅ (10 pts) Deploy a staging es completamente automático
  • ✅ (10 pts) Approval gate antes de producción funciona
  • ✅ (5 pts) Notification post-deploy se envía

Calidad del Código (20 pts)

  • ✅ (8 pts) Scripts Python con manejo de errores
  • ✅ (4 pts) Exit codes apropiados
  • ✅ (4 pts) Logs claros para debugging
  • ✅ (4 pts) Estructura limpia (config + main + helpers)

Documentación (25 pts)

  • ✅ (10 pts) DEPLOYMENT.md explica el flujo completo
  • ✅ (10 pts) RUNBOOK.md cubre los failure modes principales
  • ✅ (5 pts) METRICS.md tiene análisis cuantitativo

Setup Profesional (10 pts)

  • ✅ (5 pts) Environments configurados correctamente (staging vs production)
  • ✅ (5 pts) Secrets separados por environment

Reflexión y Plan (5 pts)

  • ✅ (3 pts) Plan de adaptación a proyecto real
  • ✅ (2 pts) Lecciones aprendidas documentadas

Extra Credit (+15 pts)

  • ✅ (+5 pts) Implementar canary deploy adicional
  • ✅ (+3 pts) Integración con un sistema de observability real (Datadog, Prometheus, etc.)
  • ✅ (+3 pts) Test del rollback (simular failure post-deploy)
  • ✅ (+2 pts) Multi-región deploy (us-east + eu-west, etc.)
  • ✅ (+2 pts) Slack bot interactivo (reaccionar emoji para aprobar)

Errores Comunes en el Proyecto

1. No probar el flujo completo

Síntoma: Implementaste cada job pero nunca corriste el pipeline end-to-end.

Por qué pasa: Los jobs separados parecen funcionar.

Cómo corregir: Haz un PR de prueba, mergéalo, observa el pipeline correr completo. Cualquier inconsistencia sale acá.

2. Approval gate sin reviewer asignado

Síntoma: El job deploy-production se queda "esperando aprobación" pero nadie configurado.

Por qué pasa: Olvidaste agregar required reviewers al environment production.

Cómo corregir: Settings → Environments → production → Required reviewers. Agregar a tú mismo para testing.

3. Scripts no ejecutables

Síntoma: ./scripts/deploy.sh: Permission denied.

Por qué pasa: Los scripts bash necesitan flag +x.

Cómo corregir:

chmod +x scripts/*.sh
git update-index --chmod=+x scripts/*.sh
git commit -am "fix: make scripts executable"

4. Documentación superficial

Síntoma: RUNBOOK.md tiene "qué hacer si falla: investigar".

Por qué pasa: Documentación escrita rápido sin pensar casos reales.

Cómo corregir: Para cada failure mode, escribe pasos concretos: comando exacto, dónde mirar logs, a quién contactar. El runbook debe ser usable bajo presión.

5. Ignorar costos en METRICS

Síntoma: El proyecto está completo pero no analizaste cuánto cuesta.

Por qué pasa: Costos parecen secundarios cuando algo funciona.

Cómo corregir: Calcular costo por run, mensual, ROI vs proceso manual. Es lo que justifica el pipeline ante el equipo.


Reflexión Final

Cuando termines este proyecto, tienes un template profesional de deployment automation que:

  1. Aplica los 6 módulos del path (CI/CD, code review, SDK, scripts, secrets, observability)
  2. Es portfolio-worthy — demuestra senior-level thinking sobre deployment
  3. Es transferible — el patrón aplica a cualquier proyecto

Lo más importante: aprendiste a balancear automatización con criterio humano. La automatización elimina trabajo repetitivo. El humano agrega contexto y autoridad. Ese balance es la diferencia entre un pipeline que el equipo confía y uno que el equipo desactiva.


Conexión con el Módulo 5

El siguiente módulo agrega la resiliencia post-deploy: ¿qué pasa cuando producción falla? Rollback automático, security scanning como gate adicional, diagnóstico inteligente de incidents. Tu pipeline actual deploya correctamente; el Módulo 5 lo hace resistente a fallos.


Recursos para el Proyecto

  1. GitHub Actions Workflow Examples — Templates oficiales
  2. The DevOps Handbook — Referencia clásica
  3. Kubernetes Deployment Strategies — Si usas K8s
  4. Slack Incoming Webhooks — Para notificaciones
  5. Datadog GitHub Integration — Observability moderna
  6. Anthropic API Best Practices — Aplicables a deployment