$CI_COMMIT_TAG in "if" statemets of regular job
Asked Answered
P

1

8

I try to make a pretty basic GitLab CI job.
I want:
When I push to develop, gitlab builds docker image with tag "develop"
When I push to main, gitlab checks that current commit has tag, and builds image with it or job is not triggered.

Build and publish docker image:
  stage: build
  rules:
    - if:
        ($CI_COMMIT_BRANCH == "main" && $CI_COMMIT_TAG && $CI_PIPELINE_SOURCE == "push")
      variables:
        TAG: $CI_COMMIT_TAG
    - if:
        ($CI_COMMIT_BRANCH == "develop" && $CI_PIPELINE_SOURCE == "push")
      variables:
        TAG: develop
  script:
    - echo $TAG
    - ...<another commands>

But it doesn't work as expected. $CI_COMMIT_TAG - is empty. Despite commit that triggers job(merge commit) has tag.

Explanation that i found that i found does not help achieve my goal using "if" statements.
Solution based on workflow suggested here not helpful either.

It's seems pretty common job with intuitive way of using variable called COMMIT_TAG.
But it just does not work. Can someone kind, please, explain to me how to achieve my goal?

Purcell answered 15/9, 2021 at 20:48 Comment(0)
D
16

Gitlab CI/CD has multiple 'pipeline sources', and some of the Predefined Variables only exist for certain sources.

For example, if you simply push a new commit to the remote, the value of CI_PIPELINE_SOURCE will be push. For push pipelines, many of the Predefined Variables will not exist, such as CI_COMMIT_TAG, CI_MERGE_REQUEST_SOURCE_BRANCH_NAME, CI_EXTERNAL_PULL_REQUEST_SOURCE_BRANCH_NAME, etc.

However if you create a Git Tag either in the GitLab UI or from a git push --tags command, it will create a Tag pipeline, and variables like CI_COMMIT_TAG will exist, but CI_COMMIT_BRANCH will not.

One variable that will always be present regardless what triggered the pipeline is CI_COMMIT_REF_NAME. For Push sources where the commit is tied to a branch, this variable will hold the branch name. If the commit isn't tied to a branch (ie, there was once a branch for that commit but now it's been deleted) it will hold the full commit SHA. Or, if the pipeline is for a tag, it will hold the tag name.

For more information, read through the different Pipeline Sources (in the description of the CI_PIPELINE_SOURCE variable) and the other variables in the docs linked above.

What I would do is move this check to the script section so we can make it more complex for our benefit, and either immediately exit 0 so that the job doesn't run and it doesn't fail, or run the rest of the script.

Build and publish docker image:
  stage: build
  image: alpine:latest
  script:
    - if [ $CI_PIPELINE_SOURCE != 'push' ]; then exit 0 fi
    - if [ $CI_COMMIT_REF_NAME != 'develop' && $CI_COMMIT_TAG == '' ]; then exit 0 fi
    - if [ $CI_COMMIT_TAG != '' ]; then git branch --contains $CI_COMMIT_TAG | grep main; TAG_ON_MAIN=$? fi
    - if [ $TAG_ON_MAIN -ne 0 ]; then exit 0 fi
    - echo $TAG
    - ...<other commands>

This is a bit confusing, so here it is line by line:

  1. If the $CI_PIPELINE_SOURCE variable isn't 'push', we exit 0.
  2. If the $CI_COMMIT_REF_NAME (again, either a commit SHA, tag name, or branch name) isn't develop and $CI_COMMIT_TAG is empty, exit 0
  3. If $CI_COMMIT_TAG isn't empty, we run a command to see if the tag was based on main, git branch --contains <tag_name>. This will return all the branches this tag is a part of (which is, the branch it was created from, and all branches that exist after the tag was made). We then pass the results through grep to look for main. If main is in the result list, the exit code is 0 which we can get with the special variable $? (always returns the previous command's exit code). We then set this exit code to a variable to use in the next conditional.
  4. We check to see if the exit code of the grep from step 3. is non-0 (that is, if main is not in the list of branches <tag_name> is part of), and we exit 0.

After all of these, we can be sure that the Pipeline Source is push, that either there is a tag and it's on the main branch, or that there isn't a tag and the pipeline is for the develop branch.

Decagon answered 16/9, 2021 at 19:53 Comment(5)
Thank you. Your text and my tries helped understand a little bit. It just confusing from my level that there are different pushes (tag, branch) because... well, i tag commit and do git push. Commit is uploaded to repo with tag and commit changes so..why CI could not recognize that tag is not understandable. But for now I will just accept it "ASIS". And probably will get deeper understandig of it in the future with more experience.Purcell
I had to ad ; after fi, otherwise I would get /bin/sh: eval: line 136: syntax error: unexpected end of file (expecting "fi")Rude
This is actually recommended against by Gitlab, even though your analysis is spot on: gitlab.com/gitlab-org/gitlab/-/issues/34578#note_381469101Impletion
For the script section, is this assuming your runner is running Linux?Carolinian
@Carolinian yes, I forgot to clarify that in my answer and will update it. If you need some other shell you can either use a different docker image or if you’re using the shell executor, you’d use whatever shell is present on the host (for example if you run gitlab-runner on a Windows box you’d use PowerShell). See the docs for more.Decagon

© 2022 - 2024 — McMap. All rights reserved.