CI/CD Avanzado para proyectos JavaScript con GitHub Actions
La automatización de la compilación, las pruebas y el despliegue (CI/CD) es una parte esencial del desarrollo moderno en JavaScript. Si ya dominas los pipelines básicos de GitHub Actions, es hora de pasar al siguiente nivel. En este artículo, analizaremos técnicas avanzadas: caché de dependencias, compilaciones matriciales, ejecución paralela de tareas, trabajo seguro con secretos y despliegue automático en VPS y Docker. Todos los ejemplos están orientados a proyectos Node.js.
1. Optimización del tiempo de compilación: caché y ejecuciones condicionales
Uno de los principales problemas de los pipelines básicos es la larga instalación de node_modules en cada ejecución. GitHub Actions permite almacenar en caché las dependencias, lo que reduce el tiempo de ejecución entre 2 y 4 veces.
Utiliza la acción actions/cache para almacenar en caché node_modules o ~/.npm:
name: CI Pipelineon: [push, pull_request]
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20'
- name: Cache node_modules uses: actions/cache@v3 id: npm-cache with: path: node_modules key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }} restore-keys: | ${{ runner.os }}-node-
- name: Install dependencies if: steps.npm-cache.outputs.cache-hit != 'true' run: npm ciConsejos avanzados:
- Usa
hashFiles('package-lock.json')para invalidar con precisión la caché al cambiar las dependencias. - Para monorepositorios, almacena en caché cada paquete por separado con
path: packages/*/node_modules. - Añade ejecuciones condicionales (
if: github.ref == 'refs/heads/main') para que las tareas pesadas (por ejemplo, la compilación de la versión de producción) se ejecuten solo en los pushes a la rama main.
2. Compilaciones matriciales y tareas paralelas
La estrategia matricial permite ejecutar un mismo workflow con diferentes parámetros. Es indispensable para probar en múltiples versiones de Node.js o sistemas operativos.
jobs: test: strategy: matrix: node-version: [18, 20, 22] os: [ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm ci - run: npm testPara la ejecución paralela de tareas independientes (por ejemplo, linting, pruebas unitarias, pruebas de integración), utiliza jobs separados. Se ejecutan simultáneamente y no se bloquean entre sí:
jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npm run lint
unit-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npm run test:unit
integration-tests: needs: [lint, unit-tests] # espera a que lint y unit terminen runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npm run test:integrationImportante: Usa needs para definir el orden de ejecución de las tareas dependientes. Esto ahorra tiempo y recursos.
3. Despliegue avanzado: Docker y VPS usando artefactos
Después de las pruebas exitosas, es necesario desplegar la aplicación. Consideremos dos escenarios populares: despliegue en una imagen Docker y en un VPS.
3.1. Compilación y publicación de una imagen Docker
jobs: build-and-push: needs: [test] runs-on: ub