Build
Lo primero que necesitamos es ejecutar el build de nuestro proyecto y para eso vamos a utilizar stages para dividir las etapas. Conviene recordar que el orden de los stages importa.
Para hacer el build vamos a necesitar instalar las dependencias, igual que lo hicimos para ejecutar el proyecto, es decir, todo en la misma secuencia, como si fuésemos a ejecutar el proyecto localmente.
Existen dos métodos para hacer la instalación de las dependencias.
npm install: instalará las dependencias de acuerdo con lo que está en el package.jsonnpm ci: instala las dependencias utilizando el archivo package-lock.json sin hacer ninguna alteración respetando exactamente las versiones definidas y garantiza que tengamos una compilación replicable. Además, utilizar npm ci es más rápido que npm install
En nuestro escenario como es un proyecto pequeño cualquier cosa funcionaría, pero es bueno conocer las mejores prácticas. Entonces vamos a utilizar npm ci.
default:
tags:
- general
stages:
- build
build:
image: node:22-slim
script:
- node --version
- npm --version
# - npm install # Instalación de las dependencias
- npm ci # Instalación de las dependencias generará la carpeta node_modules
- npm run build # Generará la carpeta build
Las carpetas que se generen se perderán al final del job, entonces necesitamos guardar lo que nos interesa para uso futuro, por ejemplo crear una imagen docker o cualquier otra cosa.
La única carpeta que utilizaremos es la carpeta build generada en el proyecto. La carpeta node_modules para este escenario no es importante.
Artifacts Advanced
Vamos a profundizar un poco más en artifacts ahora antes de continuar. Si no pasamos el nombre del artefacto generado por este job recibirá el nombre default.
-
Podemos pasar un archivo específico o incluso carpetas enteras utilizando paths y podemos utilizar comodines para filtrar lo que vamos a pasar.
...
artifacts:
name: "job1-artifacts-file"
paths:
- build/ # carpeta completa
- archivo # un archivo específico
- teste/*xyz/* # todo dentro de la carpeta teste, pero sólo las carpetas que terminan con xyz -
Pero no siempre queremos pasar todo lo que está en una carpeta y no necesitamos pasar camino por camino. Si hay algo específico que queremos excluir es posible usando exclude.
...
artifacts:
paths:
- binaries/ # Toda la carpeta binaries
exclude:
- binaries/**/*.o # Pero archivos terminados en .o no, en cualquier nivel, por el **
-
Podemos definir un tiempo para que ese artefacto sea eliminado utilizando
expire_in. Si no se define, el tiempo por defecto es de 30 días. A continuación algunos ejemplos con varias opciones de cómo poder utilizar. Podemos usar sólo minutos si queremos y poner 720 minutos en lugar de 12 horas.expire_in: 1 week # singular
expire_in: 2 weeks # plural
expire_in: 5 days # plural
expire_in: 1 day # singular
expire_in: 7 hours # plural
expire_in: 30 minutes
expire_in: 1 week 2 hours
expire_in: 1 day 1 hour 30 minutes # Día y hora en singular para 1.
expire_in: 1 month # singular
expire_in: 2 months # plural -
No es posible limitar acceso a un usuario o grupo de estos artefactos. Esta es una configuración a nivel de repositorio y no de pipeline.
...
artifacts:
name: "build-artifacts"
paths:
...Valor de when Descripción on_success Genera artefactos sólo si el job es exitoso (valor por defecto). always Genera artefactos independientemente del éxito o fallo del job. on_failure Genera artefactos sólo si el job falla. manual Genera artefactos sólo cuando el job se ejecuta manualmente. - always: es útil para generar artefactos de diagnóstico (como logs) incluso cuando el job falla.
- on_failure: Ideal para capturar artefactos que son útiles sólo cuando el job falla, como logs de error o informes de fallo.
Artefacts Paths vs Artefact Reports
El reports en GitLab CI/CD es una funcionalidad especial que permite asociar artefactos a tipos específicos de informes para que GitLab trate estos archivos de una manera especial, ofreciendo funcionalidades adicionales. Esto es particularmente útil cuando quieres que GitLab sepa que determinado artefacto tiene un significado específico, como informes de cobertura de tests, informes de seguridad o variables de entorno, y quieres que estos archivos sean procesados y visualizados de una manera estructurada. Definimos estos tipos de cosas aquí
artifacts:
when: on_success # Ya es el valor por defecto
expire_in: "1 hour"
paths:
...
reports: # Veremos más adelante.
...
Lo único que creo que podemos inyectar en nuestro pipeline y explorar por ahora son variables de entorno que podemos (no necesitamos), probar en el futuro.
En medio de nuestro pipeline vamos a generar una variable de entorno que después será cargada en otro job y vamos a probar.
Entonces para un punto inicial creo que el pipeline de build podría ser así.
default:
tags:
- general
stages:
- build
build:
image: node:22-slim
script:
- node --version
- npm --version
- npm ci # Instalación de las dependencias
- npm run build # Generará la carpeta .build
- echo "ACCESS_TOKEN=abc123" > test.env # Una prueba sólo para el reports
artifacts:
when: on_success # Ya es el valor por defecto
expire_in: "1 hour"
paths:
- build/ # La carpeta completa sin exclusiones
reports:
dotenv: test.env # Dentro de este archivo tenemos ACCESS_TOKEN=abc123
Sobre el reports, no estamos cargando una variable de entorno en este job, pero futuros jobs que dependen de estos artefactos deben venir con la variable ACCESS_TOKEN cargada en el terminal. Hablaremos más sobre variables después.
❯ git checkout -b pipe/build
❯ git add .gitlab-ci.yml
❯ git cm "add build"
❯ git push origin pipe/build

