Gitlab Pipeline – rules – What is rules in GitLab CI/CD?

DevOps

MOTOSHARE 🚗🏍️
Turning Idle Vehicles into Shared Rides & Earnings

From Idle to Income. From Parked to Purpose.
Earn by Sharing, Ride by Renting.
Where Owners Earn, Riders Move.
Owners Earn. Riders Move. Motoshare Connects.

With Motoshare, every parked vehicle finds a purpose. Owners earn. Renters ride.
🚀 Everyone wins.

Start Your Journey with Motoshare

The rules: keyword is the recommended and most powerful way to control whether a job is added to a pipeline and under what conditions it should run. It allows for complex logic using CI/CD variables, file changes, and more, offering greater flexibility than the older only/except keywords.

A rules section is an array of individual rule objects. These rules are evaluated in order from top to bottom for each job. The first rule that matches determines the job’s behavior.


Example .gitlab-ci.yml with rules:

YAML

# .gitlab-ci.yml

stages:
  - build
  - test
  - security
  - deploy
  - report

default:
  image: alpine:latest

# Job 1: Runs only on pushes to the default branch (e.g., 'main' or 'master')
build_default_branch:
  stage: build
  script:
    - echo "--- Building for default branch: $CI_DEFAULT_BRANCH ---"
    - echo "Commit on branch: $CI_COMMIT_BRANCH"
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "push"'
      # 'when: on_success' is implied if 'when' is not specified for an 'if' rule

# Job 2: Runs only when a Git tag is pushed
deploy_on_tag:
  stage: deploy
  script:
    - echo "--- Deploying for tag: $CI_COMMIT_TAG ---"
  rules:
    - if: '$CI_COMMIT_TAG' # This condition is true if a tag is present
      when: on_success # Explicitly stating the default when job should run

# Job 3: Manual job for merge requests, allows failure
test_on_merge_request:
  stage: test
  script:
    - echo "--- Testing for Merge Request: $CI_MERGE_REQUEST_IID ---"
    - echo "Source branch: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"
    - echo "Target branch: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
    # - exit 1 # Uncomment to simulate a failure
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
      when: manual # Job will be created but needs to be started manually from the UI
      allow_failure: true # If this job fails, it won't cause the whole pipeline to fail

# Job 4: Runs only if files in the 'docs/' directory have changed on a push event
build_docs:
  stage: build
  script:
    - echo "--- Building documentation because files in 'docs/' changed ---"
  rules:
    - if: '$CI_PIPELINE_SOURCE == "push"' # Only consider push events for changes
      changes:
        - docs/**/* # Glob pattern for any file within the docs directory or its subdirectories
        - README.md
      # If 'changes' are detected and the 'if' condition is met, the job runs.

# Job 5: Runs only if a specific file (e.g., a Dockerfile) exists in the repository
docker_build_if_exists:
  stage: build
  script:
    - echo "--- Dockerfile found, proceeding with Docker build ---"
    # - docker build -t my-image . # Actual docker build command
  rules:
    - if: '$CI_COMMIT_BRANCH' # Ensure it runs for branch commits, not other events unless specified
      exists:
        - Dockerfile # Path to the file to check, relative to project root
        # - another-required-file.txt
      # If 'Dockerfile' exists and the 'if' condition is met, the job runs.

# Job 6: A job that is explicitly prevented from running in any 'push' pipeline, but could run for other types if rules were added
never_run_on_push:
  stage: report
  script:
    - echo "This job should not run on push events."
  rules:
    - if: '$CI_PIPELINE_SOURCE == "push"'
      when: never # Explicitly prevents the job from being added to the pipeline for push events
    # - if: '$CI_PIPELINE_SOURCE == "schedule"' # Example: but it could run for schedules
    #   when: always

# Job 7: A job with a delayed execution for non-critical tasks
delayed_notification:
  stage: report
  script:
    - echo "--- Sending a delayed notification ---"
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "push"'
      when: delayed
      start_in: '5 minutes' # Job will start 5 minutes after the pipeline completes this stage successfully

# Job 8: Catch-all rule if no other rules in this job match (optional)
# If a job has rules, and none of them match, the job is NOT added to the pipeline by default.
# To change this, you can add a final rule without an 'if' clause.
optional_job_always_manual:
  stage: report
  script:
    - echo "This is an optional manual job."
  rules:
    - if: '$CI_COMMIT_BRANCH == "feature/special-feature"' # Runs normally for this specific branch
    - when: manual # For all other cases where the above 'if' didn't match, make it manual

