GitLab CI Rules – Change Pipeline Workflow

Bits Lovers
Written by Bits Lovers on
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 == null checks if the variable is undefined
  • $VAR alone 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 job
  • only / except - legacy keywords that still work, but rules gives you more flexibility
  • needs - make a job wait for its dependencies to finish
  • when - 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.

Bits Lovers

Bits Lovers

Professional writer and blogger. Focus on Cloud Computing.

Comments

comments powered by Disqus