Run job in CI pipeline only on merge branch into the master and get merged branch name

Hello everyone,

my name is Roman and I’m trying to set up CI pipeline. This pipeline should consist of 4 stages. First 3 stages (build, test and package) should be run on each push to the git and the last one (post_merge) should be run only when project maintainer is merged merge request into the master branch.

On the “package” stage I create a docker-image with my application and push this image into the local docker-registry. This docker image has tag equal to the name of current branch in the git. Testers can deploy this image into the testing environment and test this feature manually. When this branch is merged into the master this docker-image is not necessary anymore and I want to remove it on the “post_merge” stage.

It’s possible to remove tag by using Gitlab API something like this:

curl --request DELETE --header "PRIVATE-TOKEN: $CI_PRIVATE_TOKEN" "$GITLAB_API_V4/projects/$PROJECT_ID/registry/repositories/$REGISTRY_ID/tags/$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"

But I can’t find the way when to make this API call.

To implement this approach I created following config in .gitlab-ci.yml (I skipped some not important parts, such as variables definition):

stages:
  - build
  - test
  - package
  - post_merge

build:
  stage: build

  script:
    - ./some-build-script
    - echo $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME > build/tmp/source_branch

  artifacts:
    name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
    paths:
      - build
    expire_in: 1 hour

test:
  stage: test

  script: ./some-test-script

  artifacts:
    reports:
      junit: build/test-results/test/TEST-*.xml

package:
  stage: package

  script:
    - echo "Current application version:"
    - cat build/tmp/test_app.version
    - docker build --build-arg version=$(cat build/tmp/test_app.version) -t $DOCKER_CONTAINER_NAME:$CI_COMMIT_REF_NAME .
    - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $DOCKER_REGISTRY_HOST
    - docker push $DOCKER_CONTAINER_NAME:$CI_COMMIT_REF_NAME

cleanup:
  stage: post_merge

  only:
    refs:
      - master

  script:
    - echo "Remove image $DOCKER_CONTAINER_NAME:$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME because branch $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME merged into master"
    - curl --request DELETE --header "PRIVATE-TOKEN: $CI_PRIVATE_TOKEN" "$GITLAB_API_V4/projects/$PROJECT_ID/registry/repositories/$REGISTRY_ID/tags/$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"

First 3 steps are working as I want, but the last one is not working. In this step variable $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME is not available and it’s empty. I also tried to replace it with $CI_COMMIT_REF_NAME, but in this context this variable has value “master”, but I need to have the name of source branch here. Also I tried to replace cleanup job with something like this:

cleanup:
  stage: post_merge

  only:
    refs:
      - master
      - merge_requests

  script:
    - echo "Remove image $DOCKER_CONTAINER_NAME:$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME because branch $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME merged into master"
    - curl --request DELETE --header "PRIVATE-TOKEN: $CI_PRIVATE_TOKEN" "$GITLAB_API_V4/projects/$PROJECT_ID/registry/repositories/$REGISTRY_ID/tags/$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"

But in this case this stage starts as independent pipeline (which is consist of the only one job) and this stage doesn’t have access to artifacts of build stage. Also this pipeline runs not only on the merge request merged, but on merge request is created too.

Does any one know how to set up .gitlab-ci.yml to run stage only when some branch is merged into the master and get the name of source branch?

I found workaround for my task: I retrieve information about the branch name which is merged into the master from the commit title:

cleanup:
  stage: post_merge

  image: buildpack-deps:stretch-curl

  only:
    refs:
    - master

  script:
    - 'echo "Commit sha: $CI_COMMIT_SHA"'
    - 'echo " ====> API request: $CI_API_V4_URL/projects/$PROJECT_ID/repository/commits/$CI_COMMIT_SHA"'
    - 'RESPONSE=$(curl --header "PRIVATE-TOKEN: $CI_PRIVATE_TOKEN" "$CI_API_V4_URL/projects/$PROJECT_ID/repository/commits/$CI_COMMIT_SHA")'
    - 'echo "response: $RESPONSE"'
    - SOURCE_BRANCH=$(echo $RESPONSE |  grep -Po '"title":.*?[^\\]"' | sed 's/title//; s/Merge branch//; s/://; s/into//; s/master//; s/ //g; s/"//g;' | sed "s/'//g")
    - echo "Source docker-tag to remove $SOURCE_BRANCH"
    - 'curl --silent --request DELETE --header "PRIVATE-TOKEN: $CI_PRIVATE_TOKEN" "$CI_API_V4_URL/projects/$PROJECT_ID/registry/repositories/$REGISTRY_ID/tags/$SOURCE_BRANCH"'
4 Likes

in my case for bash script with python:

url="$CI_API_V4_URL/projects/$CI_PROJECT_ID/repository/commits/$CI_COMMIT_SHA/merge_requests"
SOURCE_BRANCH=$(python3 <<EOT
import requests, json


headers = {"PRIVATE-TOKEN": "$CI_PRIVATE_TOKEN"}
r = requests.get('$url',headers=headers)

try:
   print(json.loads(r.text)[0]["source_branch"])
except Exception:
   print("Null")
EOT
)
2 Likes

Does someone have a clean minimal script just for determining whether we are on the default branch and the pipeline trigger was such a merge commit? I’m thinking of:

cleanup:
  stage: .post
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  script: | 
    if [[ WHAT_SHOULD_I_PLACE_HERE ]] # Easiest way would be to parse $CI_COMMIT_MESSAGE ?
    then
      echo "This is a merge commit"
    else
      echo "This is not a merge commit"
    fi


Thanks a lot @romka! You led me into the right direction.

I’m using your approach except for the branch name retrieval. I’m doing it now completely with curl and jq (without the need of parsing the commit message):

  - MR_BRANCH_LAST_COMMIT_SHA=$(
      curl -s \
        --header "PRIVATE-TOKEN: $CI_PRIVATE_TOKEN" \
        "$CI_API_V4_URL/projects/$CI_PROJECT_ID/repository/commits/$CI_COMMIT_SHA" |\
        jq -r '.parent_ids | del(.[] | select(. == "'$CI_COMMIT_BEFORE_SHA'")) | .[-1]'
    )
  - MR_BRANCH_NAME=$(
      curl -s \
        --header "PRIVATE-TOKEN: $CI_PRIVATE_TOKEN" \
        "$CI_API_V4_URL/projects/$CI_PROJECT_ID/repository/commits/$MR_BRANCH_LAST_COMMIT_SHA/merge_requests" |\
        jq -r '.[0].source_branch'
    )
3 Likes

thanks, this was very useful for us.

thanks, it helped a lot