Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for GitLab CI/CD By James Joyner IV · · 10 min read

A GitLab CI rules:if Cookbook Built on Predefined Variables

GitLab exposes dozens of predefined CI variables. Here's a practical rules:if cookbook that uses them to run the right jobs on tags, MRs, default branch, and scheduled runs.

  • #gitlab-cicd
  • #ai
  • #rules
  • #variables
  • #workflow

Most rules:if mistakes come from not knowing which predefined variable describes the situation you’re trying to match. People reach for $CI_COMMIT_BRANCH when they mean “is this an MR pipeline,” or check $CI_COMMIT_TAG when the cleaner signal is $CI_PIPELINE_SOURCE. The result is rules that work in testing and surprise you in production. This is the cookbook I keep handy — common conditions matched to the right predefined variable, with copy-paste rules:if blocks.

The variables worth memorizing

A handful of predefined variables cover the vast majority of routing decisions:

  • $CI_PIPELINE_SOURCEwhy the pipeline ran: push, merge_request_event, schedule, web, trigger, pipeline, api.
  • $CI_COMMIT_BRANCH — set on branch pipelines, empty on MR pipelines and tags.
  • $CI_COMMIT_TAG — set only when building a tag.
  • $CI_DEFAULT_BRANCH — your repo’s default branch name, so rules stay portable.
  • $CI_MERGE_REQUEST_* — populated only in merge-request pipelines.
  • $CI_COMMIT_REF_PROTECTED"true" on protected refs.

The trap that catches everyone: $CI_COMMIT_BRANCH is empty in an MR pipeline. If you write if: '$CI_COMMIT_BRANCH == "main"' expecting it to fire during a merge request targeting main, it won’t.

Recipe 1: only on the default branch

deploy-staging:
  stage: deploy
  script: ./deploy.sh staging
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'

Using $CI_DEFAULT_BRANCH instead of a hardcoded "main" means this keeps working if you rename the branch. Small thing, saves a future headache.

Recipe 2: only on tags (releases)

publish-release:
  stage: release
  script: ./publish.sh
  rules:
    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'

Anchoring to a semver pattern means a stray nightly tag won’t accidentally publish a release.

Recipe 3: merge request pipelines only

review-app:
  stage: review
  script: ./deploy-review.sh
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

This is the correct signal for “we’re in an MR pipeline” — far more reliable than poking at branch variables.

Recipe 4: scheduled (nightly) runs only

nightly-scan:
  stage: scan
  script: ./full-scan.sh
  rules:
    - if: '$CI_PIPELINE_SOURCE == "schedule"'

Pair this with a named schedule and a custom variable if you have several schedules that should each trigger different jobs.

Recipe 5: protected-branch-only secrets work

deploy-production:
  stage: deploy
  script: ./deploy.sh production
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_REF_PROTECTED == "true"'
      when: manual

Checking $CI_COMMIT_REF_PROTECTED ensures a job that needs protected variables never even tries to run somewhere those variables won’t exist. Combine it with when: manual for a deploy gate.

Recipe 6: skip duplicate branch+MR pipelines

The most common workflow:rules pattern stops a push from creating both a branch pipeline and an MR pipeline:

workflow:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
      when: never
    - if: '$CI_COMMIT_BRANCH'

This says: always run MR pipelines; suppress the branch pipeline when an MR is already open for that branch; otherwise run the branch pipeline. It’s the antidote to the duplicate-pipeline noise everyone hits.

Let AI assemble the rules — then verify the matrix

Predefined variables interact in ways that are easy to get subtly wrong, so I draft complex rule sets with an LLM and then test them. A prompt I reuse:

Prompt: “Write GitLab CI rules: for a job that should run: on merge requests targeting the default branch, on the default branch after merge, and on semver tags — but NOT on scheduled pipelines or on feature-branch pushes that have no open MR. Use only predefined variables, anchor the tag to semver, and use $CI_DEFAULT_BRANCH not a literal. Then give me a test matrix: for each trigger (MR-to-main, push-to-feature-no-MR, push-to-main, tag v1.2.3, schedule), state whether the job runs.”

The test matrix is the part that matters:

Output (excerpt):

TriggerJob runs?
MR targeting mainyes
push to feature, no MRno
push to mainyes
tag v1.2.3yes
scheduled runno

Don’t trust that table — generate the real pipelines and confirm each row. The model reasons about rules: better than most humans, but it still occasionally mixes up the empty-on-MR behavior of $CI_COMMIT_BRANCH. The matrix gives you something concrete to check against the pipeline’s created/skipped view.

Where to go deeper

Once the if conditions are right, the next layer is path-scoping with changes: so jobs only run when relevant files move, and gating whole pipelines with workflow:rules. I’ve covered both in the GitLab CI/CD category, and the reusable rules:if expression cookbook prompt is where I keep the parameterized version of these recipes.

The bottom line

Good rules:if is mostly about picking the variable that actually describes your situation: $CI_PIPELINE_SOURCE for why a pipeline ran, $CI_COMMIT_TAG for tags, $CI_DEFAULT_BRANCH for portability, and $CI_COMMIT_REF_PROTECTED for “is this trusted.” Remember that $CI_COMMIT_BRANCH is empty in MR pipelines, anchor your tag patterns, and always validate the rule set against a trigger-by-trigger matrix before you ship it. Let AI draft; let the pipeline graph confirm.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.