CI/CD no tiene que ser complejo para ser profesional. Un pipeline bien hecho con GitHub Actions puede correr lint, tests, build y deploy en una única configuración YAML que cualquier ingeniero del equipo puede entender y mantener.
Este artículo construye un pipeline real desde cero, explica cada decisión y muestra los patrones que evitan los problemas más comunes en producción.
Estructura básica del workflow
Un workflow de GitHub Actions es un archivo YAML en .github/workflows/. La estructura es simple: cuándo correr, en qué ambiente, con qué pasos.
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm typecheck
- run: pnpm test --coverage
- run: pnpm build
Ese es el pipeline mínimo. Simple, pero ya captura la mayoría de los problemas antes de llegar a producción.
Cache de dependencias: el detalle que cambia la velocidad
Sin cache, cada run instala todas las dependencias desde cero. Pueden ser 2-3 minutos solo en eso. Con cache, la instalación de dependencias que no cambiaron se salta.
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm' # ← hash del lockfile como clave de cache
Para npm, usa cache: 'npm'. Para yarn, cache: 'yarn'. El cache usa el hash del lockfile como clave. Cuando el lockfile cambia, el cache se invalida.
Separando CI de CD: el job de deploy
CI (integración continua) corre en cada PR. CD (entrega continua) corre solo cuando el código entra en producción. Sepáralo en jobs distintos con dependencia explícita:
jobs:
ci:
runs-on: ubuntu-latest
steps:
# ... lint, test, build
deploy:
needs: ci # solo corre si CI pasa
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' # solo en main
environment: production # requiere aprobación manual si está configurado
steps:
- uses: actions/checkout@v4
- name: Deploy to production
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
run: |
# tu script de deploy aquí
Secrets: cómo hacerlo bien
Nunca pongas credenciales en el YAML. Usa GitHub Secrets, disponibles en Settings > Secrets and variables > Actions del repositorio.
- name: Deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: ./scripts/deploy.sh
Para ambientes (production, staging), usa Environment Secrets. Permiten configurar secrets diferentes por ambiente y agregar revisores obligatorios antes del deploy.
Matrix builds: testeando en múltiples versiones
Para librerías o APIs con soporte de múltiples versiones de Node, los matrix builds corren el CI en paralelo en todas las combinaciones:
jobs:
ci:
strategy:
matrix:
node-version: [18, 20, 22]
runs-on: ubuntu-latest
name: Test on Node ${{ matrix.node-version }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
Artifacts: preservando outputs entre jobs
Si quieres usar el resultado del build en el job de deploy, o preservar reportes de cobertura, usa artifacts:
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 7
El pipeline completo
name: CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm typecheck
- run: pnpm test --coverage
- run: pnpm build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
deploy:
needs: ci
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
environment: production
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Deploy
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: ./scripts/deploy.sh
88 líneas. Pipeline completo, seguro, con cache, separación CI/CD y deploy condicional. Eso es todo lo que la mayoría de los proyectos necesita.
¿Te gustó el contenido?
Construyo productos web y soluciones con IA de la manera correcta — arquitectura sólida, código sostenible y entrega real.
Hablemos