Si tenemos un job y stages, el job necesita tener su stage definido.
default:
tags:
- general
stages:
- build
build:
stage: build #### <<<<<
image: node:22-slim
script:
- node --version
- npm --version
- npm ci # Instalación de las dependencias
- npm run build # Generará la carpeta .build
- echo "ACCESS_TOKEN=abc123" > .env # Una prueba sólo para el reports
artifacts:
when: on_success # Ya es el valor por defecto
expire_in: "1 hour"
paths:
- build/ # La carpeta completa sin exclusiones
reports:
dotenv: .env
Rehaciendo el commit.
❯ git checkout -b pipe/build
❯ git add .gitlab-ci.yml
❯ git cm "add build"
❯ git push origin pipe/build


Y aquí todo el proceso del build.
Running with gitlab-runner 17.11.0 (0f67ff19)
on general-debian jyvyfkmfg, system ID: r_szdZCOX2meST
Preparing the "docker" executor
00:09
Using Docker executor with image node:22-slim ...
Using locally found image version due to "if-not-present" pull policy
Using docker image sha256:a7bca975c7f3a862dc60f3d8aaa3862fce3208066dd3567f060381b506f38402 for node:22-slim with digest node@sha256:157c7ea6f8c30b630d6f0d892c4f961eab9f878e88f43dd1c00514f95ceded8a ...
Preparing environment
00:01
Running on runner-jyvyfkmfg-project-69186599-concurrent-0 via 1d8224d47375...
Getting source from Git repository
00:02
Fetching changes with git depth set to 20...
Reinitialized existing Git repository in /builds/puziol/learn-gitlab-app/.git/
Created fresh repository.
Checking out 36c04930 as detached HEAD (ref is pipe/build)...
Skipping Git submodules setup
Executing "step_script" stage of the job script
00:14
Using docker image sha256:a7bca975c7f3a862dc60f3d8aaa3862fce3208066dd3567f060381b506f38402 for node:22-slim with digest node@sha256:157c7ea6f8c30b630d6f0d892c4f961eab9f878e88f43dd1c00514f95ceded8a ...
$ node --version # Nuestros primeros comandos
v22.15.0
$ npm --version
10.9.2
$ npm ci # Instalación de los paquetes
added 440 packages, and audited 441 packages in 10s
152 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
$ npm run build
> [email protected] build
> vite build
vite v6.3.2 building for production...
transforming...
✓ 31 modules transformed.
rendering chunks...
computing gzip size... # Véase a continuación las carpetas que fueron generadas.
build/index.html 0.47 kB │ gzip: 0.30 kB
build/assets/react-CHdo91hT.svg 4.13 kB │ gzip: 2.05 kB
build/assets/index-n_ryQ3BS.css 1.39 kB │ gzip: 0.71 kB
build/assets/index-BcKvuBhg.js 147.40 kB │ gzip: 47.66 kB
✓ built in 1.47s
$ echo "ACCESS_TOKEN=abc123" > .env
Uploading artifacts for successful job
00:04
Uploading artifacts... # Carga del build
build/: found 7 matching artifact files and directories
Uploading artifacts as "archive" to coordinator... 201 Created id=9845340606 responseStatus=201 Created token=eyJraWQiO
Uploading artifacts... # Carga del .env
.env: found 1 matching artifact files and directories
Uploading artifacts as "dotenv" to coordinator... 201 Created id=9845340606 responseStatus=201 Created token=eyJraWQiO
Cleaning up project directory and file based variables
00:01
Job succeeded
Cuando hacemos la descarga allá en la página del pipeline siempre recibiremos un archivo artifact.zip. Esto es porque no declaramos el nombre de nuestro artefacto. Lo haremos más tarde, cuando entendamos de variables específicas del gitlab-ci.
artifacts:
name: artifacts-[job_name]-[commit_sha] # éste sería un buen nombre... pero aún no sabemos cómo hacer esto, así que espera las escenas de los próximos capítulos.
Vale la pena mencionar que varios pipelines pueden estar en ejecución al mismo tiempo en este mismo código y cada uno tiene sus artefactos específicos.