GitLab CI: Get source branch after merge-request has been accepted
Asked Answered
H

4

18

I have a master branch which is supposed to only get commits by merging either a "release/xxxxx" branch into it or by merging a "hotfix/xxxxx" branch into it.

The pipeline for a release branch builds a docker image and publishes it using the tag "beta".
The pipeline for a release branch builds a docker image and publishes it using the tag "hotfix".

The pipeline for the master simply re-tags either "beta" to "stable" (when a release branch has been merged into the master) or "hotfix" to "stable" (when a hotfix branch has been merged into the master). Then it also creates a new git tag for that version and a release in gitlab. Finally the docker image gets deployed.

Currently the following happens:

  • A merge request is created to merge "release/xxxxx" into "master".
  • The pipeline starts automatically (before the merge request has been accepted and merged).
  • The docker job parses the CI_MERGE_REQUEST_SOURCE_BRANCH_NAME variable to determine the source branch of the merge request was a release branch.
  • The docker job then re-tags "beta" to "latest".
  • The git tag job adds a version tag and gitlab release to the project.
  • The deploy job deploys the docker image.

When the merge request is finally accepted the following happens:

  • The pipeline starts again.
  • The docker job parses CI_MERGE_REQUEST_SOURCE_BRANCH_NAME which is now empty and thus fails to determine the source branch for this merge.
  • The docker job fails.

I think it's pretty obvious that I don't want to run all these jobs (especially the deploy job) before the merge request has been accepted.

But I can't think of a good way to only run the pipeline on the master after a merge-request has been accepted and then still be able to determine the source branch.

Happenstance answered 23/1, 2020 at 11:8 Comment(4)
Can you please share your gitlab-ci.yml file?Chivaree
My actual .gitlab-ci.yml is a big mess handling up to 13 jobs per branch and there are way more than the 3 branches mentioned in my question. No to mention the fact that it includes a bunch of files from other non-public projects, uses custom non-public docker images and heavily relies on env vars set on a group and project level. There is no way I can share all that and the .gitlab-ci.yml alone wouldn't help anyone.Happenstance
I understand your situation but It's hard for us to assume your configuration as pipeline and deployment both highly depend on yml file.Chivaree
Did you find a solution ?Voodoo
B
10

Using only information stored in git :

when running after a merge request has been accepted, the HEAD commit should be the result of the merge, so HEAD^2 should be the head commit of the branch that was merged.

You can have information on :

  • the tags that point directly at this commit :

     git tag --points-at HEAD^2
    
  • the branches that point directly at this commit :

     git branch --points-at HEAD^2    # for local branches
     git branch -r --points-at HEAD^2 # for remote branches
    
  • the branches that contain this commit :

     git branch --contains HEAD^2    # for local branches
     git branch -r --contains HEAD^2 # for remote branches
    

Using environment variables set by gitlab :

after reading the predefined variables doc page, I was surprised not to find variables that would indicate that the job was triggered after a merge request was accepted.

You may look into gitlab's webhooks : Merge requests events ; these should provide enough information to identify what branches were involved in the merge request after it has been accepted.

[update] you can also use gitlab's API, see @BastienSander's answer

Behemoth answered 30/1, 2020 at 13:47 Comment(3)
When I try your commands, i have a "error: malformed object name 'HEAD^2'"Voodoo
@Voodoo : this means your current active commit is not a merge commit (HEAD^2 means "the second parent of HEAD", which is only valid if HEAD is a merge commit)Behemoth
This didnt work for me so I changed to git log --skip=1 -1 Coolidge
V
6

It seems you can do it through the API of gitlab.

Here is a curl example.

  - 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'
    )

I found that workaround here: https://forum.gitlab.com/t/run-job-in-ci-pipeline-only-on-merge-branch-into-the-master-and-get-merged-branch-name/24195/5

Voodoo answered 28/4, 2021 at 10:57 Comment(1)
Note that you have to omit the space after PRIVATE-TOKEN: like so: --header "PRIVATE-TOKEN:$CI_PRIVATE_TOKEN" - see: gitlab.com/gitlab-org/gitlab/-/issues/4781#note_88220042Dereliction
T
0

We had a similar situation. If there was a hotfix/xxxxx branch, when pushed, it could go through full pipeline, but after merge request to master is approved, only build and deploy jobs should run, skipping all the tests in order to get changes to prod ASAP. If any other branch was merged to master full pipeline should run.


The solution for us was to use commit message title from the approved merge request:

.merged_hotfix_to_master: &merged_hotfix_to_master
                     "$CI_COMMIT_TITLE =~ /^Merge branch 'hotfix\\// 
                        && $CI_COMMIT_REF_SLUG == 'master'"
    
.skip-for-hotfix-merge-to-master-rule:
  rules:
    - if: *merged_hotfix_to_master
      when: never

So if the commit message title started with Merge branch 'hotfix/ and the target branch was master job can be skipped. Rule was added where appropriate:

some-test:
  extends: .tests-common
  stage: test
    - !reference [.skip-for-hotfix-merge-to-master-rule, rules]
    - if: *only-master-branch

Not the most elegant solution, but I don't see the reason to change merge request default message anyway.

Taught answered 16/11, 2022 at 18:50 Comment(0)
P
0

I ran the export command and saw that only two predefined variables contained the source branch (as part of a string), one being $CI_COMMIT_TITLE. It returned:

declare -x CI_COMMIT_TITLE="Merge branch 'source-branch-name' into 'master'"

I had the same thought process as @boombar – using the CI_COMMIT_TITLE string to retrieve the source branch name. I split the string and set the source branch substring as a variable, like so:

- SOURCE_BRANCH_NAME=`echo $CI_COMMIT_TITLE | cut -d"'" -f 2`

Also agree that it's not the most elegant way of going about it, but as long as the default message doesn't change, you're golden. 😎

Peasant answered 15/3, 2023 at 14:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.