44

Depending on branch the build comes from I need to use slightly different command line arguments. Particularly I would like to upload snapshot nexus artifacts when building from a branch, and release artifact when building off master.

Is there a way to conditionally alter variables?

I tried to use except/only keywords like this

stages:
    - stage


variables:
    TYPE: Release


.upload_common:
    stage: stage
    tags: ["Win"]
    script: 
        - echo Uploading %TYPE%


.upload_snapshot:
    variables:
        TYPE: "Snapshot"
    except:
        - master


upload:
    extends: 
        - .upload_common
        - .upload_snapshot

Unfortunately it skips whole upload step when building off master.

The reason I am using 'extends' pattern here is that I have win and mac platforms which use slightly different variables substitution syntax ($ vs %). I also have a few different build configuration - Debug/Release, 32bit/64bit.

The code below actually works, but I had to duplicate steps for release and snapshot, one is enabled at a time.

stages:
    - stage


.upload_common:
    stage: stage
    tags: ["Win"]
    script: 
        - echo Uploading %TYPE%


.upload_snapshot:
    variables:
        TYPE: "Snapshot"
    except:
        - master

.upload_release:
    variables:
        TYPE: "Release"
    only:
        - master


upload_release:
    extends: 
        - .upload_common
        - .upload_release


upload_snapshot:
    extends: 
        - .upload_common
        - .upload_snapshot

The code gets much larger when snapshot/release configuration is multiplied by Debug/Release, Mac/Win, and 32/64bits. I would like to keep number of configurations at minimum.

Having ability to conditionally altering just a few variables would help me reducing this code a lot.

1

3 Answers 3

85

Another addition in GitLab 13.7 are the rules:variables. This allows some logic in setting vars:

job:
  variables:
    DEPLOY_VARIABLE: "default-deploy"
  rules:
    - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
      variables:                              # Override DEPLOY_VARIABLE defined
        DEPLOY_VARIABLE: "deploy-production"  # at the job level.
    - if: $CI_COMMIT_REF_NAME =~ /feature/
      variables:
        IS_A_FEATURE: "true"                  # Define a new variable.
  script:
    - echo "Run script with $DEPLOY_VARIABLE as an argument"
    - echo "Run another script if $IS_A_FEATURE exists"

It can also be done on workflow level for the whole pipeline:

workflow:
  rules:
    - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
      variables:
        DEPLOY_VARIABLE: "deploy-production"
    - if: $CI_COMMIT_REF_NAME =~ /feature/
      variables:
        IS_A_FEATURE: "true"

Then consider though that workflow rules affect the whole pipeline. You'll need to cover all your use cases.

3
  • 4
    keep in mind, it can duplicate pipelines docs.gitlab.com/ee/ci/jobs/…
    – anydasa
    Commented Apr 11, 2023 at 17:25
  • This severely limits how one can use rules though. Like if I am relying on rules to do various other things as well then it can be quite hard to massage them to also allow setting variables, since GitLab will only do anything with the first rule it matches.
    – Ben Farmer
    Commented Nov 22, 2023 at 4:17
  • Indeed, it is not super flexible, but in my experience it covers most needs. Check the other solution I propose - using a dotenv report. That one addressed the problem you describe, but introduces another one - these generated vars cannot be used in job definitions, only in scripts (and few other places) Commented Nov 23, 2023 at 10:51
21

Unfortunatelly YAML-anchors or GitLab-CI's extends don't seem to allow to combine things in script array of commands as of today.

I use built-in variable CI_COMMIT_REF_NAME in combination with global or job-only before_script to solve similar tasks without repeating myself.

Here is an example of my workaround on how to set environment variable to different values dynamically for PROD and DEV during delivery or deployment:

.provide ssh private deploy key: &provide_ssh_private_deploy_key
  before_script:
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
    - |
      if [ "$CI_COMMIT_REF_NAME" == "master" ]; then
        echo "$SSH_PRIVATE_DEPLOY_KEY_PROD" > ~/.ssh/id_rsa
        MY_DYNAMIC_VAR="we are in master (PROD)"
      else
        echo "$SSH_PRIVATE_DEPLOY_KEY_DEV" > ~/.ssh/id_rsa
        MY_DYNAMIC_VAR="we are NOT in master (DEV)"
      fi
    - chmod 600 ~/.ssh/id_rsa


deliver-via-ssh:
  stage: deliver
  <<: *provide_ssh_private_deploy_key
  script:
    - echo Stage is deliver
    - echo $MY_DYNAMIC_VAR
    - ssh ...

Also consider this workaround for concatenation of "script" commands: https://stackoverflow.com/a/57209078/470108

hopefully it helps.

2
  • Pardon my ignorance, but someone can explain to me what does the pipe in line 6: - |?
    – Erdal G.
    Commented Mar 1, 2021 at 11:32
  • 5
    @ErdalG. it is YAML feature, a block-line style indicator for multi-line strings
    – vak
    Commented Mar 3, 2021 at 9:04
3

A nice way to prepare variables for other jobs is the dotenv report artifact. Unfortunately, these variables cannot be used in many places, but if you only need to access them from other jobs scripts, this is the way:

# prepare environment variables for other jobs
env:
  stage: .pre
  script:
    # Set application version from GIT tag or fake it
    - echo "APPVERSION=${CI_COMMIT_TAG:-0.1-dev-$CI_COMMIT_REF_SLUG}+$CI_COMMIT_SHORT_SHA" | tee -a .env
  artifacts:
    reports:
      dotenv: .env

In the script of this job you can conditionally prepare and write your environment values into a file, then make a dotenv artifact out of it. Subsequent - or better yet dependent - jobs will pick up the variables for their scripts from there.

Not the answer you're looking for? Browse other questions tagged or ask your own question.