GitHub Actions: de CI/CD básico a pipelines de Platform Engineering

En este artículo
  1. El nivel 0: el workflow que todo el mundo tiene
  2. Nivel 1: matrices y múltiples entornos
  3. Nivel 2: caches y artefactos
  4. Nivel 3: jobs dependientes y despliegues
  5. Nivel 4: Platform Engineering con GitHub Actions
  6. Validación de Terraform
  7. Escaneo de seguridad
  8. Generación de documentación
  9. Nivel 5: workflows reutilizables
  10. Los errores que cometí
  11. 1. No usar concurrency
  12. 2. Hardcodear secrets en los logs
  13. 3. No usar matrix.fail-fast: false
  14. Mi stack de GitHub Actions
  15. Conclusión

Llevo años usando GitHub Actions y la evolución que he visto es brutal. Empecé con workflows de tres líneas que ejecutaban npm test y ahora tengo pipelines que gestionan infraestructura, validan seguridad, generan documentación y despliegan a producción — todo sin tocar un servidor.

En este artículo te cuento cómo pasé de lo básico a lo avanzado, con ejemplos reales que puedes copiar.

El nivel 0: el workflow que todo el mundo tiene

name: CI
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npm test

Esto es lo que todo el mundo tiene. Funciona. Pero es solo el punto de partida.

Nivel 1: matrices y múltiples entornos

El primer salto fue entender las matrices. En lugar de un solo job, ejecutas tests en múltiples versiones de Node, Python o Go:

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

Esto te da 6 jobs en paralelo. Si uno falla, los demás siguen corriendo. Muy útil para asegurarte de que tu código funciona en todas las versiones soportadas.

Nivel 2: caches y artefactos

El segundo salto fue optimizar. npm ci es rápido, pero ¿y si puedes evitarlo?

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

Con esto, la primera instalación descarga todo. Las siguientes usan la cache y tardan segundos. En pipelines que se ejecutan 50 veces al día, la diferencia es enorme.

Y los artefactos:

- uses: actions/upload-artifact@v4
  with:
    name: coverage-report
    path: coverage/
    retention-days: 30

Esto te permite ver los resultados de cobertura incluso después de que el job termine.

Nivel 3: jobs dependientes y despliegues

Aquí es donde se pone interesante. Puedes hacer que un job dependa de otro:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    steps:
      - uses: actions/checkout@v4
      - run: echo "Deploying to production..."
        env:
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}

El needs: test hace que deploy solo corra si test pasa. El if hace que solo deploye en main y solo en push (no en PRs).

Nivel 4: Platform Engineering con GitHub Actions

Aquí es donde todo cambia. GitHub Actions no es solo para tests y deploy — es una plataforma completa de automatización.

Validación de Terraform

name: Terraform Plan
on:
  pull_request:
    paths:
      - 'infrastructure/**'

jobs:
  plan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: terraform init
      - run: terraform plan -out=tfplan
      - uses: actions/upload-artifact@v4
        with:
          name: tfplan
          path: tfplan

Cada PR que toca Terraform genera un plan. Lo subes como artefacto y lo revisas antes de merge. Sin necesidad de un servidor de CI dedicado.

Escaneo de seguridad

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-results.sarif'
      - name: Upload Trivy scan results
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: 'trivy-results.sarif'

Esto integra directamente con GitHub Security tab. Cada PR muestra vulnerabilidades antes de que lleguen a producción.

Generación de documentación

name: Docs
on:
  push:
    branches: [main]
    paths:
      - 'docs/**'

jobs:
  docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: pip install mkdocs-material
      - run: mkdocs build
      - uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./site

Cada push a main regenera la docs y la publica en GitHub Pages. Sin intervención humana.

Nivel 5: workflows reutilizables

Cuando tienes 10 repos con los mismos workflows, copiar y pegar se vuelve insostenible. La solución: workflows reutilizables.

# .github/workflows/ci.yml (reusable)
name: Reusable CI
on:
  workflow_call:
    inputs:
      node-version:
        type: number
        default: 20
    secrets:
      DEPLOY_KEY:
        required: true

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
      - run: npm ci && npm test

Y en cada repo:

name: CI
on: [push, pull_request]

jobs:
  ci:
    uses: mi-org/shared-workflows/.github/workflows/ci.yml@main
    with:
      node-version: 22
    secrets:
      DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}

Un solo lugar para actualizar la configuración de CI. Si cambias algo, se propaga a todos los repos.

Los errores que cometí

1. No usar concurrency

Sin concurrency, cada push dispara un workflow nuevo. Si haces 5 pushes en un minuto, tienes 5 workflows corriendo en paralelo. Con concurrency:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

Esto cancela workflows anteriores del mismo branch. Ahorras tokens y tiempo.

2. Hardcodear secrets en los logs

Nunca hagas echo $SECRET en un step. Si el step falla, el log se expone. Usa env en lugar de args:

# ✅ Correcto
- run: ./deploy.sh
  env:
    API_KEY: ${{ secrets.API_KEY }}

# ❌ Incorrecto
- run: API_KEY=${{ secrets.API_KEY }} ./deploy.sh

3. No usar matrix.fail-fast: false

Por defecto, si un job de la matrix falla, cancela los demás. Para tests, quieres ver TODOS los resultados:

strategy:
  fail-fast: false
  matrix:
    node-version: [18, 20, 22]

Mi stack de GitHub Actions

Esto es lo que uso en mis proyectos de Platform Engineering:

Acción Uso
actions/checkout@v4 Siempre
actions/setup-node@v4 Proyectos Node.js
actions/setup-python@v5 Proyectos Python
hashicorp/setup-terraform@v3 Terraform plans
aquasecurity/trivy-action@master Escaneo de vulnerabilidades
peaceiris/actions-gh-pages@v3 Deploy a GitHub Pages
codecov/codecov-action@v4 Upload de cobertura
github/codeql-action/upload-sarif@v3 Integración con Security tab

Conclusión

GitHub Actions ha evolucionado de un simple sistema de CI/CD a una plataforma completa de automatización. Para Platform Engineers, es una herramienta fundamental: puedes automatizar desde tests básicos hasta despliegues de infraestructura con seguridad integrada.

La clave es empezar simple y añadir complejidad gradualmente. No necesitas todos los workflows de nivel 5 desde el día uno. Empieza con tests, añade caches, luego despliegues, y poco a poco construye tu plataforma de automatización.

¿Qué workflows usas en tus proyectos? ¿Tienes algún truco que te funcione bien?


Sergio Valverde es Platform Engineer en Lunik, Organizer de GitHub Community Spain, y entusiasta del Developer Experience. Sígueme en GitHub, LinkedIn, y X.