CI Optimization - Automated Merge Request Creation

· 4 min read

In practical project branch management, we often have this requirement: when we fix a bug directly against production, and the production branch is confirmed OK, we still need to manually create merge requests to other branches. As the number of branch environments increases, this becomes increasingly tedious because it’s purely manual labor. Where there's manual work, there will be mistakes. So, can this process be automated?

Branch Automation Concept

Brief introduction to the environments (branches) shown in the diagram:

  1. master - our production environment
  2. uat - business testing environment
  3. sit - internal team tester environment
  4. dit - development integration environment where we work

The current workflow is: if we fix a bug based on master (production), this fix/456 will be manually merged to master, but then this code still needs to be merged to uat => sit => dit, eventually flowing into the DIT development trunk, because these branches actually have an inclusion relationship. This creates repetitive work for developers or operations personnel.

As mentioned initially, manual work inevitably leads to mistakes. Over time, the likely tragedy with such an operational process is forgetting to create MRs - meaning lost code. Therefore, automation is essential.

Automation Implementation

Over the weekend, I completed this automation for our team

Feasibility Basis

  • GitLab itself provides APIs to create merge requests
  • CI runtime is in Linux node environment, allowing shell scripts to make MR requests

Here are the implementation scripts and CI configuration:

.gitlab-ci.yml

image: xxxxx/alpine:latest

variables:
 PRIVATE_TOKEN: NcWQWe9nLhxgrkNEg1Zs # GitLab account private token

stages:
 - openMr
 
cache:
 paths:
   - node_modules

# Merge UAT to SIT
openMr:
 image: "xxxxx/node:10.16.0"
 before_script: [] 
 stage: openMr
 variables:
   TARGET_BRANCH: sit
 only:
   - /^uat\/*/ # We have a very strict naming convention
 except:
   variables:
     - $CI_COMMIT_MESSAGE =~ /Merge branch 'sit' into 'uat'/
 script:
   - chmod +x ./scripts/merge-request.sh
   - HOST=${CI_PROJECT_URL} CI_PROJECT_ID=${CI_PROJECT_ID} CI_COMMIT_REF_NAME=${CI_COMMIT_REF_NAME} GITLAB_USER_ID=${GITLAB_USER_ID} PRIVATE_TOKEN=${PRIVATE_TOKEN} TARGET_BRANCH=${TARGET_BRANCH} ./scripts/merge-request.sh 

merge-request.sh

  #!/usr/bin/env bash
# Extract the host where the server is running, and add the URL to the APIs
[[ $HOST =~ ^https?://[^/]+ ]] && HOST="${BASH_REMATCH[0]}/api/v4/projects/"

# The description of our new MR, we want to remove the branch after the MR has
# been closed
BODY="{
    \"id\": ${CI_PROJECT_ID},
    \"source_branch\": \"${CI_COMMIT_REF_NAME}\",
    \"target_branch\": \"${TARGET_BRANCH}\",
    \"remove_source_branch\": true,
    \"title\": \"WIP: Merge ${CI_COMMIT_REF_NAME} to ${TARGET_BRANCH}\",
    \"assignee_id\":\"${GITLAB_USER_ID}\"
}";

# Require a list of all the merge request and take a look if there is already
# one with the same source branch
LISTMR=`curl --silent "${HOST}${CI_PROJECT_ID}/merge_requests?state=opened" --header "PRIVATE-TOKEN:${PRIVATE_TOKEN}"`;
COUNTBRANCHES=`echo ${LISTMR} | grep -o "\"source_branch\":\"${CI_COMMIT_REF_NAME}\"" | wc -l`;

# No MR found, let's create a new one
if [ ${COUNTBRANCHES} -eq "0" ]; then
    curl -X POST "${HOST}${CI_PROJECT_ID}/merge_requests" \
        --header "PRIVATE-TOKEN:${PRIVATE_TOKEN}" \
        --header "Content-Type: application/json" \
        --data "${BODY}";

    echo "Opened a new merge request: WIP: ${CI_COMMIT_REF_NAME} and assigned to you";
    exit;
fi

echo "No new merge request opened";

Download click here

Notes

What seems like simple configuration actually has some pitfalls

  1. Executing shell scripts requires chmod +x, otherwise you’ll get permission errors
  2. The reason for using CI_COMMIT_MESSAGE regex filtering in the except section is to avoid triggering MRs in cases like when UAT branch receives an SIT MR to UAT - otherwise UAT would MR to SIT again, creating a deadlock.

Automated Merge Request Approval

Since we can already use APIs to create merge requests, theoretically we should also be able to automatically approve requests. Investigation revealed that the API does support this.

However, it requires meeting two conditions:

  1. Paid version Bronze / Starter
  2. Version requirement: Introduced in GitLab Starter 10.6.

If requirements aren’t met, you’ll get 404 errors.

Since the company uses the community edition, the direct API support route isn’t available. For those who meet the requirements, you can simply extend the previous script.

Final Thoughts

  • It may seem like a small step, but it solves labor costs and has real value.
  • In principle, it’s still git hooks combined with scripted access to GitLab API requests, so even if not using GitLab’s CI mechanism, Jenkins could also solve this.

Reference Documentation

Authors
Developer, digital product enthusiast, tinkerer, sharer, open source lover