Un workflow de GitHub Actions es un archivo YAML dentro de `.github/workflows/` que describe qué eventos disparan una ejecución (`push`, `pull_request`, `schedule`, `workflow_dispatch`) y qué jobs y steps se ejecutan sobre cada runner. Escribir ese YAML a mano es propenso a errores de indentación y a olvidar detalles como el `permissions` mínimo o el cacheo de dependencias, que es justo lo que más impacta en el tiempo de build. Este generador de workflows de GitHub Actions construye el archivo completo (checkout, versión de Node o de otro runtime, cache, tests y, opcionalmente, build y deploy) a partir de un formulario, sin salir del navegador y sin depender de plantillas genéricas descargadas de terceros.
Anatomía de un workflow: eventos, jobs y steps
Un workflow se activa por un bloque on, que puede combinar varios eventos: push con filtro de ramas, pull_request con filtro de rutas (paths) para no relanzar CI cuando sólo cambia la documentación, o schedule con sintaxis cron para tareas periódicas. Dentro, cada job corre en un runner independiente (runs-on: ubuntu-latest es el más habitual y el más barato en minutos de cómputo) y contiene una lista ordenada de steps. Cada step o ejecuta un run de shell o invoca una uses: que referencia una action publicada, idealmente fijada por SHA de commit y no sólo por tag, para evitar que una action de terceros cambie de comportamiento sin aviso entre ejecuciones.
Cache de dependencias: el mayor ahorro de minutos
La action actions/cache (o el parámetro cache: npm integrado en actions/setup-node) guarda node_modules o la caché de npm/pnpm entre ejecuciones usando como clave un hash del lockfile. Cuando el lockfile no cambia, la clave coincide y el runner descarga el archivo comprimido de la caché en lugar de reinstalar todo desde el registro. En un proyecto mediano esto reduce el paso de instalación de más de un minuto a pocos segundos. La clave de cache correcta debe incluir el sistema operativo del runner y la versión del runtime, porque unos binarios nativos compilados para Node 18 en Ubuntu no sirven en Node 20 o en Windows.
Matrices de build: probar varias versiones a la vez
El bloque strategy.matrix permite declarar combinaciones de variables (por ejemplo, versiones de Node [18, 20, 22] o sistemas operativos [ubuntu-latest, macos-latest]) y GitHub Actions lanza un job independiente por cada combinación, en paralelo. Esto es imprescindible para una librería publicada en npm que debe funcionar en varias versiones de Node soportadas simultáneamente. fail-fast: false evita que el fallo de una combinación cancele el resto, útil cuando quieres ver el informe completo de compatibilidad en lugar de detenerte en el primer error. El coste en minutos de cómputo crece de forma lineal con el número de combinaciones, así que conviene limitar la matriz a lo que realmente necesitas soportar.
Secrets, variables de entorno y el principio de mínimo privilegio
Los secrets (definidos en Settings → Secrets and variables del repositorio o de la organización) se inyectan como variables de entorno con env: y nunca deben imprimirse con run: echo, porque GitHub los enmascara en el log sólo si detecta el valor exacto, no variaciones codificadas. El bloque permissions a nivel de workflow o de job debe declararse explícitamente y en el mínimo necesario: un job de test no necesita contents: write, y un job que sólo lee el repositorio puede limitarse a contents: read. Desde 2023 GitHub aplica permisos de sólo lectura por defecto en el GITHUB_TOKEN para repositorios nuevos, pero conviene no depender del valor por defecto y fijarlo en el YAML.
Artifacts y dependencias entre jobs
Cuando un job de build genera archivos que necesita un job de deploy posterior, actions/upload-artifact sube esos archivos y actions/download-artifact los recupera en otro job, que debe declarar needs: build para forzar el orden y esperar a que termine. Sin needs, GitHub Actions ejecuta todos los jobs en paralelo por defecto, lo que en un pipeline de build-y-deploy provocaría que el deploy arrancara antes de que el build terminase. Los artifacts tienen una retención configurable (90 días por defecto en repositorios públicos) y cuentan para la cuota de almacenamiento de Actions, así que no conviene subir carpetas completas de node_modules como artifact.
Condicionales: ejecutar un step sólo en ciertas ramas o eventos
El campo if: acepta expresiones sobre el contexto del workflow, como if: github.ref == 'refs/heads/main' para restringir el deploy sólo a la rama principal, o if: github.event_name == 'pull_request' para publicar comentarios de cobertura sólo en revisiones. También es habitual usar if: always() en un step de notificación para que se ejecute incluso si un step anterior ha fallado, o if: failure() para lanzar una alerta únicamente cuando algo se ha roto. Estas condiciones evitan tener que duplicar el workflow completo en varios archivos para cubrir variaciones de comportamiento entre ramas o eventos.
Reutilización: composite actions y workflows reutilizables
Cuando el mismo bloque de steps se repite en varios workflows (por ejemplo, checkout más setup más lint), una composite action local en .github/actions/mi-accion/action.yml lo encapsula en una sola llamada uses: ./.github/actions/mi-accion. Para lógica compartida entre repositorios, un workflow reutilizable declarado con on: workflow_call permite que otro repositorio lo invoque con uses: org/repo/.github/workflows/ci.yml@main y le pase inputs y secrets explícitos. Esta segunda opción es la que usan las organizaciones para centralizar la política de CI/CD de decenas de repositorios sin copiar y pegar YAML en cada uno.
Salidas reales de ejemplo
Run actions/setup-node@v4
with:
node-version: 20
cache: npm
Attempting to download 20.11.1...
Acquiring 20.11.1 - x64 from tool-cache
Environment details
node version: v20.11.1
npm version: 10.2.4
Cache restored from key: node-cache-Linux-x64-npm-a1b2c3d4e5f6...
Cache restored successfully
npm cache is restored.
Run npm ci
added 842 packages in 1.8sFíjate en la línea `Cache restored from key` y en que el paso de instalación tarda menos de dos segundos porque no vuelve a descargar el registro de npm.
build (ubuntu-latest, 18) ✅ completed 1m 42s
build (ubuntu-latest, 20) ✅ completed 1m 35s
build (ubuntu-latest, 22) ❌ failed 0m 58s
└─ Error: Cannot find module 'undici' (native fetch API mismatch)
build (macos-latest, 20) ✅ completed 2m 11s
Annotations
1 error and 0 warningsCada combinación de la matriz aparece como job independiente; el fallo en Node 18 no cancela los demás porque el workflow usa fail-fast: false.
With the provided path, there will be 34 files uploaded
Artifact name is valid!
Root directory input is valid!
Beginning upload of artifact content to blob storage
Uploaded bytes 2621440
Finished uploading artifact content to blob storage!
SHA256 digest of uploaded artifact zip is 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
Artifact dist-app has been successfully uploaded! Final size is 2621440 bytes.El artifact queda disponible para descarga y para el job de deploy que declare needs: build.
Run softprops/action-gh-release@v2
HttpError: Resource not accessible by integration
at /home/runner/work/_actions/.../request.js:74
Error: Resource not accessible by integration
Process completed with exit code 1.Error típico cuando el workflow intenta crear un release sin declarar permissions: contents: write.
Runners hospedados por GitHub más comunes
| Runner | vCPU / RAM | Multiplicador de minutos | Uso típico |
|---|---|---|---|
| ubuntu-latest | 4 vCPU / 16 GB | x1 | Test, lint, build de la mayoría de proyectos |
| windows-latest | 4 vCPU / 16 GB | x2 | Builds .NET o pruebas específicas de Windows |
| macos-latest | 3 vCPU / 14 GB | x10 | Firmar y compilar apps iOS/macOS |
Minutos consumidos por minuto real de ejecución según el multiplicador de facturación de GitHub Actions.
Casos de uso comunes
- CI de una librería de npm que debe validarse en varias versiones de Node antes de publicar
- Pipeline de build y despliegue automático a un VPS o a un bucket S3 al hacer push a main
- Lint y tests obligatorios como check de un pull request antes de permitir el merge
- Job programado (`schedule`) que ejecuta un backup nocturno o revisa dependencias desactualizadas
- Workflow reutilizable centralizado para estandarizar CI en todos los repositorios de una organización
Buenas prácticas
- Fija las actions de terceros por SHA de commit, no sólo por tag, para evitar cambios inesperados
- Declara `permissions` explícitos y mínimos en cada job, nunca dejes el valor por defecto sin revisar
- Usa `paths`/`paths-ignore` para no relanzar CI cuando sólo cambian archivos de documentación
- Cachea dependencias con una clave que incluya el hash del lockfile y el sistema operativo del runner
- Separa el job de test del job de deploy con `needs`, y protege el deploy con `environment` y reglas de aprobación
- No imprimas secrets con `echo` ni los pases como argumento de línea de comandos visible en el log