Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

if condition variables in gitlab-ci.yml

I have a simple request but can't find any sample code for it.

Suppose I want to set an environment variable depending on the branch name. Something like this (though I know this code doesn't work)

variables:
  rules:
    - if: '$CI_COMMIT_BRANCH != "master"'
      variables:
        env: "dev"
    - if: '$CI_COMMIT_BRANCH == "master"'
      variables:
        env: "prod"
  stackName: projectA-${env}

So the stackName or other variables can use ${env} as suffix in the name and I can also use ${env} in jobs , scripts or stages

How can I set it?

like image 460
Bill Avatar asked Dec 17 '25 15:12

Bill


2 Answers

The variable env or stackName can be set in two different job, which run with a rule according the branch. And you can define a template job, to make same thing for both environment. For example, define two jobs like this with a template :

.template-job:
  script:
    - echo $STACK_NAME

prod-job:
  variables:
    STACK_NAME: projectA-prod
  rules:
    - if: '$CI_COMMIT_BRANCH == "master"'
  extends: .template-job

dev-job:
  variables:
    STACK_NAME: projectA-dev
  rules:
    - if: '$CI_COMMIT_BRANCH != "master"'
  extends: .template-job

Using this, you have two jobs but they extends the same template-job, which is using the variable $STACK_NAME. You can of course name it ENV or whatever.

like image 79
fmdaboville Avatar answered Dec 20 '25 07:12

fmdaboville


A general feature for setting variables based on conditions involving other variables is not provided in GitLab's CI YAML syntax. There are some workarounds which may or may not apply to your current requirement.

This is a dummy job to verify the variables:

main:
  stage: build
  script:
    - echo "Building in ${env} using stack ${stackName}"

Using a dotenv report

The most general solution to conditionally setting variables is leaving the declarative gitlab .yml file and use a job that generates a dotenv report, like this (assuming a docker executor. You may as well use a shell executor, possibly adapting the syntax to the shell you are using):

setvars:
  stage: .pre
  script:
    - |
      env=dev
      if [[ "$CI_COMMIT_BRANCH" == main-dotenv ]]
      then
        env=prod
      fi
      echo env=$env >> .env
      echo stackName=projectA-$env >> .env
  artifacts:
    reports:
      dotenv: .env

You can implement arbitrarily complex logic in Jobs creating dotenv reports. You can have multiple jobs that generate dotenv reports, all of the reports will be merged and available in later stages, and that's why I put that job into the .pre stage. See it in action on a main branch or a dev branch

Using include files

Staying inside .gitlab-ci.yml to define the variable values instead of using a Job removes one layer of complication. While you can't conditionally set variables, you can conditionally include YAML files that can conditionally set variables.

include:
  - local: ci/prod.yml
    rules:
      - if: $CI_COMMIT_BRANCH == "main-include"
  - local: ci/dev.yml
    rules:
      - if: $CI_COMMIT_BRANCH != "main-include"

variables:
  stackName: projectA-${env}

Again, you can implement setting of different variables based on different conditions using multiple include items, each having its own set of rules.

See it in action on a a main branch or a dev branch

Using workflow rules

While this is likely the most simple way to get the specific task done you give in your question, it has the most severe limitiations. The idea has already been presented in https://stackoverflow.com/a/78756108 :

workflow:
  rules:
    - if: $CI_COMMIT_BRANCH == "main-workflow"
      variables:
        env: prod
    - variables:
        env: dev

variables:
  stackName: projectA-${env}

See it in action on a a main branch or a dev branch

If this approach fits your use case, it's fine, there is nothing that prevents you from doing it this way.

Limit 1: Only one kind of condition supported

You should be aware though that there is only one set of workflow rules, while there are multiple sets of include rules. For any set of rules, GitLab always chooses the first "matching" rule and ignores all subsequent rules. So imagine you don't just want a variable env set based on a computation derived from $CI_COMMIT_BRANCH, but you also need a variable arcanicity set depending on the user inputting the expected magic word in a pipeline variable. The new demonstration job now looks like

variables:
  MAGIC_WORD:
    description: "Your chance to utter a magic word"
    value: lame

main:
  stage: build
  script:
    - echo "Building in ${env} using stack ${stackName} in an ${arcanicity} job"

This job is meant to use ${env} and ${stackName} set conditionally on $CI_COMMIT_BRACNH, as well as ${arcanicity} set conditionally on $MAGIC_WORD.

You can easily extend the dotenv-based solution like this:

setvars:
  stage: .pre
  script:
    - |
      env=dev
      if [[ "$CI_COMMIT_BRANCH" == main-dotenv ]]
      then
        env=prod
      fi
      echo env=$env >> .env
      echo stackName=projectA-$env >> .env
      if [[ "$MAGIC_WORD" == "xyzzy" ]]
      then
        echo arcanicity=magic >> .env
      else
        echo arcanicity=profane >> .env
      fi
  artifacts:
    reports:
      dotenv: .env

See it in action in a normal and a magic job.

You can also apply this idea to the include-based solution:

include:
  - local: ci/prod.yml
    rules:
      - if: $CI_COMMIT_BRANCH == "main-include"
  - local: ci/dev.yml
    rules:
      - if: $CI_COMMIT_BRANCH != "main-include"
  - local: ci/profance.yml
    rules:
      - if: $MAGIC_WORD != "xyzzy"
  - local: ci/magic.yml
    rules:
      - if: $MAGIC_WORD == "xyzzy"


variables:
  stackName: projectA-${env}

See it in action in a normal and a magic job.

There is no way to express the same idea in the workflow-based variant, because as soon as the workflow rule that matches on $CI_COMMIT_BRANCH == main applied and provided the appropriate value for env, further workflow rules will not be considered, so a later rule that depends on $MAGIC_WORD is not evaluated.

Limit 2: Interaction with other uses of workflow rules

The primary intention of workflow rules is not the conditional setting of variables, but deciding whether the pipeline should be run at all given certain conditions. In case the decision is to run the pipeline (i.e. the rule that applies has when: always or doesn't specify when at all), one set of variables may be injected "as a side effect of choosing this reason to run a pipeline". A prominent example to use workflow rules to control pipeline creation is switching a project to merge-request pipelines for branches in merge requests but run "normal" pipelines on tags and commits to the main branch using

workflow:
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
    - if: $CI_COMMIT_TAG
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

This snippet contains three rules that start a pipeline, but do not set variables. If you put the snippet that sets variables before this snippet like

workflow:
  rules:
    - if: $CI_COMMIT_BRANCH == "main-workflow"
      variables:
        env: prod
    - if: $CI_COMMIT_BRANCH != "main-workflow"
      variables:
        env: dev
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
    - if: $CI_COMMIT_TAG
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

one of the first two conditions will always match, so the block to create merge request pipeline is not reached. On the other hand, if you place the variable setting stuff below the "actual" workflow stuff like

workflow:
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
    - if: $CI_COMMIT_TAG
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_COMMIT_BRANCH == "main-workflow"
      variables:
        env: prod
    - if: $CI_COMMIT_BRANCH != "main-workflow"
      variables:
        env: dev

you will get completely undesired behaviour: In case of merge-request pipelines, tag pipelines and pushes the the main branch, the pipeline is executed as intended, but without the variables being set. On the other hand, for branch pipelines on branches that are part of a merge request, the original workflow snippet had no matching rule which will default into not running the branch pipeline (as the merge-request pipeline is good enough in many cases). With this addition, either one of the variable-setting rules will match, and the jobs that are supposed to be suppressed are executed anyway, and these undesired jobs are the only jobs that have ${env} set.

If the limits don't matter

Given these limits of the approach based on workflow rules, you should make the use of workflow rules for variable setting obvious in your YAML configuration, which in my oppinion means you should not hide it inside an include file. Whoever is going to edit the pipeline after you to edit the workflow should be aware that there already is a workflow definition just used to set variables.

like image 39
Michael Karcher Avatar answered Dec 20 '25 09:12

Michael Karcher



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!