How to set an environment variable to a specific stage of gitlab ci pipeline - automation

I am using GitLab ci as my pipeline for build -> test -> deploy_dev -> deploy_prod.
Some time ago I added a step called deploy private to deploy to some private dev environments.
To support this feature in the pipeline I need to edit an environment variable only for that stage in the pipeline (let's call that stage in pipeline deploy_private).
A few key points:
I need to change this environment variable, it is not fixed - I mean that each developer can name his private dev environment as he likes and it is not a fixed environment variable.
I do not want to override the environment variable to the whole pipeline, other stages in the pipeline use that environment variable already.
I do not want to override the environment variable to each job manually, we have about 10 jobs under deploy private and this will take too much time and it is a mundane task.
Making the pipeline less cupeled to that environment variable is the correct way to go, but this is a huge and old project so this is a risky task that could take weeks.
I tried to run the whole pipeline with a new environment variable - but this is not a valid option in my case.
Currently, I am editing the environment variable manually in GitLab UI, but this takes a long time, it is error-prone, and having 10 jobs in that stage of the pipeline means that this takes about 2-3 minutes

After searching for a while, and opening a feature ticket for Gitlab, I decided to on a simple but creative solution.
Taking the environment variable from the git branch name, by a branch naming convention we started to use in our projects.
see this link for more details.
Code for gitlab-ci.yaml file looks something like this
.set_stage_deploy_private: &set_stage_deploy_private
- echo "Please note that deploy private does not set the STAGE env var by
default"
- echo "You can set it manually or follow Branch Name Convention
(See README.me file)"
- echo "Your branch name is $CI_COMMIT_REF_NAME"
# using Shell Parameter Expansion to extract STAGE after the . sign
- if [ -z "$STAGE" ] || [ -z "${STAGE// }" ] ;
then echo "STAGE was not set - will set STAGE from Branch Name Convention" ;
STAGE="${CI_COMMIT_REF_NAME##*.}" ;
fi
- echo "Your STAGE is $STAGE"
- echo "Verifying the STAGE is properly set ... "
# if STAGE is empty (was not set as env var and not with Branch Name
# Convention) abort build
- if [ -z "$STAGE" ] || [ -z "${STAGE// }" ] ;
then echo "STAGE is empty! STAGE was not set properly exiting..." ;
exit 1 ;
fi
# if STAGE is "sandbox" or "prod" abort the build
- if [ "$STAGE" = "$CI_COMMIT_REF_NAME" ] || [ "$STAGE" = "sandbox" ] ||
[ "$STAGE" = "prod" ] ;
then echo "STAGE is not allowed! STAGE was not set properly
exiting..." ;
exit 1 ;
fi
- echo "Deploying service $SERVICE with stage $STAGE"
# Matrix deployment job for all 8 services to private stack
deploy_private:
extends:
- .deploy-dev
variables:
AWS_ACCOUNT_ID: $AWS_ACCOUNT_ID_DEV
AWS_REGION: $AWS_REGION_DEV
environment: private/${SERVICE}
parallel:
matrix:
- SERVICE:
- rabo
- rai
- rams
- raop
- ras
- raw
- rps
- school
before_script:
- *set_stage_deploy_private # will set STAGE env var to cache
script:
- *deploy_private # rest of deployment logic

Related

transferring strings across gitlab ci tasks stages using variables

I am wanting to store the output from a script in a variable for use in subsequent commands from within Gitlab CI.
Here is the script:
image: ...
build c-ares:
variables:
CARES_ARTIFACTS_DIR: "-"
script:
- CARES_ARTIFACTS_DIR=$(./build-c-ares.sh)
after_script:
- echo $CARES_ARTIFACTS_DIR
artifacts:
name: CARES_ARTIFACTS
paths:
- $CARES_ARTIFACTS_DIR
My intention is to:
first declare the variable CARES_ARTIFACTS_DIR with global scope
Set the variable value using the output from the build-c-ares.sh script
Recover the output from the build-c-ares.sh script on a later command using the variable
My code does not behave as intended - on dereferencing the variable I find it contains the original value it was assigned at declaration:
$ CARES_ARTIFACTS_DIR=$(./build-c-ares.sh)
Cloning into 'c-ares'...
Running after_script
00:01
Running after script...
$ echo $CARES_ARTIFACTS_DIR
-
Uploading artifacts for successful job
00:00
Uploading artifacts...
WARNING: -: no matching files. Ensure that the artifact path is relative to the working directory
ERROR: No files to upload
It is probably easier to just redirect the script output to a file and define that as an artifact.
Something similar to:
image: ...
build c-ares:
script:
- ./build-c-ares.sh > script_output
- cat script_output
artifacts:
paths:
- script_output
In regards to the specific issue, the variables used in the "artefacts" step will again use the variable initialisation defined for the job. Both the "artefacts" and the "script" steps for the job will start with the custom CARES_ARTIFACTS_DIR variable set to the value "-":
build c-ares:
variables:
CARES_ARTIFACTS_DIR: "-"
script:
# $CARES_ARTIFACTS_DIR=="-"
- CARES_ARTIFACTS_DIR=$(./build-c-ares.sh)
# $CARES_ARTIFACTS_DIR=="hello from build-c-ares.sh"
- echo $CARES_ARTIFACTS_DIR # prints "hello from build-c-ares.sh"
after_script:
# $CARES_ARTIFACTS_DIR=="-"
- echo $CARES_ARTIFACTS_DIR # prints "-"
Fundamentally, Gitlab variables cannot feed information across job steps as intended in the original post. My subjective opinion is to keep steps independent where possible and restrict input to artefacts from upstream jobs or variables explicitly defined in the pipeline script or settings.

Drone CI - How to set pipeline env var to result of CLI output

I recognize that within a pipeline step I can run a simple export, like:
commands:
- export MY_ENV_VAR=$(my-command)
...but if I want to use this env var throughout the whole pipeline, is it possible to do something like this:
environment:
MY_ENV_VAR: $(my-command)
When I do this, I get yaml: unmarshal errors: line 23: cannot unmarshal !!seq into map[string]*yaml.Variable which suggests this isn't possible. My end goal is to write a drone plugin that accepts the output of $(...) as one if it's settings. I'd prefer to have the drone plugin not run the command, but just use the output.
I've also attempted to use step dependencies to export an env var, however it's state doesn't carry over between steps:
- name: export
image: bash
commands:
- export MY_VAR=$(my-command)
- name: echo
image: bash
depends_on:
- export
commands:
- echo $MY_VAR // empty
Writing the command output to a script file might be a better way to do what you want, since filesystem changes are persisted between individual steps.
---
kind: pipeline
type: docker
steps:
- name: generate-script
image: bash
commands:
# - my-command > plugin-script.sh
- printf "echo Fetching Google;\n\ncurl -I https://google.com/" > plugin-script.sh
- name: test-script-1
image: curlimages/curl
commands:
- sh plugin-script.sh
- name: test-script-2
image: curlimages/curl
commands:
- sh plugin-script.sh
From Drone's Docker pipeline documentation:
Workspace
Drone automatically creates a temporary volume, known as your workspace, where it clones your repository. The workspace is the current working directory for each step in your pipeline.
Because the workspace is a volume, filesystem changes are persisted between pipeline steps. In other words, individual steps can communicate and share state using the filesystem.
⚠ Workspace volumes are ephemeral. They are created when the pipeline starts and destroyed after the pipeline completes.
if cant execute command in environment period.
maybe you can define a "command string" in "environment" block, like:
environment:
MY_ENV_VAR: 'echo "this is command to execute"' # note the single quote
then in commands block,
commands:
- eval $MY_ENV_VAR
worth a try

Variable inside variable gitlab ci

Is there a way to use predefined variable inside custom variable in gitlab ci like this:
before_script:
- cat "${$CI_COMMIT_REF_NAME}" >> .env
to extract the name of branch from $CI_COMMIT_REF_NAME and use it as a name of custom variable
Update:
Check out GitLab 14.3 (September 2021)
Use variables in other variables
CI/CD pipeline execution scenarios can depend on expanding variables declared in a pipeline or using GitLab predefined variables within another variable declaration.
In 14.3, we are enabling the “variables inside other variables” feature on GitLab SaaS.
Now you can define a variable and use it in another variable definition within the same pipeline.
You can also use GitLab predefined variables inside of another variable declaration.
This feature simplifies your pipeline definition and eliminates pipeline management issues caused by the duplicating of variable data.
Note - for GitLab self-managed users the feature is disabled by default.
To use this feature, your GitLab administrator will need to enable the feature flag.
(demo -- video)
See Documentation and Issue.
dba asks in the comments:
Does this include or exclude using globally defined variables?
dba's own answer:
Global variables can be reused, but they need the local_var: ${global_var} syntax with recursive expansion (independent of the shell).
Check if this matches gitlab-org/gitlab-runner issue 1809:
Description
In the .gitlab-ci.yml file, a user can define a variable and use it in another variable definition within the same .gitlab-ci.yml file.
A user can also use a GitLab pre-defined variable in a variable declaration.
Example
variables:
variable_1: "foo" # here, variable_1 is assigned the value foo
variable_2: "${variable_1}" # variable_2 is assigned the value variable_1.
# The expectation is that the value in variable_2 = value set for variable_1
If it is, it should be completed/implemented for GitLab 14.1 (July 2021)
Lots of options.
But you could just pass the predefined var into the .env
image: busybox:latest
variables:
MY_CUSTOM_VARIABLE: $CI_JOB_STAGE
ANIMAL_TESTING: "cats"
before_script:
- echo "Before script section"
- echo $CI_JOB_STAGE
- echo $MY_CUSTOM_VARIABLE
- echo $MY_CUSTOM_VARIABLE >> .env
- echo $CI_COMMIT_BRANCH >> .env
- cat .env
example pipeline output
$ echo "Before script section"
Before script section
$ echo $CI_JOB_STAGE
build
$ echo $MY_CUSTOM_VARIABLE
build
$ echo $MY_CUSTOM_VARIABLE >> .env
$ echo $CI_COMMIT_BRANCH >> .env
$ cat .env
build
exper/ci-var-into-env
$ echo "Do your build here"
Do your build here
or pass it in earlier.
image: busybox:latest
variables:
MY_CUSTOM_VARIABLE: "${CI_JOB_STAGE}"
ANIMAL_TESTING: "cats"
before_script:
- echo "Before script section"
- echo $CI_JOB_STAGE
- echo $MY_CUSTOM_VARIABLE
- echo $MY_CUSTOM_VARIABLE >> .env
- cat .env
example: https://gitlab.com/codeangler/make-ci-var-custom-var-in-script/-/blob/master/.gitlab-ci.yml

Conditional variables in gitlab-ci.yml

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.
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"
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.
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.

Setting "local" stage env variable in Serverless v1.x

In the Serverless 1.x framework, you set environment variables in the serverless.yml file at the service root. I'm trying to set the STAGE variable that corresponds with the stage that the service is running in. It's easy for dev and prod, like so:
provider:
name: aws
...
stage: dev
environment:
STAGE: ${opt:stage, self:provider.stage}
This means that if --stage is explicitly defined, then the env variable uses that. If it's not, it uses the default dev.
When I use sls invoke local however, the default stage will be dev and that's not correct. What's the best way to get the stage to be local when invoking locally?
The obvious solution is adding -s local to the command. This was starting to get too verbose for my liking, however, and it also increases the probability of accidentally deploying to a new stage called local which is obviously undesirable.
So, I created this helper bash function:
# Invoke serverless service
invoke() {
if [ "${1}" == "local" ]; then
stage="local -s local"
else
stage="-s ${1}"
fi
payloads_dir="tests/payloads/"
if [ -z $3 ]; then
payload="${payloads_dir}${2}/default.json"
else
payload="${payloads_dir}${2}/${3}.json"
fi
if [ ! -f "${payload}" ]; then
echo $payload
echo "Payload does not exist."
return 1
fi
time --format='%e seconds' serverless invoke $stage -f $2 -p $payload
}
Usage: $ invoke stage function_name [payload_name]
Examples:
$ invoke local myFunction
will invoke the function locally with the payload at tests/payloads/myFunction/default.json while applying the local stage env.
$ invoke dev myFunction my_payload
will invoke the deployed function with stage dev and payload tests/payloads/myFunction/my_payload.json (the stage env will be correct if the deployed service has the appropriate serverless.yml file).
This is clearly an opniniated implementation, but feel free to modify it to your liking!