Search code examples
bashgitlabgitlab-cidevops

How to assign values to the variables depending on other variables defined on the pipeline start?


I'm trying to assign values to the list of variables, according of appropriate choice maded at the pipeline startup. So In my case:

I have a gitlab-ci.yml file with following:

variables:
  DOCKER_DRIVER: overlay
  REGISTRY: registry.restream.ru:5000
  CENTOS_TESTS_VERSION: 1.1.3
  CENTOS_PYTHON_VERSION: 1.0.5
  CENTOS_CPP_VERSION: 1.0.4
  REDOS7_CPP_VERSION: 1.0.0
  REDOS8_TESTS_VERSION: 1.0.0
  REDOS8_CPP_VERSION: 1.0.0
  REDOS8_PYTHON_VERSION: 1.0.0
  OS_VERSION:
    value: "RedOS-8"
    options:
      - "RedOS-8"
      - "CentOS-7"
      - "RedOS-7"
    description: "The deployment target. Set to 'RedOS-8' by default."
  COMPONENT_TYPE:
      value: "Python"
      options:
        - "Python"
        - "C++"
        - "Tests"
      description: "The deployment component type. Set to 'Python' by default."

default:
  before_script:
    - >
      if [[ $OS_VERSION = "RedOS-8" ]] && [[ $COMPONENT_TYPE = "C++" ]]; then
          variables:
            DOCKERFILE = ./redos8/cpp.Dockerfile
            IMAGE_NAME = redos8-cpp
            IMAGE_VERSION = $REDOS8_CPP_VERSION
      elif [[ $OS_VERSION = "RedOS-8" ]] && [[ $COMPONENT_TYPE = "Python" ]]; then
          variables:
            DOCKERFILE = ./redos8/python.Dockerfile
            IMAGE_NAME: redos8-python
            IMAGE_VERSION = REDOS8_PYTHON_VERSION
      elif [[ $OS_VERSION = "RedOS-8" ]] && [[ $COMPONENT_TYPE = "Tests" ]]; then
          variables:
            DOCKERFILE: ./redos8/tests.Dockerfile
            IMAGE_NAME: redos8-tests
            IMAGE_VERSION = $REDOS8_TESTS_VERSION
      elif [[ $OS_VERSION = "RedOS-7" ]]; then
          variables:
            DOCKERFILE = ./redos7/cpp.Dockerfile
            IMAGE_NAME = redos7-cpp
            IMAGE_VERSION = $REDOS7_CPP_VERSION
      elif [[ $OS_VERSION = "CentOS-7" ]] && [[ $COMPONENT_TYPE = "Python" ]]; then
          variables:
            DOCKERFILE = ./redos7/python.Dockerfile
            IMAGE_NAME = centos7-python
            IMAGE_VERSION = $CENTOS_PYTHON_VERSION   
      else
          variables:
            DOCKERFILE = ./centos7/cpp.Dockerfile
      fi

stages:
  - lint
  - build

.lint:
  stage: lint
  image: $REGISTRY/hadolint/hadolint:v2.12.0-alpine
  rules:
    - changes:
        - $DOCKERFILE
        - repo/*
        - scripts/*
  tags:
    - dind
  script:
    - hadolint $DOCKERFILE

.build:
  image: $REGISTRY/docker:latest
  services:
    - $REGISTRY/docker:dind
  stage: build
  variables:
    FULL_IMAGE_NAME: $REGISTRY/rtpp-team/ci-cd/$IMAGE_NAME:$IMAGE_VERSION
  rules:
    - changes:
        - $DOCKERFILE
        - repo/*
        - scripts/*
  tags:
    - dind
  script:
      
    - docker build --pull --tag $FULL_IMAGE_NAME --file $DOCKERFILE .
    - docker push $FULL_IMAGE_NAME

lint_dockerfile:tests:
  extends: .lint
  variables:
    DOCKERFILE: $DOCKERFILE

build_image:tests:
  extends: .build
  variables:
    DOCKERFILE: $DOCKERFILE
    IMAGE_NAME: $IMAGE_NAME
    IMAGE_VERSION: $CENTOS_TESTS_VERSION
  needs:
    - job: lint_dockerfile:tests

lint_dockerfile:python:
  extends: .lint
  variables:
    DOCKERFILE: $DOCKERFILE

build_image:python:
  extends: .build
  variables:
    DOCKERFILE: $DOCKERFILE
    IMAGE_NAME: $IMAGE_NAME
    IMAGE_VERSION: $CENTOS_PYTHON_VERSION
  needs:
    - job: lint_dockerfile:python

lint_dockerfile:cpp:
  extends: .lint
  variables:
    DOCKERFILE: $DOCKERFILE

build_image:cpp:
  extends: .build
  variables:
    DOCKERFILE: $DOCKERFILE
    IMAGE_NAME: $IMAGE_VERSION
    IMAGE_VERSION: $CENTOS_CPP_VERSION
  needs:
    - job: lint_dockerfile:cpp

For example, I chose condition like "RedOS-8" + "C++".

In log output I get:

Getting source from Git repository
00:01
Fetching changes with git depth set to 20...
Reinitialized existing Git repository in /builds/dev_team/devops/ci-cd-docker-images/.git/
Checking out 61cad3e4 as detached HEAD (ref is devops/WINK-5057)
Skipping Git submodules setup
Executing "step_script" stage of the job script
00:01
Using docker image sha256:19b38dcec411d7f333601a68f55cb3e710fca099615a7eee0fa2e020adfc7292 for registry.team.ru:5000/hadolint/hadolint:v2.12.0-alpine with digest hadolint/hadolint@sha256:3c206a451cec6d486367e758645269fd7d696c5ccb6ff59d8b03b0e45268a199
$ if [[ $OS_VERSION = "RedOS-8" ]] && [[ $COMPONENT_TYPE = "C++" ]]; then # collapsed multi-line command
/bin/sh: eval: line 179: variables:: not found

I tried to change multi-line characters from > to |. Also, I tried to use one condition for test like:

default:
  before_script:
    - >
      if [[ $OS_VERSION = "RedOS-8" ]] && [[ $COMPONENT_TYPE = "C++" ]]; then
          variables:
            DOCKERFILE = ./redos8/cpp.Dockerfile
            IMAGE_NAME = redos8-cpp
            IMAGE_VERSION = $REDOS8_CPP_VERSION

With no result. The log output gave me the same error with "# collapsed multi-line command" and $DOCKERFILE not found for the first pipeline step called "hadolint".


Solution

  • Writing long scripts in the pipline reduces readability and invites errors. Use shellcheck.

    variables: is not a shell command, so you get /bin/sh: eval: line 179: variables:: not found . DOCKERFILE = ./redos8/cpp.Dockerfile is not shell assignment.

    Create a shell script in the repository:

    #!/bin/sh
    # before_script.sh
    case "$OS_VERSION $COMPONENT_TYPE"
    'RedOS-8 C++')
        export DOCKERFILE=./redos8/cpp.Dockerfile
        export IMAGE_NAME=redos8-cpp
        export IMAGE_VERSION=$REDOS8_CPP_VERSION
        ;;
    'RedOS-8 Python')
        # etc..
        ;;
    'RedOS-7 '*)
        # etc..
        ;;
    esac
    
    "$@"
    

    Then source it from the pipeline.

    default:
       before_script:
          - . ./before_script.sh
    

    I also like beeing explicit. The last "$@" can be used to run as a wrapper, which allows for easy local testing:

    script:
       - ./before_script.sh some command with required environment