109

How do I run a .gitlab-ci.yml job only on a tagged Master branch?

job:
  script:
  - echo "Do something"
  only:
  - master
  - tags

The above code will run if either condition exists: a Master branch or a tagged commit.

My goal is to have this run for a production deploy, but it would require that it be on the Master branch and that it be tagged (with a version). Otherwise, I'll have another job that will push to staging if its missing a tag.

Carson Cole
  • 4,183
  • 6
  • 25
  • 35
  • 2
    How about running it for tags and checking if tag belongs to master branch in a script? – Jakub Kania Mar 15 '17 at 00:12
  • I thought it should also work with Variables and tried it with `rules: - if: $CI_COMMIT_TAG != null' && $CI_COMMIT_BRANCH == "master"` and also with `only: refs: - test variables: - $CI_COMMIT_TAG != null`, but without success... – Wolfson Sep 02 '20 at 10:45
  • 1
    @Wolfson The problem with that is that if the pipeline is triggered by a tag, `CI_COMMIT_BRANCH` is not defined, and viceversa with a commit trigger and `CI_COMMIT_TAG` – Anakhand Mar 11 '21 at 22:13

11 Answers11

40

This behavior will be introduced in version 12.

Open issue was recently update:

Jason Lenny @jlenny changed title from {-Update .gitlab-ci.yml to support conjunction logic for build conditions-} to Conjunction logic for build conditions MVC · 2 days ago

Jason Lenny @jlenny changed milestone to 12.0 · 2 days ago

(fingers crossed)

A solution is to use the except keyword to exclude all the branches, in conjunction with only to run on tags, in this way you run your pipeline only on tag in master branch:

  only:
    - tags
  except:
    - branches

I'm using version 11.3.4

  • 1
    The issue seems to have been migrated to [another issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/60085) - and it missed 12.0, but development of feature is ongoing so it could be available in 12.1. – Kristoffer Bakkejord Jul 01 '19 at 18:23
  • 9
    How does this run only on tags on master branch? Can't we tag any branch? – Charlie Schliesser Aug 11 '20 at 20:07
  • 6
    The example will also run on other branches then protected branches. – Kound Oct 15 '20 at 10:23
  • @CharlieSchliesser this job runs only when both the conditions are satisfied: you can tag even other branches, but it will be skipped. – Sergio Tomasello Nov 17 '21 at 11:22
  • 1
    Is the except part really needed? doesn't only tags already exclude everything but tags, which excludes branches as well? – Daniel Jun 02 '22 at 09:59
  • @Daniel: `branches` means all the branches except the master. The example above allows to run pipelines only when you create a tag on the master branch. – Sergio Tomasello Jun 07 '22 at 14:11
  • 1
    @SergioTomasello To be precise, "branches" means all brances other than the _default branch_, which can be named master, main, or muppet. – Martin Thøgersen Aug 30 '22 at 22:09
  • For a solution that does really only runs for tags that are on selected branches see https://stackoverflow.com/questions/62756669 – Kound Jan 13 '23 at 10:25
25

Thanks to others like Matt Alioto who posted about the open issue (which is labeled Product Vision 2019 so hopefully they knock it out this year).

Specific to Carlson Cole's question, this would work:

job_for_master_no_tags:
  stage: deploy
  script:
  - echo "Release to Staging"
  only:
  - master

job_for_master_tags_only:
  stage: deploy
  script:
  - echo "Release to Production"
  only:
  - tags
  except:
  - /^(?!master).+@/    # Ruby RegEx for anything not starting with 'master'
  • To see how this RegEx works check out https://rubular.com/r/1en2eblDzRP5Ha
  • I tested this on GitLab version 11.7.0 and it works
    • Note: If you try to use - /^(?!master).+/ (without the @) it doesn't work - learned that the hard way
Eric D. Johnson
  • 10,219
  • 9
  • 39
  • 46
10

I made it work with this working code snippet, all others were not working for me

only:
 - tags  # please mention the "s" compared to Sergio Tomasello's solution
except:
 - branches

I use 11.4.3

hannes ach
  • 16,247
  • 7
  • 61
  • 84
8

I had the same problem. I wanted to trigger a deploy to our staging-environment on a push or merge, and only when applying a tag deploy it our production-environment.

We need 2 variables for this: $CI_COMMIT_BRANCH and $CI_COMMIT_TAG. With these variables we could deduct if the pipeline was triggered by a commit or a tag. Unfortunately the first variable is only set when committing on a branch, while the second variable only is set on applying a Tag. So this was no solution...

So I went for the next-best setup by only do a production-release when a tag is set by specified conventions and by a manual trigger. This is my .gitlab-ci.yml file:

stages:
  - deploy:staging
  - deploy:prod

deploy-to-staging:
  stage: deploy:staging
  rules:
    - if: $CI_COMMIT_BRANCH == 'master'
  script:
    - echo "Deploying to Staging..."

deploy-to-production:
  stage: deploy:prod
  rules:
    - if: $CI_COMMIT_TAG =~ /^v(?:\d+.){2}(?:\d+)$/
      when: manual
  script:
    - echo "Deploying to Production..."

