Skip to main content

Rules

Antes de empezar a hablar de rules, debes saber que es una evolución de algo que todavía se utiliza pero está en desuso llamado only.

Antes se utilizaban dos cosas juntas (only y except) que hoy resolvemos solo con rules. Usa rules siempre que sea posible. only está en desuso.

Solo para entender el contexto de lo que teníamos antes...

job:
script: echo "Ejecutando job"
only:
- main # Ejecuta solo en la rama main
- tags

only: ejecuta el job solamente cuando el commit proviene de ciertas condiciones (rama, etiqueta, etc).

O except...

job:
script: echo "Ejecutando job"
except:
- develop

except: no ejecuta el job si cumple alguna de estas condiciones.

Limitaciones:

  • No tenemos if o lógica condicional usando only y except.
  • No podemos hacer lógicas combinadas utilizando las variables.

Así que olvídate de eso y vayamos directo a lo que nos da más poder, rules.

job:
script:
...
rules: [] # Es una lista
  1. Las reglas se evalúan en orden, la que haga match primero define el comportamiento del job.
  2. Súper personalizable con expresiones, variables, etiquetas, rama, MR, etc.

Una tabla rápida para entender la diferencia.

Aspectoonly / exceptrules
Soporte oficialObsoletoActual y recomendado
FlexibilidadBajaAlta
Condicional (if)No
Orden de evaluaciónLimitadaSecuencial y lógica
Detección MRLimitadaAvanzada
ExpresividadBajaAlta

Only y except solo están presentes por una cuestión de retrocompatibilidad y nada más.

Documentación Oficial Rules

Documentación Oficial Only y Except

La estructura de un bloque rules es la siguiente.

job:
script:
...
rules:
- if: <expresión>
when: [on_success | on_failure | manual | always | never | delayed]
start_in: <tiempo>
allow_failure: true/false
exists:
- <ruta/del/archivo>
changes:
- <archivo o carpeta>
variables:
- <variable>
Campo¿Obligatorio?Descripción
ifExpresión booleana usando variables de GitLab ($CI_*, etc).
whenDefine cuándo se ejecutará el job (on_success, manual, etc).
start_inUsado con when: delayed (ej: "5 minutes").
allow_failureSi es true, el fallo en el job no rompe el pipeline.
existsEjecuta el job si el/los archivo(s) existe(n).
changesEjecuta el job si los archivos listados fueron modificados.
variablesLista de variables obligatorias para que esta regla sea aplicada.

when

Tenemos un elemento importante ahí que además de estar dentro de rules también puede ser declarado fuera (directamente en el job) que es el when. Primero vamos a entender qué hace y después entender la diferencia entre usar en rules y fuera de él.

Valor de whenComportamiento
on_successEjecuta el job solamente si ningún job anterior falla. (Por defecto)
on_failureEjecuta el job solamente si algún job anterior falla.
neverNunca ejecuta el job, independientemente de cualquier condición. Solo puede ser usado en rules, al fin y al cabo no tendría sentido
alwaysEjecuta el job siempre, incluso si jobs anteriores fallan.
manualEl job queda esperando ejecución manual (aparece botón en la interfaz de GitLab).
delayedEl job se ejecuta con retraso, conforme al tiempo definido en start_in:.

Ejemplo de when: manual sería que alguien necesita aprobar el despliegue para ir a producción.

job_manual:
script: echo "Ejecutando el despliegue..."
when: manual

Ejemplo delayed + start_in sería dar un tiempo entre el despliegue en staging y el despliegue en producción para que dé tiempo a probar y si alguien percibe un error da tiempo a cancelar el job antes de que se ejecute. Si usas delayed, el start_in es obligatorio.

job_delayed:
script: echo "Ejecutando el despliegue..."
when: delayed
start_in: 10 minutes

Un ejemplo de always podría ser enviar una notificación al final del pipeline incluso si falla, por ejemplo enviar un email y una notificación en Slack. Si quieres que solo envíe si falla entonces podría ser on_failure.

Cuando declaramos el when fuera de las rules lo que estamos haciendo en realidad es colocando el mismo when para todas las rules. Esto sirve para dejar el rules más limpio.

job:
script: echo "Ejecutando"
when: manual
rules:
- if: Condición 1
- if: Condición 2

Es decir, es lo mismo que hacer esto.

job:
script: echo "Ejecutando"
rules:
- if: Condición 1
when: manual
- if: Condición 2
when: manual

Pero podemos sobrescribir para alguna regla específica. En este caso el job siempre es manual, pero si es en la rama develop entonces se ejecuta directamente sin intervención.

job:
script: echo "Desplegando"
when: manual
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_BRANCH == "staging"'
- if: '$CI_COMMIT_BRANCH == "develop"'
when: on_success

allow_failure

Lo mismo vale para allow_failure, que puede ser definido directamente en el nivel del job o individualmente dentro de cada regla en rules. Cuando se define en el nivel del job, el valor será aplicado como valor predeterminado para todas las rules.

Por defecto, allow_failure es false, es decir: si el job falla, el pipeline también será considerado con fallo. Cuando se define como true, el job puede fallar sin romper el pipeline, permitiendo que los próximos jobs continúen normalmente.

Esto es útil, por ejemplo, para pruebas experimentales, análisis estáticos o cualquier etapa no crítica que no quieras que bloquee el resto de la ejecución.

exists, changes y variables

Muchas veces el código sufre cambios que no cambian nada en el entorno, un excelente ejemplo de esto es hacer una alteración en el README.md. ¿Para qué vamos a hacer un despliegue?

En el bloque de rules, además de if, puedes usar exists y changes, que son formas de activar un job con base en la presencia o modificación de archivos.

job:
script: "Creando una imagen"
when: manual
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
exists:
- Dockerfile
- .dockerignore
job:
script: "Creando una imagen"
when: manual
rules: # Observa que puede existir sin el IF. tanto changes como exists.
- changes: # Si alguno de estos archivos sufre cambios
- src/**
- config/*.yaml

Ya el bloque variables, como vimos antes, sirve para definir variables personalizadas.

deploy:
stage: deploy
image: alpine
script:
- echo "Despliegue iniciando con TOKEN=$TOKEN"
environment:
name: $DEPLOY_ENV
rules:
- if: '$CI_COMMIT_BRANCH == "develop"'
variables:
DEPLOY_ENV: develop
- if: '$CI_COMMIT_BRANCH == "main"'