I’m working on a reusable pipeline on GitLab-CI (currently v16.8.6), where one CI variable should fall back to a calculated value if it is not set otherwise.
the calculated value is constant for the entire pipeline, but it is used to pick the images used to run jobs (so it must be present outside of the script
section).
afaik, the only way achieve variables with dynamic values is by using dotenv artifacts, like so:
makevar:
stage: .pre
script:
- echo "VERSION=latest" > dot.env
artifacts:
reports:
dotenv: dot.env
build:
stage: build
image: alpine:${VERSION}
script:
- echo "it works"
this works nicely, as long as i’m happy with the calculated value.
in practice it turns out that I (or rather: some of my users), want to extend the pipeline definition to run some jobs (or even some pipelines) with a manually crafted value ofthe variable.
e.g. the following should define an additional job that uses alpine:edge
as the base image, regardless of what the makevar
calculated for VERSION
:
include:
- https://git.example.com/common/pipeline/-/raw/main/gitlab-ci.yml
build experimental:
extends: build
variables:
VERSION: edge
or this should ensure that all jobs have VERSION=edge
:
include:
- https://git.example.com/common/pipeline/-/raw/main/gitlab-ci.yml
variables:
VERSION: edge
unfortunately this does not work, since the variable precedence rules ensure that the dotenv variables override all locally defined variables:
- Variables from dotenv reports.
- Variables defined in jobs in the .gitlab-ci.yml file.
- Variables defined outside of jobs (globally) in the .gitlab-ci.yml file.
we can solve the case of a global override, by fixing the makevar
job:
makevar:
stage: .pre
script:
-|
echo "VERSION=${VERSION:=latest}" > dot.env
artifacts:
reports:
dotenv: dot.env
however, how can I achieve the same for a per-job override?
for additional thrills, we need to handle the case where a user manually triggered a pipeline with the variable set to an empty value (which by default will unset any value calculated or set within the gitlab-ci.yml, breaking any image: alpine:${VERSION}
stanza (given that alpine:
is not a valid image).
(the pipelines are already rather complex, with a dozen or so jobs at minimum with artifacts being passed between jobs (or not); and they are used for probably some thousand projects, some of them with local modifications (like additional jobs, with more dependencies…)