En el Nivel 8 viste qué es la integración continua: la portería automática que corre tus
tests en cada PR y bloquea el merge si algo falla. También viste de pasada un ci.yml
mínimo. Lo que no viste es el cómo: qué hay dentro de ese fichero, qué significa cada línea
y cómo se convierte en algo útil de verdad.
En el capítulo 1 de este nivel abriste Pull Requests con gh pr create. GitHub puede
configurarse para que esos PRs no se puedan mergear hasta que el CI pase. El workflow que
vas a aprender a escribir aquí es exactamente lo que bloquea ese merge. Vamos pieza a pieza.
La anatomía de un workflow#
Un workflow es un fichero YAML que vive en .github/workflows/ dentro de tu repositorio.
GitHub lo lee automáticamente y lo ejecuta cuando ocurre el evento que le indiques. No
necesitas instalar nada ni configurar ningún servidor: GitHub provisiona la máquina, ejecuta
el workflow y te muestra el resultado en la pestaña “Actions” del repositorio.
Un workflow tiene cuatro piezas principales:
# Nombre visible en la pestaña Actions de GitHub
name: CI
# Cuándo se ejecuta este workflow
on: [push, pull_request]
# Permisos mínimos: este workflow solo lee el código, no publica nada.
# El principio de mínimo privilegio dicta que un workflow declara solo los permisos que necesita.
permissions:
contents: read
# Lista de jobs que se ejecutan
jobs:
# Nombre del job; se ve en el log de Actions
test:
# Runner: máquina virtual Ubuntu que GitHub provisiona
runs-on: ubuntu-latest
# Lista de pasos que ejecuta el job, en orden
steps:
# Cada step tiene un nombre y una acción o comando
- name: Obtener el código
uses: actions/checkout@v6
- name: Ejecutar tests
run: pnpm testEl flujo cuando haces push es el siguiente:
push a GitHub
│
▼
GitHub detecta el evento "push"
│
▼
aprovisiona una máquina virtual Ubuntu limpia (el runner)
│
▼
ejecuta los steps del job en orden
│
├── checkout@v6 → clona el repositorio
├── pnpm install → instala dependencias
└── pnpm test → corre los tests
│
▼
verde (pasa) o rojo (falla) junto al commit en GitHubCada elemento tiene un papel claro. name es el nombre que ves en la interfaz. on decide
cuándo corre. jobs agrupa el trabajo. Los steps dentro de cada job son la secuencia
exacta de comandos que se ejecuta en la máquina.
Cuándo se ejecuta: los triggers (on)#
El campo on define qué evento de GitHub dispara el workflow. Los más usados:
# Dispara en cada push a cualquier rama y en cada PR
on: [push, pull_request]
# Solo en pushes a main (ignora otras ramas)
on:
push:
branches: [main]
# Solo en PRs que apuntan a main
on:
pull_request:
branches: [main]
# Permite ejecutarlo a mano desde la interfaz de GitHub
on:
workflow_dispatch:push dispara el workflow cada vez que alguien sube commits al repositorio, sea la rama que
sea. pull_request lo dispara cuando se abre un PR, cuando se actualiza con nuevos commits,
o cuando se sincroniza con la rama base.
workflow_dispatch es útil para workflows de deploy manual: aparece como un botón “Run
workflow” en la interfaz de GitHub Actions y te permite lanzar el workflow sin necesidad de
hacer un push.
En el Team Builder, la combinación [push, pull_request] cubre el caso habitual: el CI
corre en tu rama mientras trabajas (para que sepas si algo está roto antes de pedir revisión)
y de nuevo cuando abres el PR (para que el revisor sepa que el código que revisa pasa
las comprobaciones).
Dónde corre: runners y jobs#
Un runner es la máquina virtual que GitHub aprovisiona para ejecutar tu workflow. Cada vez que el workflow se dispara, GitHub arranca una máquina nueva, limpia, sin nada instalado salvo el sistema operativo y unas herramientas básicas. Tu workflow parte de cero cada vez.
jobs:
# El job "lint" corre en su propio runner, independiente del resto
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: pnpm lint
# El job "test" corre en otro runner, en paralelo con "lint"
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: pnpm test
# El job "deploy" espera a que "test" y "lint" pasen antes de correr
deploy:
runs-on: ubuntu-latest
# needs encadena jobs: deploy no empieza hasta que lint y test terminan en verde
needs: [lint, test]
steps:
- run: echo "desplegando..."Por defecto, todos los jobs de un workflow corren en paralelo en runners separados. Si
necesitas que un job espere a otro, usas needs:. En el ejemplo anterior, deploy no
empieza hasta que lint y test terminan en verde. Si cualquiera de los dos falla,
deploy no corre.
ubuntu-latest es la opción más común y la más económica en GitHub Actions. También existen
windows-latest y macos-latest para proyectos que necesitan probarse en esos sistemas.
Steps: uses frente a run#
Dentro de un job, los steps son los pasos concretos. Un step o usa una action reutilizable
(con uses) o ejecuta un comando de shell (con run). No puede hacer las dos cosas a la vez.
steps:
# uses: invoca una action publicada en GitHub Marketplace o en un repositorio
# @v6 fija la versión; sin versión, tomaría la última, lo que puede romper el workflow
- name: Descargar el código
uses: actions/checkout@v6
# uses con parámetros: la clave "with" pasa opciones a la action
- name: Instalar pnpm
uses: pnpm/action-setup@v4
with:
# Versión de pnpm que se instala
version: 9
# run: ejecuta cualquier comando de shell disponible en el runner
- name: Instalar dependencias
run: pnpm install --frozen-lockfile
# run con varias líneas: el separador | ejecuta cada línea como un comando
- name: Verificar calidad
run: |
pnpm lint
pnpm testUna action es código empaquetado para resolver un problema de infraestructura: clonar el repositorio, configurar una versión de Node, autenticarse con un registro de Docker, subir un fichero a S3. En vez de escribir esos comandos a mano cada vez, invocas la action y le pasas los parámetros que necesita.
actions/checkout@v6 es casi obligatoria en cualquier workflow: sin ella, el runner no tiene
el código del repositorio. Es siempre el primer step.
Caché de dependencias: que el CI vuele#
Sin caché, cada run de CI arranca una máquina limpia y descarga todas las dependencias desde npm. En un proyecto con cien dependencias, eso son fácilmente sesenta segundos o más solo en el paso de instalación. Multiplicado por veinte PRs al día, son veinte minutos de espera que no aportan nada.
La caché de pnpm soluciona esto: GitHub guarda el store de pnpm entre runs y lo restaura si el lockfile no ha cambiado. Solo se descargan los paquetes nuevos o modificados.
- name: Instalar pnpm
uses: pnpm/action-setup@v4
with:
version: 9
# cache: 'pnpm' activa la integración de caché automática para pnpm.
# actions/setup-node calcula un hash del pnpm-lock.yaml.
# Si ese hash coincide con un run anterior, restaura el store antes de instalar.
- name: Configurar Node.js con caché de pnpm
uses: actions/setup-node@v6
with:
node-version: 24
cache: 'pnpm'
- name: Instalar dependencias
run: pnpm install --frozen-lockfileEl “¿y qué?” de ignorar esto: un CI que tarda un minuto y medio en instalar dependencias es un CI que el equipo deja de esperar. Cuando el CI ya no frena el flujo de trabajo, la gente empuja sin esperar el verde. La portería deja de funcionar en la práctica aunque técnicamente esté activa.
Con caché activa, el segundo run y los siguientes pasan la instalación en tres o cinco segundos. El CI vuelve a ser algo que el equipo espera porque no cuesta nada esperarlo.
Secrets: lo que nunca va en el YAML#
Muchos workflows necesitan credenciales: tokens de autenticación para publicar en un registro de npm, claves de API para notificar a un servicio externo, contraseñas de bases de datos de prueba. Nunca deben ir escritas en el YAML.
# MAL: el token queda en el historial de Git para siempre
- name: Publicar paquete
run: npm publish --token ghp_MiTokenSecreto123
# BIEN: el token vive en la configuración cifrada del repositorio
- name: Publicar paquete
run: npm publish --token ${{ secrets.NPM_TOKEN }}
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}Los secrets se crean en la configuración del repositorio (Settings → Secrets and variables →
Actions). GitHub los cifra y los inyecta en el workflow en tiempo de ejecución. En los logs,
cualquier valor que coincida con un secret aparece enmascarado como ***.
El “¿y qué?” de hardcodear un token: un repositorio que hoy es privado puede hacerse público
mañana. Aunque no se haga público, cualquier persona con acceso al historial puede recuperar
ese commit con el token expuesto. GitHub escanea los repositorios públicos en busca de tokens
reconocibles y avisa a los proveedores correspondientes para revocarlos, así que en cuestión
de minutos el token está comprometido e inutilizado. Es la misma regla del .env que
aprendiste en el Nivel 0, aplicada al entorno de CI.
Nunca hardcodees un secret. Ni en un commit “de prueba que luego borro”.
Cada workflow debería declarar además los permisos mínimos que necesita con la clave
permissions a nivel del workflow: un workflow de CI que solo lee código no necesita
más que contents: read. Es el principio de mínimo privilegio aplicado al pipeline.
Matrix: probar en varias versiones a la vez#
Tu proyecto puede funcionar perfectamente en Node 22 y romperse en Node 24 por un cambio en alguna API del runtime. Si tu CI solo prueba en una versión, no lo sabrás hasta que el problema llegue a producción.
La matrix de estrategia resuelve esto: define un conjunto de valores y GitHub lanza un job por cada combinación, todos en paralelo.
jobs:
ci:
runs-on: ubuntu-latest
# strategy.matrix define las combinaciones que se ejecutan en paralelo
strategy:
matrix:
# Dos valores → dos jobs en paralelo, uno con Node 22 y otro con Node 24.
# Node 22 es la LTS de mantenimiento y Node 24 la LTS activa
# (Node 20 quedó fuera de soporte desde abril de 2026).
node-version: [22, 24]
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
with:
version: 9
# ${{ matrix.node-version }} inyecta el valor de este job concreto:
# en el job con Node 22 vale "22"; en el job con Node 24 vale "24"
- uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm testEn la pestaña Actions verás dos jobs corriendo en paralelo: ci (22) y ci (24). Si uno
falla, el check del commit queda en rojo aunque el otro haya pasado. Lo sabes antes de
que llegue a producción.
Puedes añadir más ejes a la matrix (sistema operativo, versión de pnpm…) pero el número de jobs crece multiplicativamente: dos versiones de Node por dos sistemas operativos son cuatro jobs. Usa la matrix con criterio; para la mayoría de proyectos, dos versiones de Node es más que suficiente.
Artifacts: guardar lo que produce el build#
El build de producción (el dist/ que genera pnpm build) existe dentro del runner mientras
el workflow corre. Cuando el run termina, el runner se destruye y todo lo que había dentro
desaparece. Si un compañero necesita revisar ese build, tiene que hacer checkout y construirlo
en local.
Los artifacts son la solución: un step al final del job sube el contenido que quieras al almacenamiento de GitHub, donde queda disponible para descarga durante el tiempo que configures.
# El build de producción se genera en el step anterior
- name: Construir bundle de producción
run: pnpm build
# actions/upload-artifact sube el contenido de dist/ al almacenamiento de GitHub
- name: Subir dist/ como artifact
uses: actions/upload-artifact@v4
with:
# Nombre del artifact; se ve en la pestaña Actions
name: dist-produccion
# Qué carpeta o fichero se sube
path: dist/
# Cuántos días se conserva antes de borrarse automáticamente
retention-days: 7En la pestaña Actions, al final del run, aparece el artifact como un fichero descargable. Cualquier miembro del equipo puede descargarlo sin necesidad de tener el código en local.
Los artifacts son también la base del CD: el paso siguiente no es descargarlo a mano, sino configurar otro job que lo publique automáticamente en un servidor. Pero eso llega en el capítulo siguiente.
De CI a CD#
CI (integración continua) responde a: ¿este código es correcto? Corre lint, tests y build en cada push y bloquea el merge si algo falla. Es la portería.
CD (despliegue continuo) responde a: ¿cuándo y cómo se publica? Una vez el CI pasa en main, CD automatiza el envío a producción. Puede ser GitHub Pages, Vercel, un servidor propio o cualquier plataforma de hosting.
En términos de workflow, el CD es otro job que usa needs: para esperar al CI:
# Permisos mínimos para el job de CI; el job de deploy necesitará permisos adicionales
permissions:
contents: read
jobs:
ci:
runs-on: ubuntu-latest
steps:
- run: pnpm test
- run: pnpm build
# deploy no corre hasta que ci pase en verde
deploy:
needs: ci
# Solo se despliega cuando el evento es un push a main,
# no en cada PR ni en ramas de feature
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- run: echo "publicando en producción..."El status check que bloquea el merge en los PRs del capítulo 1 es el job de CI. Cuando configuras la rama protegida en GitHub (Settings → Branches → Branch protection rules) y marcas el CI como check obligatorio, el botón de merge queda desactivado hasta que el workflow pasa en verde.
El cómo concreto del deploy —Vercel, GitHub Pages, variables de entorno en producción— es el capítulo siguiente.
Buenas prácticas de seguridad del pipeline#
Gestionar secrets correctamente es el primer paso, pero hay dos prácticas adicionales
que un pipeline serio debería incorporar. La primera es fijar las actions por el SHA del
commit en vez de por tag: uses: actions/checkout@<sha-del-commit> en lugar de @v6.
Un tag puede reasignarse a un commit malicioso sin que lo veas venir —como ocurrió en
varios incidentes de cadena de suministro en los últimos años—; el SHA es inmutable.
La segunda es añadir un paso de pnpm audit en el CI o activar Dependabot en el
repositorio para que GitHub avise automáticamente cuando una dependencia tenga una
vulnerabilidad conocida. El CI verifica que tu código funciona; estas dos prácticas
verifican que el entorno desde el que corre y las dependencias que usa son de confianza.
Comprueba lo que sabes#
Pregunta 1 de 7
Evalúa el siguiente código y responde:
on: [push, pull_request]Tu turno#
El ejercicio es un workflow de CI real: lo creas en tu repositorio local del Team Builder, haces commit y compruebas que GitHub lo detecta y ejecuta. Los ficheros de solución muestran el workflow completo de cada tier con comentarios que explican cada decisión. Haz el tuyo primero y luego compara.
Ejercicio · hazlo en local
CI con GitHub Actions para el Team Builder
Sobre el repositorio de tu Team Builder de React (el que vienes construyendo desde el Nivel 7), crea el fichero `.github/workflows/ci.yml` y comprueba que GitHub lo detecta y ejecuta automáticamente. Las soluciones muestran el workflow completo de cada tier con comentarios que explican cada decisión. Haz el tuyo primero y luego compara.
Paso 1: Portería mínima: tests en cada push y PR
- Creas `.github/workflows/ci.yml` con un job que instala las dependencias con `pnpm install --frozen-lockfile` y ejecuta `pnpm test`.
- El workflow se dispara en `push` y en `pull_request` (cualquier rama).
- Usas `actions/checkout@v6`, `pnpm/action-setup@v4` y `actions/setup-node@v6` en ese orden.
- Haces un commit y subes la rama a GitHub; compruebas en la pestaña Actions que el workflow se ejecuta y pasa.
- Si los tests fallan, el commit aparece con el semáforo en rojo y el merge del PR queda bloqueado.
Paso 2: Gate de calidad completo con caché
- Mismo workflow que el tier "ok", más lint (`pnpm lint`) y build (`pnpm build`) como steps adicionales.
- Activas la caché de dependencias de pnpm con `cache: 'pnpm'` en `actions/setup-node`.
- El orden de los steps es: checkout → instalar pnpm → configurar Node con caché → instalar dependencias → lint → tests → build.
- Puedes explicar por qué el build en CI puede fallar aunque en tu máquina funcione (entorno limpio, sin variables de entorno locales).
- Tras el segundo run, el paso de instalación tarda segundos en vez de un minuto porque reutiliza la caché.
Paso 3: Matrix, artifact y concurrencia
- Mismo workflow que el tier "mejor", con `strategy.matrix.node-version: [22, 24]` para correr el job en paralelo sobre las dos LTS vivas de Node (Node 22, mantenimiento; Node 24, activa).
- En el paso de `actions/setup-node`, usas `${{ matrix.node-version }}` para inyectar la versión de la matrix.
- Al final del job, subes `dist/` como artifact con `actions/upload-artifact@v4`; el nombre incluye la versión de Node (`dist-node-${{ matrix.node-version }}`).
- Configuras `concurrency` con `group: ci-${{ github.ref }}` y `cancel-in-progress: true` para que un push nuevo cancele el run anterior de la misma rama.
- En la pestaña Actions ves dos jobs en paralelo (uno con Node 22 y otro con Node 24) y el artifact disponible para descarga al terminar.
Cómo hacerlo en local
Clona el repositorio del curso, entra en la carpeta del ejercicio y abre el
index.html en tu navegador. Toda tu solución va en
solucion.js.
git clone <repo>
cd exercises/nivel-9/ci-cd-github-actions
# abre index.html en el navegador y edita solucion.js Ver soluciones
# Tier ok — portería mínima
# Un solo job que instala dependencias y corre los tests.
# Si los tests fallan, GitHub marca el commit en rojo y bloquea el merge del PR.
# Límite de este tier: no lintea ni construye el bundle, así que un fallo de
# ESLint o un error de build pasan desapercibidos hasta que alguien los ve en local.
# Nombre visible en la pestaña "Actions" de GitHub
name: CI
# Cuándo se ejecuta este workflow:
# push — en cada commit que sube a cualquier rama
# pull_request — cuando se abre o actualiza un PR
on: [push, pull_request]
# Permisos mínimos: este workflow solo lee el código, no publica nada.
permissions:
contents: read
jobs:
# Nombre del job; se ve en el log de Actions
test:
# Runner: una máquina virtual Ubuntu que GitHub provisiona gratis
runs-on: ubuntu-latest
steps:
# Paso 1: descarga el código del repositorio en la máquina virtual
- name: Obtener el código
uses: actions/checkout@v6
# Paso 2: instala pnpm en la máquina virtual antes de usarlo
- name: Instalar pnpm
uses: pnpm/action-setup@v4
with:
# Versión de pnpm que se instala; debe coincidir con la de tu proyecto
version: 9
# Paso 3: instala Node.js con la versión especificada
- name: Configurar Node.js
uses: actions/setup-node@v6
with:
# Node 24 es la LTS activa en 2026
node-version: 24
# Paso 4: instala las dependencias exactas del lockfile
# --frozen-lockfile falla si el lockfile no está actualizado,
# lo que evita que entre código con dependencias inconsistentes
- name: Instalar dependencias
run: pnpm install --frozen-lockfile
# Paso 5: ejecuta los tests con Vitest
# Si algún test falla, este paso devuelve código de salida distinto de 0
# y GitHub marca el run completo como fallido
- name: Ejecutar tests
run: pnpm test Por qué este nivel
- La portería mínima que cualquier proyecto profesional debería tener: cada push y cada PR activan el CI y, si los tests fallan, el merge queda bloqueado. Es simple y eficaz.
- Su límite: no lintea ni construye el bundle. Un fallo de ESLint o un error de TypeScript en el build pasan desapercibidos hasta que alguien los descubre en local. El tier "mejor" cierra esos huecos.
# Tier mejor — gate de calidad completo con caché
# Supera al tier ok en dos aspectos:
# 1. Añade linting y build como gates obligatorios, no solo los tests.
# Un fallo de ESLint o un error de TypeScript en el build ya no pueden llegar a main.
# 2. Activa la caché de dependencias de pnpm: el primer run descarga todo,
# los siguientes reutilizan lo cacheado y pasan de ~60 segundos a ~5 segundos
# en el paso de instalación.
# Límite de este tier: solo se prueba en una versión de Node, así que una
# incompatibilidad con una versión distinta pasa desapercibida.
name: CI
on: [push, pull_request]
# Permisos mínimos: este workflow solo lee el código, no publica nada.
permissions:
contents: read
jobs:
ci:
runs-on: ubuntu-latest
steps:
- name: Obtener el código
uses: actions/checkout@v6
- name: Instalar pnpm
uses: pnpm/action-setup@v4
with:
version: 9
# La caché funciona así: actions/setup-node calcula un hash del lockfile de pnpm.
# Si ese hash ya existe en la caché de GitHub, restaura la carpeta de módulos
# antes de que pnpm install se ejecute, y la instalación solo descarga
# los paquetes que hayan cambiado desde la última vez.
- name: Configurar Node.js con caché de pnpm
uses: actions/setup-node@v6
with:
# Node 24 es la LTS activa en 2026
node-version: 24
# cache: 'pnpm' activa la integración de caché automática para pnpm
cache: 'pnpm'
- name: Instalar dependencias
run: pnpm install --frozen-lockfile
# El linter caza problemas de estilo y errores comunes antes de que
# lleguen a revisión de código; es el gate más barato de ejecutar
- name: Linting
run: pnpm lint
- name: Ejecutar tests
run: pnpm test
# Por qué un build en CI puede fallar aunque en local funcione:
# en tu máquina tienes variables de entorno configuradas, rutas de módulos
# cacheadas y quizá dependencias globales instaladas que el entorno de CI
# no tiene. El build en CI parte de cero y reproduce lo que ocurriría
# en el servidor de producción. Si aquí falla, en producción también fallaría.
- name: Construir bundle de producción
run: pnpm build Por qué es mejor que el anterior
- Los tres gates que un proyecto serio necesita: lint, tests y build. Añadir la caché de pnpm hace que el segundo run y los siguientes pasen el paso de instalación en segundos en vez de en un minuto.
- Su límite: solo se prueba en una versión de Node. Una incompatibilidad con Node 22 pasa desapercibida si el entorno de producción usa una versión diferente a la del CI. El tier "excelente" resuelve esto con matrix.
# Tier excelente — matrix, artifact y concurrencia
# Supera al tier mejor en tres aspectos:
# 1. Matrix: los tests y el build corren en paralelo sobre Node 22 y Node 24.
# Node 22 es la LTS de mantenimiento y Node 24 la LTS activa (Node 20 quedó fuera
# de soporte desde abril de 2026); por eso se prueba sobre las dos LTS vivas.
# Si una API de Node cambia de comportamiento entre versiones, lo sabes antes
# de que llegue a producción. Cada combinación aparece como un job separado
# en la pestaña Actions con su propio log.
# 2. Artifact: el dist/ generado se sube a GitHub al final del job.
# Cualquier persona del equipo puede descargarlo desde la interfaz sin tener
# que hacer checkout y construir en local. Es la base del paso siguiente: CD.
# 3. Concurrencia: si haces tres pushes seguidos a la misma rama, solo el último
# importa. cancel-in-progress: true cancela los runs anteriores en cuanto
# llega uno nuevo, evitando malgastar minutos de CI en código ya superado.
# Nota: el paso de CD (publicar el dist en un servidor o en GitHub Pages) llega
# en el capítulo siguiente; este workflow termina en el artifact.
name: CI
# cancel-in-progress cancela el run anterior de la misma rama cuando llega un push nuevo.
# group construye una clave única por rama: si dos pushes van a la misma rama,
# el segundo cancela el primero.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
on: [push, pull_request]
# Permisos mínimos: este workflow solo lee el código, no publica nada.
permissions:
contents: read
jobs:
ci:
runs-on: ubuntu-latest
# strategy.matrix define una rejilla de combinaciones que GitHub ejecuta en paralelo.
# Con dos versiones de Node se lanzan dos jobs simultáneos: uno con Node 22 y otro con Node 24.
# Si añades más ejes (sistema operativo, versión de pnpm...) el número de jobs crece
# multiplicativamente, así que no abuses: dos o tres combinaciones ya cubren lo esencial.
strategy:
matrix:
node-version: [22, 24]
steps:
- name: Obtener el código
uses: actions/checkout@v6
- name: Instalar pnpm
uses: pnpm/action-setup@v4
with:
version: 9
# ${{ matrix.node-version }} inyecta el valor actual de la matrix en este job.
# En el job con Node 22 vale "22"; en el job con Node 24 vale "24".
- name: Configurar Node.js ${{ matrix.node-version }} con caché de pnpm
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- name: Instalar dependencias
run: pnpm install --frozen-lockfile
- name: Linting
run: pnpm lint
- name: Ejecutar tests
run: pnpm test
- name: Construir bundle de producción
run: pnpm build
# actions/upload-artifact sube el contenido de dist/ al almacenamiento de GitHub.
# El artifact queda accesible en la pestaña Actions durante 90 días por defecto.
# name incluye la versión de Node para diferenciar los artifacts de cada job de la matrix.
- name: Subir dist/ como artifact
uses: actions/upload-artifact@v4
with:
name: dist-node-${{ matrix.node-version }}
# path indica qué carpeta o fichero se sube
path: dist/
# retention-days controla cuántos días se conserva el artifact en GitHub
retention-days: 7 Por qué es mejor que el anterior
- El workflow de un proyecto serio: matrix para cazar incompatibilidades entre versiones de Node, artifact para que el equipo descargue el build sin tener que construirlo en local, y concurrencia para no malgastar minutos de CI en código ya superado.
- El artifact es además la base del siguiente paso: CD. En el capítulo siguiente, en vez de subir el dist/ a GitHub y dejarlo ahí, lo publicarás en un servidor o en GitHub Pages de forma automática cuando el CI pase en main.