Code language: PHP (php)

Explanation:

  1. rules: Keyword:
    • Used within a job definition. It takes an array of rule objects.
    • Rules are evaluated in the order they are listed. The first rule that matches determines the job’s behavior.
    • If no rules match for a job, the job is not added to the pipeline by default.
  2. Structure of a Rule:Each item in the rules array is an object that can contain several clauses:
    • if:: An expression that is evaluated. If true, this rule matches (unless other conditions like changes or exists are also present and don’t match). You use CI/CD predefined variables here.
      • Operators: == (equals), != (not equals), =~ (regex match), !~ (regex no match), && (AND), || (OR – though multiple if clauses in separate rules act like OR for the job). Parentheses () can be used for grouping.
    • when:: Defines what to do if the rule matches.
      • on_success (default if if is present and when is omitted): Run the job automatically if the stage is reached.
      • manual: Add the job to the pipeline, but it requires manual action (clicking a play button) to start.
      • delayed: Run the job automatically after a specified duration once its stage is reached and preceding jobs are complete. Requires start_in.
      • never: Do not add the job to the pipeline if this rule matches.
      • always: Run the job regardless of the status of earlier stage jobs (use with caution, often combined with allow_failure: true).
    • changes:: An array of file paths or glob patterns. The rule matches only if files matching these patterns have changed in the latest push (compared to the previous commit on the same branch, or for MRs, compared to the target branch).
    • exists:: An array of file paths or glob patterns. The rule matches only if all specified files/paths exist in the repository at the commit being processed.
    • allow_failure:: (boolean, defaults to false) If true, the pipeline will continue to the next stage even if this job fails. This can be set per rule.
    • variables:: Define job-specific variables if this rule matches.
  3. build_default_branch (Job 1):
    • if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "push"': This job runs only if the commit is on the default branch AND the pipeline was triggered by a push event. The when: on_success is implied.
  4. deploy_on_tag (Job 2):
    • if: '$CI_COMMIT_TAG': This job runs if the $CI_COMMIT_TAG variable is set (meaning a Git tag was pushed).
  5. test_on_merge_request (Job 3):
    • if: '$CI_PIPELINE_SOURCE == "merge_request_event"': Runs for merge request events.
    • when: manual: It will appear in the pipeline but won’t run automatically.
    • allow_failure: true: If it fails after being run manually, the overall pipeline status won’t be marked as failed.
  6. build_docs (Job 4):
    • if: '$CI_PIPELINE_SOURCE == "push"': Condition based on pipeline source.
    • changes: - docs/**/* - README.md: The job only runs if there are changes in the docs directory or the README.md file for that push event.
  7. docker_build_if_exists (Job 5):
    • if: '$CI_COMMIT_BRANCH': A general condition to ensure it runs for branches.
    • exists: - Dockerfile: The job runs only if a Dockerfile exists at the root of the repository for the given commit.
  8. never_run_on_push (Job 6):
    • if: '$CI_PIPELINE_SOURCE == "push"' combined with when: never: This effectively blocks the job from running on any push event. You could add other rules below it to make it run for other events (e.g., schedules).
  9. delayed_notification (Job 7):
    • when: delayed and start_in: '5 minutes': This job will automatically start 5 minutes after its stage (report) is reached and preceding jobs in that stage (if any) are complete, but only for pushes to the default branch.
  10. optional_job_always_manual (Job 8):
    • The first rule if: '$CI_COMMIT_BRANCH == "feature/special-feature"' makes the job run normally (auto) for this specific branch.
    • The second rule when: manual has no if. This acts as a catch-all. If the first rule didn’t match, this second rule will match, and the job will be added as a manual job.

Benefits of rules::

  • Clarity: Conditions are often easier to read and understand than complex only/except combinations.
  • Power: Allows more complex conditions, including checks for file changes (changes), file existence (exists), and variable comparisons with regex.
  • Control: Provides fine-grained control over when a job runs (manual, delayed) and allow_failure on a per-rule basis.
  • Single Source of Truth: Replaces the need for both only and except.

rules: is a fundamental concept for modern GitLab CI/CD, allowing you to build highly efficient and precise pipelines.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x