GitLab CI Rules – Change Pipeline Workflow
GitLab’s rules keyword gives you control over whether a job runs or gets skipped. You build these rules from conditions that check variables and events.
Comparing Variables
Use == and != to compare a variable against a string. Quotes work either way around:
if: $VAR == "some value"
if: $VAR != "some value"
if: "some value" == $VAR
Two special cases worth knowing:
$VAR == nullchecks if the variable is undefined$VARalone checks if it exists (is defined and non-empty)
Regex Matching
GitLab supports regex operators =~ and !~ using RE2 syntax. The =~ operator returns true when it finds a match; !~ returns true when there is no match.
if: $VAR =~ /^string.*/
if: $VAR1 !~ /^string.*/
Regex is case-sensitive by default. Append the i flag to make it case-insensitive:
if: $VAR =~ /content/i
Deciding When a Job Runs
Without any rule specified, GitLab runs the job by default. Several keywords control job execution:
rules- define conditions for including or excluding a jobonly/except- legacy keywords that still work, butrulesgives you more flexibilityneeds- make a job wait for its dependencies to finishwhen- control whether a job runs automatically or manually
You can combine these with GitLab’s predefined variables or your own custom variables.
Writing Rules
The rules keyword is where things get flexible. GitLab evaluates your rules from top to bottom and stops at the first match. When a rule hits, the job either runs or gets skipped based on that rule’s configuration.
Here’s an example:
job:
script: python script.py
rules:
- if: '$CI_COMMIT_REF_NAME == "branch_name"'
when: manual
allow_failure: true
- if: '$CI_COMMIT_REF_NAME == "another_branch"'
If the pipeline is for branch_name, the first rule matches. GitLab adds the job to the pipeline but marks it as manual, so it waits for someone to trigger it. The allow_failure: true means the pipeline continues even if nobody triggers this job.
If the branch is not branch_name, GitLab skips the first rule and evaluates the second. When the branch is another_branch, the job runs automatically with the defaults (when: on_success, allow_failure: false).
If no rule matches, the job does not run at all.
You can flip this logic easily. Change when: manual to when: never and the job gets excluded when the branch name matches.
Manual Triggers
The manual value for when is useful for deployment jobs. You do not want code getting deployed to production every time someone pushes a WIP commit. Set the job to when: manual and the pipeline proceeds automatically until it hits that gate, where it waits for a human to click run.
For an example of manual deployment in action, see how to run Terraform from GitLab CI.
Combining Keywords
You can mix keywords within a single rule. GitLab treats them as a logical AND, so all of them must be satisfied for the rule to match. For monorepo pipelines, combining rules with parallel matrix builds lets you trigger targeted jobs per module only when relevant files change.
Running on File Changes
Use changes to trigger a job only when specific files change:
variables:
MODE: BUILD
build:
script: mvn package -U
rules:
- if: '$MODE == "BUILD"'
changes:
- src/main/java/*
- src/scripts/*
when: manual
When $MODE equals BUILD and any file in src/main/java or src/scripts was modified, the job becomes a manual trigger.
Checking if Files Exist
Use exists to run a job only when certain files are present in the repository:
variables:
VERSION: v1
job:
script: docker build -t app:$VERSION .
rules:
- exists:
- Dockerfile
If a Dockerfile exists anywhere in the project, the job runs. The paths are relative to the project root and support glob patterns. If you list multiple files, the rule matches when any of them exist.
Combining OR Logic
GitLab 13.3 and later support || and && within if conditions:
compile:
script:
- make && make install
rules:
if: ($CI_COMMIT_BRANCH == "release" || $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH) && $ANOTHER_VARIABLE
Use parentheses to group conditions. Without them, operator precedence can bite you.
Skipping Jobs with except
The except keyword excludes a job from pipelines matching certain criteria:
lint:
script:
- lint src/
except:
variables:
- $CI_PIPELINE_SOURCE == api
This skips the lint job when the pipeline was triggered via the API. $CI_PIPELINE_SOURCE is one of several predefined variables that GitLab sets automatically based on what started the pipeline. Common values include push, merge_request_event, schedule, api, and web.
Detecting Merge Request Pipelines
Setting up environments for those deployments? See GitLab CI Environments and Review Apps for a complete walkthrough of environment-scoped pipelines and automatic review app creation.
To check if a pipeline belongs to a merge request, use only with refs: merge_requests:
build:
script: docker build -t app:v2 .
only:
refs:
- merge_requests
changes:
- Dockerfile
- scripts/**/*
This job runs only in merge request pipelines, and only when Dockerfile or files under scripts/ changed. The changes keyword here filters further based on what was actually modified in the commit.
Comments