17

I would like to create a pipeline that is only run if both of the following conditions are met:

  • A tag refers to the given commit
  • The commit exists on any protected branch (i.e. master)
  • Optional: The job should be run whenever a tagged unprotected branch is merge (with a merge request) into a protected branch or if a tag is added to a protected branch.

I've tried:

publish:
  stage: publish
  script:
    - echo "Publish!"
  rules:
    # Only publish if tag given and commit is present on a protected branch
    - if: '$CI_COMMIT_TAG && $CI_COMMIT_REF_PROTECTED == "true"'

Which does not work as either the $CI_COMMIT_TAG is set or the $CI_COMMIT_REF_PROTECTED is set to true.

I am aware of the similar Questions: Gitlab ci run job on master with release tag only and How to run a gitlab-ci.yml job only on a tagged branch?.

Also I know there is/was a wide discussion in the issues from gitlab, with some solution (or something close to this) like this.

The general problem seems to be that it is not possible in gitlab to determine reliable if a commit if on a given branch as the information (git history) for this is not given.

This question is to keep track of a proper solution within gitlab CI for this common use case.

Kound
  • 1,835
  • 1
  • 17
  • 30

1 Answers1

16

Combining the workaround mentioned in the question with the new gitlab rule and workflow features I came up with an answer that seems satisfying for me.

The person originally posting the workaround mentioned that there are cases in which git branch contains does not give the correct results. So I made sure, that git fetch does not make a shallow copy (note for the beginning it could be useful to change the GIT_STRATEGY to clone, so that old possible shallow copies are removed).

Instead of using CI_COMMIT_REF_PROTECTED which could be true also for protected tags, I hardcoded the $CI_DEFAULT_BRANCH (thx to [Gostega) as protected.

# Be quite strict in what can trigger a pipeline, actually only pushes of
# branches or version tags should trigger anything - otherwise we need to catch
# too many edge cases.
workflow:
  rules:
    # Do no allow manually triggered pipelines to prevent duplicates!
    # Instead rerun the pipeline created with the last push
    - if: $CI_PIPELINE_SOURCE != "push"
      when: never
    # Only execute when a valid version tag like v1.0, 2.3 or similar is given
    # Required is always one point like 1.0
    - if: $CI_COMMIT_TAG =~ /^v?[0-9]+[.][0-9]+([.][0-9]+)?$/
    - if: $CI_COMMIT_BRANCH
variables:
  # Make sure we don't get a shallow copy
  GIT_DEPTH: 0
  # Fetch is default just to make clear what is used
  GIT_STRATEGY: fetch
  # make sure we fetch everything and also see what is happening
  GIT_FETCH_EXTRA_FLAGS: -f --tags --prune --update-head-ok

default:
  before_script:
    - export CI_LOG_LINE=$(git log --decorate=full| grep "^commit $CI_COMMIT_SHA[ ]")
    # var = 1 if the current commit is the **latest** on the default branch
    - export IS_ON_MAIN=$(echo $CI_LOG_LINE | grep -qso "origin/${CI_DEFAULT_BRANCH}, " && echo 1 || echo 0)
    # var = 1 if current commit is on any remote commit that is part of default branchs history
    - export COMMIT_ON_MAIN=$(git branch -r --contains $CI_COMMIT_SHA | grep -Eq "^[ ]+origin/${CI_DEFAULT_BRANCH}$"  && echo 1 || echo 0)


stages:
  - check_update_environment
  - test
  - publish

check_update_environment:
  stage: check_update_environment
  script:
    # Exit if tag is given on none main branch early
    # Check for 
    - if [[ ! -z "$CI_COMMIT_TAG" && $COMMIT_ON_MAIN != 1 ]]; then
         echo "Tags should never be applied to non main branches!" >&2;
         echo "We quit early! Please delete the tag, merge the branch to main and recreate the tag to continue" >&2;
         exit 1;
      fi

test:
  stage: test
  script:
    - echo "Doing testing..."
  dependencies:
    - check_update_environment

publish:
  stage: publish
  script:
    - echo "Publishing..."
  rules:
    # Run always if there is version tag. The version tag is defined
    #   in the workflow rules
    # Due to the fail early in the environment check this is never done for
    # branches that aren't the default branch
    - if: $CI_COMMIT_TAG
  dependencies:
    - test
Kound
  • 1,835
  • 1
  • 17
  • 30
  • 2
    Hey @Kound you still using this workaround, or have you developed anything better? By the way, instead of hardcoding to master, I use `if: $CI_COMMIT_REF_NAME != $CI_DEFAULT_BRANCH`. Which lets me reuse it across any project (we have started moving to `main` as the default branch name so have a mix of names). – Gostega Jul 12 '21 at 01:38
  • 1
    Hey @Gostega thanks for the tip. I will test it soon and update the answer. I didn't develop a more sophisticated solution and this one is still in operation. Btw: $CI_COMMIT_REF_NAME will be the tag for pipeline triggered by the tag. So this is not useful to check if the tag pipeline is on master. (See also https://docs.gitlab.com/ee/ci/variables/predefined_variables.html) – Kound Jul 12 '21 at 08:13
  • This is a really complicated solution. I've kind of settled on just making the tags themselves protected. Quite annoying we cannot combine the conditions on branches and tags. – CMCDragonkai May 21 '22 at 13:44
  • Well all the limitations are originated in the nature of git itself. IMHO my solution is not that complicated. I still removed some debug stuff in order to remove some noise. – Kound May 23 '22 at 08:36