I'm trying to run a pipeline where the final stage depends on a previous stage with jobs that are conditional and manual.
I made this example .gitlab-ci.yml to demonstrate the point. I am working with three stages:
stages:
- test
- publish
- create tag
There is one job in the Test stage
# Tests Stage
run tests:
stage: test
script:
- run the tests
Three jobs in the Publish stage, all of which are manual and only exist when certain files have changed
.publish:
stage: publish
script:
- publish x
rules:
- changes:
- $DIR/**/*
when: manual
# Publish Stage
publish package a:
variables:
DIR: a
extends:
- .publish
publish package b:
variables:
DIR: b
extends:
- .publish
publish package c:
variables:
DIR: c
extends:
- .publish
And finally the Create Tag stage, which I only want to run if one of the publish jobs has completed.
# Create Tag Stage
create tag with all packages:
stage: create tag
script:
- git tag
Usually I can use needs
to make the Create Tag job depend on a publishing job. But if, for example, I only make changes in the a/ directory, I will get an error for the following because only "publish package a" exists:
needs:
- "publish package a"
- "publish package b"
- "publish package c"
What I really want is something like
needs:
- "publish package a" if exists
- "publish package b" if exists
- "publish package c" if exists
But there's nothing like this as far as I know. What can I do to run the Create Tags job only when the existing jobs in the Publish stage have completed?
What you're looking for is Optional Needs
which was introduced with a Feature Flag in version 13.10, and was promoted (available for use without the feature flag) in 14.0. This allows you to have a job like this:
build:
stage: build
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
rspec:
stage: test
needs:
- job: build
optional: true
In this example, the build
job only runs if the pipeline is for a Branch (and not a tag, merge request, etc.) and if the branch name is the same as the project's default branch. If this isn't the case, this job isn't added to the pipeline at all, so we need to ensure that our needs
for the rspec
job is optional.
By adding the optional: true
attribute to our needs
array, we tell Gitlab that the rspec
job needs the build
job if it exists.
So, for your example:
stages:
- test
- publish
- create tag
# Tests Stage
run tests:
stage: test
script:
.publish:
stage: publish
script:
- publish x
rules:
- changes:
- $DIR/**/*
when: manual
# Publish Stage
publish package a:
variables:
DIR: a
extends:
- .publish
publish package b:
variables:
DIR: b
extends:
- .publish
publish package c:
variables:
DIR: c
extends:
- .publish
# Create Tag Stage
create tag with all packages:
stage: create tag
needs:
- job: "publish package a"
optional: true
- job: "publish package b"
optional: true
- job: "publish package c"
optional: true
script:
- git tag
This feature was introduced in Issue 30680.