Saltar al contenido principal
45 min de lectura1 de agosto de 2026
CI/CDGitHub ActionsDevOpsPipelineDeploy

Requisitos previos

  • git

Lo que aprenderás

  • Entender los conceptos de CI/CD y GitHub Actions
  • Escribir workflows con jobs y steps
  • Automatizar build, lint y test
  • Usar secrets y cache de dependencias
  • Desplegar automáticamente con matrices y triggers
Tutorial45 min

CI/CD con GitHub Actions

GitHub Actions automatiza el pipeline completo de tu repositorio: cada push puede compilar, pasar lint, ejecutar tests y desplegar a producción. Aprende los conceptos, la estructura YAML y los patrones que usan los proyectos reales.


¿Qué es CI/CD?

CI (Continuous Integration) es la práctica de integrar el código de todo el equipo en el repositorio compartido de forma frecuente. Cada integración se verifica automáticamente con build, lint y tests, de modo que los errores aparecen en minutos, no en días.

CD (Continuous Delivery / Deployment) automatiza la publicación del código verificado. En Continuous Delivery el artefacto queda listo para desplegarse con un clic; en Continuous Deployment se despliega solo a producción cuando el pipeline pasa.

El flujo mental: commit → CI comprueba que todo está sano → CD entrega el artefacto → despliegue. Cada capa falla rápido y de forma ruidosa, así el equipo sabe dónde mirar antes de que el problema llegue a usuarios.

Conceptos de GitHub Actions

Actions se estructura en cinco piezas con una jerarquía clara:

Workflow: el archivo YAML completo en .github/workflows/. Define el autómata: cuándo se dispara y qué hace.

Evento: lo que dispara el workflow. push, pull_request, schedule, workflow_dispatch...

Job: un conjunto de pasos que se ejecutan en un mismo runner. Los jobs de un workflow corren en paralelo por defecto.

Step: cada acción individual dentro de un job: un comando, una acción reutilizable de la marketplace...

Runner: la máquina que ejecuta los jobs. GitHub ofrece runners alojados (ubuntu-latest, macos-latest, windows-latest) y puedes conectar los tuyos.

Estructura del workflow

Todo empieza con un archivo YAML dentro de .github/workflows/. La clave on declara los eventos que lo disparan:

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  workflow_dispatch:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: echo "¡Pipeline en marcha!"

Con push el workflow corre en cada commit a main; con pull_requesttambién en cada PR contra main, que es donde se filtran los errores antes de integrar. workflow_dispatch añade un botón "Run workflow" manual en la UI de GitHub.

Jobs y steps

Cada job declara en qué runner ejecutarse y una lista de steps que se ejecutan en orden. Los steps pueden ser acciones de la comunidad ( uses) o comandos shell ( run):

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      # Clona el repositorio dentro del runner
      - uses: actions/checkout@v4

      # Configura Node.js en la máquina
      - uses: actions/setup-node@v4
        with:
          node-version: 22

      # Comando shell: instala dependencias
      - run: npm ci

      # Varios comandos, directorio de trabajo y env propio
      - name: Verificar formato
        working-directory: apps/web
        env:
          NODE_ENV: test
        run: |
          npm run lint
          npm test
Cada run se ejecuta en una shell nueva e independiente: las variables y el directorio no persisten entre steps. Usa env: a nivel de step o job para pasar contexto, o actions/upload-artifact para guardar archivos entre jobs.

Build, lint y test

El corazón de CI para un proyecto Node/TypeScript: instalar de forma reproducible, compilar, comprobar estilo y ejecutar la suite de tests. Los pasos con nombre hacen legible el pipeline en la UI de GitHub:

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm

      - name: Instalar dependencias
        run: npm ci

      - name: Typecheck
        run: npm run typecheck

      - name: Lint
        run: npm run lint

      - name: Tests
        run: npm test

      - name: Build
        run: npm run build

npm ci instala exactamente lo que define package-lock.json y borra node_modules antes — es la opción reproducible para CI, a diferencia de npm install.

Secrets

Nunca pongas tokens ni contraseñas en el YAML: se guardan en Settings → Secrets and variables → Actions del repositorio y se referencian en el workflow. GitHub los inyecta como variables de entorno sin exponerlos en los logs:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Desplegar
        env:
          VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
        run: |
          npx vercel deploy --prod --token "$VERCEL_TOKEN"
          curl -X POST https://api.miapp.com/deploy
Un secreto referenciado en un step no es visible para el runner salvo que lo pases como variable de entorno. Y cuidado: ${{ secrets.API_KEY }} dentro de un run: puede quedar impreso en los logs. Pasa los secretos por env: y redacta la salida.

Cache de dependencias

Instalar dependencias cada vez tarda minutos. La acción actions/cache restaura node_modules desde un cache con key derivada del lockfile, y la guarda de nuevo si cambia:

- uses: actions/cache@v4
  with:
    path: node_modules
    key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

La key incluye el hash del lockfile: si las dependencias no cambian, el cache acierta. restore-keys ofrece caches parciales cuando la key falla. setup-node lo tiene integrado con cache: npm para este caso común.

Con pnpm la clave está en que las dependencias van en ~/.local/share/pnpm/store y se usa pnpm fetch para poblar el store antes de instalar:

- uses: pnpm/action-setup@v4
  with:
    version: 9
- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: pnpm
- run: pnpm install --frozen-lockfile

Despliegue

El último tramo: publicar el artefacto verificado. Según el destino cambia el mecanismo — un CLI con token (Vercel), una API REST (Render), o un push a la rama gh-pages para GitHub Pages:

# Despliegue a Vercel solo desde main
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npm run build

      - name: Deploy a Vercel
        env:
          VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
          VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
        run: npx vercel deploy --prod --token "$VERCEL_TOKEN"

Protege el job de despliegue con una condición needs: solo desplegar si los jobs de calidad han pasado. Y en repositorios con reviewers, el entorno de GitHub ("environment") puede exigir aprobación humana antes del despliegue a producción.

Separar el job deploy con needs: [quality] hace que el despliegue dependa explícitamente de que build, lint y tests hayan terminado en verde. Sin esa dependencia, los jobs paralelos podrían desplegar un build roto.

Matrices

Una matrix ejecuta el mismo job con distintas combinaciones de variables, típicamente para probar varias versiones de Node o sistemas operativos. Cada combinación es un job independiente y paralelo:

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20, 22]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: npm
      - run: npm ci
      - run: npm test

Puedes combinar dimensiones: os: [ubuntu-latest, macos-latest] con node-version: [20, 22] genera 4 jobs. Para versiones futuras, include añade configuraciones extra y exclude descarta combinaciones que no quieres.


Ejercicios


Con GitHub Actions tu repositorio deja de ser código inerte: cada push se comprueba, se prueba y se publica de forma reproducible. Los patrones de este tutorial — jobs separados, cache, secrets, matrices — son los mismos bloques con los que se construyen pipelines de cualquier complejidad.

Siguiente lección

Kubernetes: orquestación de contenedores

Domina Kubernetes: arquitectura del cluster, Pods, Deployments con escala y rollback, Services, ConfigMaps y Secrets, e Ingress para exponer tus aplicaciones.