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
# 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:
# 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
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
# ===== 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;"]
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
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
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 }}
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 }}
OPENAI_API_KEYsk-proj-tu-api-key-aquijobs:
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
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 | 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 |
Solución:
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # ← Añade permisos
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
Causa común: Tests fallando
Solución:
# Ejecutar tests localmente npm test # Ver qué test falla específicamente npm test -- --verbose
Causa común: Cache corrupto
Solución en workflow:
- name: Clear cache and install
run: |
npm ci --cache .npm --prefer-offline
Causa común: Runner sin disponibilidad
Solución:
runs-on: ubuntu-latest (más rápido que versiones específicas)Causa: Branch protection no configurada
Solución:
# 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
# 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
# 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 ✅
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm' # ← Ahorra 2-3 minutos por build
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
Settings → Branches → Branch protection rules
# ❌ 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
# 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}
| 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 |
| 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 |
docker-compose logs -fRecordad:
"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. 🚀