Tutorial de GitHub Actions: Automatiza CI/CD para Tus Proyectos en 2026
Tutorial de GitHub Actions: Automatiza CI/CD para Tus Proyectos en 2026
Tutorial paso a paso de GitHub Actions. Aprende a automatizar pruebas, builds y despliegues con ejemplos prácticos para Node.js y Python.
¿Qué es CI/CD y Por Qué Importa?
Imagina esto: terminas una funcionalidad, haces push al repositorio, y en los próximos cinco minutos ya sabes si tu código rompe los tests, si el estilo de código es correcto y si la app se despliega correctamente en staging. Sin tocar un solo comando manualmente.
Eso es CI/CD:
- Integración Continua (CI): Cada push ejecuta tests y verificaciones automáticamente
- Entrega/Despliegue Continuo (CD): El código validado se despliega automáticamente
El resultado: menos bugs en producción, feedback más rápido y desarrolladores que no dedican tiempo a tareas repetitivas.
GitHub Actions: La Plataforma CI/CD de GitHub
GitHub Actions es la solución CI/CD nativa de GitHub, disponible desde 2019 y hoy adoptada por millones de proyectos. Sus ventajas:
- Integración total con GitHub: Disparadores nativos para push, PR, releases, issues
- Marketplace con miles de acciones: Reutiliza lo que la comunidad ya construyó
- Gratuito para repos públicos: Sin límites en proyectos open source
- Runners en la nube: Ubuntu, Windows y macOS disponibles
Conceptos Clave
Workflow (workflow.yml)
├── Trigger (on: push, PR, schedule...)
└── Jobs (se ejecutan en paralelo por defecto)
├── Job: test
│ └── Steps (se ejecutan en secuencia)
│ ├── Step: Checkout código
│ ├── Step: Instalar Node.js
│ ├── Step: npm install
│ └── Step: npm test
└── Job: deploy (solo si test pasa)
└── Steps
└── Step: Deploy a Vercel
| Término | Descripción |
|---|---|
| Workflow | El proceso completo de automatización (archivo YAML) |
| Event | Lo que dispara el workflow (push, PR, cron...) |
| Job | Unidad de trabajo que corre en un runner |
| Step | Acción individual dentro de un job |
| Action | Módulo reutilizable del Marketplace |
| Runner | La máquina virtual donde se ejecuta el job |
| Secret | Variable de entorno cifrada (para API keys) |
Tu Primer Workflow: CI para Node.js
# .github/workflows/ci.yml
name: CI - Tests y Calidad de Código
# ¿Cuándo se ejecuta?
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
name: Tests en Node.js ${{ matrix.node-version }}
runs-on: ubuntu-latest
# Ejecutar en múltiples versiones de Node.js
strategy:
matrix:
node-version: [20.x, 22.x]
fail-fast: false # No cancela otras versiones si una falla
steps:
- name: 📥 Checkout del código
uses: actions/checkout@v4
- name: 🟢 Configurar Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm' # Cachear dependencias automáticamente
- name: 📦 Instalar dependencias
run: npm ci # Más estricto que npm install, ideal para CI
- name: 🔍 Verificar tipos (TypeScript)
run: npm run typecheck
- name: 🎨 Verificar formato (ESLint + Prettier)
run: npm run lint
- name: 🧪 Ejecutar tests con cobertura
run: npm test -- --coverage --ci
- name: 📊 Subir reporte de cobertura
uses: codecov/codecov-action@v4
if: matrix.node-version == '22.x' # Solo para una versión
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
Workflow para Python
# .github/workflows/python-ci.yml
name: CI - Python
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
quality:
name: Calidad y Tests Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Configurar Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Instalar dependencias
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Verificar formato con black
run: black --check --diff .
- name: Lint con ruff
run: ruff check .
- name: Verificar tipos con mypy
run: mypy app/ --strict
- name: Tests con pytest
run: |
pytest \
--cov=app \
--cov-report=xml \
--cov-report=term-missing \
-v \
--tb=short
- name: Subir cobertura
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
Usando Secrets de Forma Segura
Los API keys, contraseñas y tokens nunca deben estar en el código. GitHub Secrets los cifra y los hace disponibles de forma segura.
Configurar Secrets
- Ve a tu repositorio → Settings → Secrets and variables → Actions
- Haz clic en New repository secret
- Nombre:
DATABASE_URL, Valor:postgresql://user:pass@host/db
Usar Secrets en el Workflow
steps:
- name: Desplegar aplicación
env:
# Los secrets se referencian así:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
echo "Iniciando despliegue..."
./scripts/deploy.sh
Importante: GitHub enmascara los valores de los secrets en los logs (***), pero nunca los imprimas explícitamente con echo.
Pipeline Completo: CI + CD a Producción
# .github/workflows/full-pipeline.yml
name: Pipeline Completo
on:
push:
branches: [main]
pull_request:
branches: [main]
# Cancelar runs anteriores del mismo branch
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# ── 1. Tests ────────────────────────────────────
test:
name: Tests
runs-on: ubuntu-latest
timeout-minutes: 10
services:
postgres:
image: postgres:17
env:
POSTGRES_PASSWORD: testpass
POSTGRES_DB: test_db
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm test
env:
DATABASE_URL: postgresql://postgres:testpass@localhost:5432/test_db
NODE_ENV: test
# ── 2. Build de Docker Image ─────────────────────
build:
name: Build Docker Image
runs-on: ubuntu-latest
needs: [test] # Solo si test pasa
if: github.ref == 'refs/heads/main' # Solo en main
outputs:
image-tag: ${{ steps.meta.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Extraer metadata para Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=sha,prefix=,suffix=,format=short
type=raw,value=latest,enable=true
- name: Login a GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build y Push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
# ── 3. Deploy a Staging ───────────────────────────
deploy-staging:
name: Deploy a Staging
runs-on: ubuntu-latest
needs: [build]
environment: staging # Requiere aprobación manual si se configura
steps:
- uses: actions/checkout@v4
- name: Deploy a Staging
run: |
echo "Desplegando versión ${{ needs.build.outputs.image-tag }}"
# Aquí va el comando de deploy real (kubectl, helm, etc.)
# ── 4. Tests de Integración en Staging ───────────
e2e-tests:
name: Tests E2E en Staging
runs-on: ubuntu-latest
needs: [deploy-staging]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- name: Ejecutar tests Playwright
run: npx playwright test
env:
BASE_URL: https://staging.tuapp.com
# ── 5. Deploy a Producción ────────────────────────
deploy-production:
name: Deploy a Producción
runs-on: ubuntu-latest
needs: [e2e-tests]
environment: production # Requiere aprobación manual
steps:
- name: Deploy a Producción
run: echo "Desplegando en producción..."
Técnicas Avanzadas
Workflows Reutilizables
# .github/workflows/reusable-deploy.yml
on:
workflow_call:
inputs:
environment:
required: true
type: string
image-tag:
required: true
type: string
secrets:
DEPLOY_KEY:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy ${{ inputs.environment }}
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
run: |
echo "Desplegando ${{ inputs.image-tag }} en ${{ inputs.environment }}"
# Llamar al workflow reutilizable
jobs:
deploy:
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: production
image-tag: ${{ needs.build.outputs.tag }}
secrets:
DEPLOY_KEY: ${{ secrets.PRODUCTION_DEPLOY_KEY }}
Condiciones y Filtros
# Solo ejecutar si cambian archivos en ciertas rutas
on:
push:
paths:
- 'src/**'
- 'tests/**'
- 'package.json'
paths-ignore:
- '**.md'
- 'docs/**'
jobs:
deploy-backend:
# Solo si los cambios son en el backend
if: contains(github.event.commits[0].modified, 'backend/')
runs-on: ubuntu-latest
steps:
- run: echo "Solo se despliega el backend"
Notificaciones de Estado
notify:
runs-on: ubuntu-latest
needs: [deploy-production]
if: always() # Ejecutar siempre, incluso si falla
steps:
- name: Notificar éxito en Slack
if: needs.deploy-production.result == 'success'
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "✅ Deploy exitoso en producción - ${{ github.sha }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
- name: Notificar fallo
if: needs.deploy-production.result == 'failure'
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "❌ Deploy FALLÓ en producción - <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Ver logs>"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
Checklist para un Pipeline Sólido
- Tests unitarios en múltiples versiones del runtime
- Verificación de tipos (TypeScript/mypy)
- Linting y format checking
- Tests de integración con servicios reales (BD, Redis)
- Scan de vulnerabilidades de dependencias (
npm audit,pip-audit) - Build de imagen Docker con cache
- Deploy a staging antes de producción
- Tests E2E en staging
- Aprobación manual para producción
- Notificaciones al equipo
Conclusión
GitHub Actions transforma tu repositorio en una factoría de software automatizada. Una vez que tienes el pipeline configurado, cada push activa un ciclo de verificación que garantiza la calidad del código antes de que llegue a producción.
El tiempo invertido en configurar este sistema se recupera en la primera semana: menos bugs escapados, menos deploys manuales fallidos, más confianza del equipo para hacer cambios frecuentes.
Empieza con el workflow más simple posible —solo tests— y ve añadiendo etapas según lo necesites. La automatización bien diseñada es incremental.