If you really want to automate this, you have to do a little scripting to find out if the applied tag actually belongs to a commit that is on the master-branch. Check this comment on the GitLab issuetracker for more information: https://gitlab.com/gitlab-org/gitlab-foss/-/issues/31305#note_28580169

Milkmannetje
  • 1,152
  • 1
  • 10
  • 35
6

Years later, still trying to launch a job on tags on master branch...

The issue at Gitlab has been closed : https://gitlab.com/gitlab-org/gitlab-foss/-/issues/27818

It's not possible to spot a tag on master branch as Git does not work this way. Branches and tags are separate references, each pointing to a commit. So, a tag has no relation with a branch.

My solution is to check the tag name to detect that it represents a PRODUCTION release :

deploy-prod:
  stage: deploy-manual
  only:
    variables:
      - $CI_COMMIT_TAG =~ /^v\d+.\d+.\d+-?.*$/
  when: manual

The regexp matches semver tag names like :

  • v1.2.0
  • v2.0.0-beta.1
  • ...
Franck
  • 560
  • 7
  • 20
5

This behavior is not yet supported by gitlab-ci, although there is an open issue to add it.

In the meantime I've also heard anecdotal reports that

only:
  - master
only:
  - tags

will get the job done (as well as anecdotal reports that it won't).

bPratik
  • 6,894
  • 4
  • 36
  • 67
Matt Alioto
  • 403
  • 2
  • 10
  • I've tried that and it didn't seem to seem to make any difference. Thanks for the note on the open issue. – Carson Cole Mar 18 '17 at 17:48
  • The only other thing I can suggest is hacking something together with regular expressions like the users [here](https://github.com/gitlabhq/gitlab-ci/issues/639) are experimenting with. – Matt Alioto Mar 18 '17 at 17:54
  • I just tried the proposed solution and it is indeed working! – Ali Jun 30 '17 at 19:59
  • 2
    @MattAlioto too bad that link doesn't exist anymore, always try to include a brief example in case the original source goes away ;) – Jonas D. Jul 25 '17 at 07:51
  • 1
    @JonasD. This seems to be the link you're looking for: https://gitlab.com/gitlab-org/gitlab-ce/issues/27818 – Riga Aug 16 '17 at 20:53
  • 6
    Per https://gitlab.com/gitlab-org/gitlab-ce/issues/27818#note_38463332 second only just overrides the first. – bbodenmiller Jul 20 '18 at 04:18
2

I needed to enable the pipeline only if there is a tag on the HEAD of master and found the following solution: the condition only checks that the last commit was on a tag. in the script I compare the tag's commit hash and master HEAD's commit hash and fails the step if they are not equal

rules:
- if: '$CI_COMMIT_TAG != null
  when: always
...
script:
# verifying that the tag that triggered the pipeline is done on the head of master
- latest_commit=$(git rev-parse master)
- echo "CI_COMMIT_SHA - $CI_COMMIT_SHA"
- echo "latest_commit - $latest_commit"
- if [ $latest_commit != $CI_COMMIT_SHA ]; then echo "tagging and deploying a version is allowed only on the HEAD of master"; exit 1; fi
lior.mor
  • 158
  • 1
  • 9
1

There is no proper build in solution in gitlab so far for this problem. To keep track of the development of a proper solution and to keep a working workaround updated I created: Gitlab CI: Run Pipeline job only for tagged commits that exist on protected branches

Kound
  • 1,835
  • 1
  • 17
  • 30
1

I have faced the same issue, this is how I tried to solve it

my_job:
  stage: build
  services:
    - name: docker:dind
  image: docker:latest
  script:
    - ...
  rules:
    - if: $CI_COMMIT_BRANCH == 'master' && $CI_COMMIT_TAG == null
  cache: {}

this job runs only when there is a commit on the master branch (excluding any other commit on personal/feature branch). In the very same way I trigger builds on tags:

  script:
    - ...
    
  rules:
    - if: $CI_COMMIT_BRANCH == 'master' && $CI_COMMIT_TAG != null
Dharman
  • 30,962
  • 25
  • 85
  • 135
Mauro Dorni
  • 455
  • 2
  • 14
1

Docs actually say that only and except are not being developed and that users should use rules instead.
Here is an example which will only run on a tag.

deploy-to-prod:
  stage: deploy
  rules:
    - if: $CI_COMMIT_TAG
  environment:
    name: production
  variables:
    API_ENVIRONMENT: prod
  when: manual
BenM
  • 21
  • 1
0

I have found workaround for this.

.gitlab-ci.yml

build-image-prod:
  stage: image
  only:
    refs:
      - tags
    variables:
      - $CI_COMMIT_TAG =~ /^v.*$/
  script:
      - ./scripts/not-on-main $CI_COMMIT_TAG && exit 0
      - echo "Image building..."

scripts/not-on-main:

#!/bin/sh
if [ -z "$1" ]; then
    echo "Missing argument with commit ref."
    exit 0
fi
git fetch origin main
if git branch -r --contains "$1" | grep -q main; then
    echo "Ref $1 is on main branch."
    exit 1
else
    echo "Ref $1 is not on main branch - exiting."
    exit 0
fi

The job runs for each tag in repository if name of tag starts with "v". The helper script fetches main and checks if tag is on it. If the tag is not on main branch then the job exits without building.

jirkabs
  • 31
  • 4