Commit cfea26f5 authored by Raphael Defosseux's avatar Raphael Defosseux
Browse files

CI: initial pipeline definition


Signed-off-by: Raphael Defosseux's avatarRaphael Defosseux <raphael.defosseux@eurecom.fr>
parent 3922a836
Pipeline #23045 failed with stage
in 0 seconds
#!/bin/groovy
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
//-------------------------------------------------------------------------------
// Location of the CN executor node
def cn_ci_host = params.Host_CN_CI_Server
// for lock
def cn_ci_resource = params.DockerContainers
// Location of the 2nd CN executor
def new_host_flag = false
def new_host = ""
def new_host_user = ""
// Tags/Branches to use
def nrf_tag = "develop"
def nrf_branch = "develop"
//-------------------------------------------------------------------------------
// Pipeline start
pipeline {
agent {
label cn_ci_host
}
options {
disableConcurrentBuilds()
timestamps()
ansiColor('xterm')
lock(cn_ci_resource)
gitLabConnection('OAI GitLab')
gitlabBuilds(builds: [
"Build NRF Image",
"Static Code Analysis",
"Code Formatting Checker"
])
}
stages {
stage ('Verify Parameters') {
steps {
script {
echo '\u2705 \u001B[32mVerify Parameters\u001B[0m'
JOB_TIMESTAMP = sh returnStdout: true, script: 'date --utc --rfc-3339=seconds | sed -e "s#+00:00##"'
JOB_TIMESTAMP = JOB_TIMESTAMP.trim()
if (params.Host_CN_CI_2nd_Server_Flag != null) {
new_host_flag = params.Host_CN_CI_2nd_Server_Flag
if (new_host_flag) {
new_host = params.Host_CN_CI_2nd_Server
new_host_user = params.Host_CN_CI_2nd_Server_Login
echo "1st Node is ${NODE_NAME}"
echo "2nd Node is ${new_host}"
} else {
echo "Node is ${NODE_NAME}"
}
} else {
echo "Node is ${NODE_NAME}"
}
echo "Git URL is ${GIT_URL}"
}
}
}
stage ('Prepare Source Code') {
steps {
script {
sh "git clean -x -d -f > /dev/null 2>&1"
if ("MERGE".equals(env.gitlabActionType)) {
gitCommitAuthorEmailAddr = env.gitlabUserEmail
echo "GitLab Usermail is ${gitCommitAuthorEmailAddr}"
sh "./ci-scripts/doGitLabMerge.sh --src-branch ${env.gitlabSourceBranch} --src-commit ${env.gitlabMergeRequestLastCommit} --target-branch ${env.gitlabTargetBranch} --target-commit ${GIT_COMMIT}"
nrf_tag = "ci-tmp"
nrf_branch = env.gitlabSourceBranch
} else {
echo "Git Branch is ${GIT_BRANCH}"
echo "Git Commit is ${GIT_COMMIT}"
gitCommitAuthorEmailAddr = sh returnStdout: true, script: 'git log -n1 --pretty=format:%ae ${GIT_COMMIT}'
gitCommitAuthorEmailAddr = gitCommitAuthorEmailAddr.trim()
echo "GitLab Usermail is ${gitCommitAuthorEmailAddr}"
sh "git log -n1 --pretty=format:\"%s\" > .git/CI_COMMIT_MSG"
}
sh "tar -cjhf /tmp/openair-nrf.tar.bz2 ."
sh "mv /tmp/openair-nrf.tar.bz2 ."
copyTo2ndServer('openair-nrf.tar.bz2', new_host_flag, new_host_user, new_host)
sh "mkdir -p archives"
if (new_host_flag) {
sh "mkdir -p archives/oai-nrf-cfg"
}
}
}
post {
failure {
script {
def message = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): Merge Conflicts -- Cannot perform CI"
addGitLabMRComment comment: message
currentBuild.result = 'FAILURE'
}
}
}
}
stage('Build Core Network Function') {
parallel {
stage ('Build NRF Image') {
steps {
script {
gitlabCommitStatus(name: "Build NRF Image") {
myShCmd('docker image prune --force', new_host_flag, new_host_user, new_host)
if ("PUSH".equals(env.gitlabActionType)) {
// Currently this pipeline only runs for pushes to `develop` branch
// First clean image registry
try {
myShCmd('docker image rm oai-nrf:develop', new_host_flag, new_host_user, new_host)
} catch (Exception e) {
echo "Maybe a previous build went wrong"
}
}
myShCmd('docker build --target oai-nrf --tag oai-nrf:' + nrf_tag + ' --file docker/Dockerfile.ubuntu.18.04 --build-arg NEEDED_GIT_PROXY="http://proxy.eurecom.fr:8080" . > archives/nrf_docker_image_build.log 2>&1', new_host_flag, new_host_user, new_host)
myShCmd('docker image ls >> archives/nrf_docker_image_build.log', new_host_flag, new_host_user, new_host)
}
}
}
post {
always {
script {
copyFrom2ndServer('archives/nrf_docker_image_build.log', 'archives', new_host_flag, new_host_user, new_host)
}
}
success {
sh "echo 'OAI-NRF DOCKER IMAGE BUILD: OK' >> archives/nrf_docker_image_build.log"
}
unsuccessful {
sh "echo 'OAI-NRF DOCKER IMAGE BUILD: KO' >> archives/nrf_docker_image_build.log"
}
}
}
// Running CPPCHECK in parallel to gain time
stage ('Static Code Analysis') {
steps {
script {
gitlabCommitStatus(name: "Static Code Analysis") {
// Running on xenial to have 1.72 version of cppcheck
myShCmd('docker run --name ci-cn-cppcheck -d ubuntu:xenial /bin/bash -c "sleep infinity"', new_host_flag, new_host_user, new_host)
myShCmd('docker exec ci-cn-cppcheck /bin/bash -c "apt-get update && apt-get upgrade --yes" > archives/cppcheck_install.log', new_host_flag, new_host_user, new_host)
myShCmd('docker exec ci-cn-cppcheck /bin/bash -c "apt-get install --yes cppcheck bzip2" >> archives/cppcheck_install.log', new_host_flag, new_host_user, new_host)
myShCmd('docker cp ./openair-nrf.tar.bz2 ci-cn-cppcheck:/home', new_host_flag, new_host_user, new_host)
myShCmd('docker exec ci-cn-cppcheck /bin/bash -c "cd /home && tar -xjf openair-nrf.tar.bz2"', new_host_flag, new_host_user, new_host)
myShCmd('docker exec ci-cn-cppcheck /bin/bash -c "rm -f /home/openair-nrf.tar.bz2"', new_host_flag, new_host_user, new_host)
myShCmd('docker exec ci-cn-cppcheck /bin/bash -c "cd /home && cppcheck --enable=warning --force --xml --xml-version=2 --suppressions-list=ci-scripts/cppcheck_suppressions.list src 2> cppcheck.xml 1> cppcheck_build.log"', new_host_flag, new_host_user, new_host)
}
}
}
post {
always {
script {
myShCmd('docker cp ci-cn-cppcheck:/home/cppcheck.xml archives', new_host_flag, new_host_user, new_host)
myShCmd('docker cp ci-cn-cppcheck:/home/cppcheck_build.log archives', new_host_flag, new_host_user, new_host)
copyFrom2ndServer('archives/cppcheck*.*', 'archives', new_host_flag, new_host_user, new_host)
// no need to keep the cppcheck container
myShCmd('docker rm -f ci-cn-cppcheck', new_host_flag, new_host_user, new_host)
}
}
success {
sh "echo 'CPPCHECK: OK' >> archives/cppcheck_install.log"
}
unsuccessful {
sh "echo 'CPPCHECK: KO' >> archives/cppcheck_install.log"
}
}
}
// Running CLANG-FORMATTING check in parallel to gain time
stage ('Code Formatting Checker') {
steps {
script {
gitlabCommitStatus(name: "Code Formatting Checker") {
myShCmd('docker run --name ci-cn-clang-formatter -d ubuntu:bionic /bin/bash -c "sleep infinity"', new_host_flag, new_host_user, new_host)
myShCmd('docker exec ci-cn-clang-formatter /bin/bash -c "apt-get update && apt-get upgrade --yes" > archives/clang_format_install.log', new_host_flag, new_host_user, new_host)
myShCmd('docker exec ci-cn-clang-formatter /bin/bash -c "apt-get install --yes git tree bzip2" >> archives/clang_format_install.log', new_host_flag, new_host_user, new_host)
myShCmd('docker cp ./openair-nrf.tar.bz2 ci-cn-clang-formatter:/home', new_host_flag, new_host_user, new_host)
myShCmd('docker exec ci-cn-clang-formatter /bin/bash -c "cd /home && tar -xjf openair-nrf.tar.bz2"', new_host_flag, new_host_user, new_host)
myShCmd('docker exec ci-cn-clang-formatter /bin/bash -c "rm -f /home/openair-nrf.tar.bz2"', new_host_flag, new_host_user, new_host)
// We install a dedicated version (installed on our CI server).
myShCmd('docker cp /opt/clang-format/9.0.0/bin/clang-format ci-cn-clang-formatter:/usr/local/bin', new_host_flag, new_host_user, new_host)
if ("MERGE".equals(env.gitlabActionType)) {
myShCmd('docker exec ci-cn-clang-formatter /bin/bash -c "cd /home && ./ci-scripts/checkCodingFormattingRules.sh --src-branch ' + env.gitlabSourceBranch +' --target-branch ' + env.gitlabTargetBranch + '"', new_host_flag, new_host_user, new_host)
} else {
myShCmd('docker exec ci-cn-clang-formatter /bin/bash -c "cd /home && ./ci-scripts/checkCodingFormattingRules.sh"', new_host_flag, new_host_user, new_host)
}
}
}
}
post {
always {
script {
myShCmd('docker cp ci-cn-clang-formatter:/home/src/oai_rules_result.txt src', new_host_flag, new_host_user, new_host)
// May not have been generated
try {
myShCmd('docker cp ci-cn-clang-formatter:/home/src/oai_rules_result_list.txt src', new_host_flag, new_host_user, new_host)
} catch (Exception e) {
echo "Failed to copy src/oai_rules_result_list.txt! It may not have been generated. That's OK!"
}
copyFrom2ndServer('archives/clang_format*.*', 'archives', new_host_flag, new_host_user, new_host)
copyFrom2ndServer('src/oai_rules*.*', 'src', new_host_flag, new_host_user, new_host)
// no need to keep the clang-formatter container
myShCmd('docker rm -f ci-cn-clang-formatter', new_host_flag, new_host_user, new_host)
}
}
}
}
}
}
}
post {
success {
script {
if ("MERGE".equals(env.gitlabActionType)) {
def message = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): passed (" + BUILD_URL + ")"
echo "This is a MERGE event"
addGitLabMRComment comment: message
}
}
}
unsuccessful {
script {
if ("MERGE".equals(env.gitlabActionType)) {
def message = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): failed (" + BUILD_URL + ")"
echo "This is a MERGE event"
addGitLabMRComment comment: message
}
}
}
cleanup {
script {
// Removing temporary / intermediate images
try {
if ("MERGE".equals(env.gitlabActionType)) {
myShCmd('docker image rm --force oai-nrf:ci-tmp', new_host_flag, new_host_user, new_host)
}
} catch (Exception e) {
echo "We failed to delete the OAI-NRF temp image"
}
try {
myShCmd('docker image prune --force', new_host_flag, new_host_user, new_host)
} catch (Exception e) {
echo "We failed to prune all unneeded intermediate images"
}
// Zipping all archived log files
sh "zip -r -qq docker_logs.zip archives"
if (fileExists('docker_logs.zip')) {
archiveArtifacts artifacts: 'docker_logs.zip'
}
// Generating the HTML report
if ("MERGE".equals(env.gitlabActionType)) {
sh "python3 ci-scripts/generateHtmlReport.py --job_name=${JOB_NAME} --job_id=${BUILD_ID} --job_url=${BUILD_URL} --git_url=${GIT_URL} --git_src_branch=${env.gitlabSourceBranch} --git_src_commit=${env.gitlabMergeRequestLastCommit} --git_pull_request=True --git_target_branch=${env.gitlabTargetBranch} --git_target_commit=${GIT_COMMIT}"
} else {
sh "python3 ci-scripts/generateHtmlReport.py --job_name=${JOB_NAME} --job_id=${BUILD_ID} --job_url=${BUILD_URL} --git_url=${GIT_URL} --git_src_branch=${GIT_BRANCH} --git_src_commit=${GIT_COMMIT}"
}
sh "sed -i -e 's#TEMPLATE_TIME#${JOB_TIMESTAMP}#' test_results_oai_nrf.html"
if (fileExists('test_results_oai_nrf.html')) {
archiveArtifacts artifacts: 'test_results_oai_nrf.html'
}
if (fileExists('deploy_results_oai_cn5g.html')) {
sh "sed -i -e 's#TEMPLATE_TIME#${JOB_TIMESTAMP}#' *_results_oai_cn5g.html"
archiveArtifacts artifacts: '*_results_oai_cn5g.html'
}
// Sending email to commiter
if (params.sendToCommitterEmail != null) {
if (params.sendToCommitterEmail) {
emailext attachmentsPattern: '*results*.html',
body: '''Hi,
Here are attached HTML report files for $PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!
Regards,
OAI CI Team''',
replyTo: 'no-reply@openairinterface.org',
subject: '$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!',
to: gitCommitAuthorEmailAddr
}
}
}
}
}
}
def copyTo2ndServer(filename, flag, user, host) {
if (flag) {
if ("openair-nrf.tar.bz2".equals(filename)) {
sh "ssh ${user}@${host} 'rm -rf /tmp/CI-CN-NRF'"
sh "ssh ${user}@${host} 'mkdir -p /tmp/CI-CN-NRF'"
}
sh "scp ${filename} ${user}@${host}:/tmp/CI-CN-NRF"
if ("openair-nrf.tar.bz2".equals(filename)) {
sh "ssh ${user}@${host} 'cd /tmp/CI-CN-NRF && tar -xjf ${filename}'"
sh "ssh ${user}@${host} 'mkdir -p /tmp/CI-CN-NRF/archives'"
sh "ssh ${user}@${host} 'mkdir -p /tmp/CI-CN-NRF/archives/oai-nrf-cfg'"
}
}
}
def copyFrom2ndServer(filename, target, flag, user, host) {
if (flag) {
sh "scp ${user}@${host}:/tmp/CI-CN-NRF/${filename} ${target}"
}
}
def myShCmd(cmd, flag, user, host) {
if (flag) {
sh "ssh -t -t ${user}@${host} 'cd /tmp/CI-CN-NRF && ${cmd}'"
} else {
sh "${cmd}"
}
}
def myShRetCmd(cmd, flag, user, host) {
if (flag) {
ret = sh returnStdout: true, script: "ssh -t -t ${user}@${host} 'cd /tmp/CI-CN-NRF && ${cmd}'"
} else {
ret = sh returnStdout: true, script: "${cmd}"
}
ret = ret.trim()
return ret
}
#!/bin/bash
#/*
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
# * contributor license agreements. See the NOTICE file distributed with
# * this work for additional information regarding copyright ownership.
# * The OpenAirInterface Software Alliance licenses this file to You under
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
# * except in compliance with the License.
# * You may obtain a copy of the License at
# *
# * http://www.openairinterface.org/?page_id=698
# *
# * Unless required by applicable law or agreed to in writing, software
# * distributed under the License is distributed on an "AS IS" BASIS,
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# * See the License for the specific language governing permissions and
# * limitations under the License.
# *-------------------------------------------------------------------------------
# * For more information about the OpenAirInterface (OAI) Software Alliance:
# * contact@openairinterface.org
# */
function usage {
echo "OAI Coding / Formatting Guideline Check script"
echo " Original Author: Raphael Defosseux"
echo ""
echo " Requirement: clang-format / git shall be installed"
echo ""
echo " By default (no options) the complete repository will be checked"
echo " In case of merge/pull request, provided source and target branch,"
echo " the script will check only the modified files"
echo ""
echo "Usage:"
echo "------"
echo " checkCodingFormattingRules.sh [OPTIONS]"
echo ""
echo "Options:"
echo "--------"
echo " --src-branch #### OR -sb ####"
echo " Specify the source branch of the merge request."
echo ""
echo " --target-branch #### OR -tb ####"
echo " Specify the target branch of the merge request (usually develop)."
echo ""
echo " --help OR -h"
echo " Print this help message."
echo ""
}
if [ $# -ne 4 ] && [ $# -ne 1 ] && [ $# -ne 0 ]
then
echo "Syntax Error: not the correct number of arguments"
echo ""
usage
exit 1
fi
cd src
if [ $# -eq 0 ]
then
echo " ---- Checking the whole repository ----"
echo ""
if [ -f oai_rules_result.txt ]
then
rm -f oai_rules_result.txt
fi
if [ -f oai_rules_result_list.txt ]
then
rm -f oai_rules_result_list.txt
fi
EXTENSION_LIST=("h" "hpp" "c" "cpp")
NB_TO_FORMAT=0
NB_TOTAL=0
for EXTENSION in ${EXTENSION_LIST[@]}
do
echo "Checking for all files with .${EXTENSION} extension"
FILE_LIST=`tree -n --noreport -i -f -P *.${EXTENSION} | sed -e 's#^\./##' | grep "\.${EXTENSION}"`
for FILE_TO_CHECK in ${FILE_LIST[@]}
do
TO_FORMAT=`clang-format -output-replacements-xml ${FILE_TO_CHECK} 2>&1 | grep -v replacements | grep -c replacement`
NB_TOTAL=$((NB_TOTAL + 1))
if [ $TO_FORMAT -ne 0 ]
then
NB_TO_FORMAT=$((NB_TO_FORMAT + 1))
# In case of full repo, being silent
#echo "src/$FILE_TO_CHECK"
echo "src/$FILE_TO_CHECK" >> ./oai_rules_result_list.txt
fi
done
done
echo "Nb Files that do NOT follow OAI rules: $NB_TO_FORMAT over $NB_TOTAL checked!"
echo "NB_FILES_FAILING_CHECK=$NB_TO_FORMAT" > ./oai_rules_result.txt
echo "NB_FILES_CHECKED=$NB_TOTAL" >> ./oai_rules_result.txt
exit 0
fi
checker=0
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-h|--help)
shift
usage
exit 0
;;
-sb|--src-branch)
SOURCE_BRANCH="$2"
let "checker|=0x1"
shift
shift
;;
-tb|--target-branch)
TARGET_BRANCH="$2"
let "checker|=0x2"
shift
shift
;;
*)
echo "Syntax Error: unknown option: $key"
echo ""
usage
exit 1
esac
done
if [ $checker -ne 3 ]
then
echo "Source Branch is : $SOURCE_BRANCH"
echo "Target Branch is : $TARGET_BRANCH"
echo ""
echo "Syntax Error: missing option"
echo ""
usage
exit 1
fi
# Merge request scenario
MERGE_COMMMIT=`git log -n1 --pretty=format:%H`
if [ -f .git/refs/remotes/origin/$TARGET_BRANCH ]
then
TARGET_INIT_COMMIT=`cat .git/refs/remotes/origin/$TARGET_BRANCH`
else
TARGET_INIT_COMMIT=`git log -n1 --pretty=format:%H origin/$TARGET_BRANCH`
fi
echo " ---- Checking the modified files by the merge request ----"
echo ""
echo "Source Branch is : $SOURCE_BRANCH"
echo "Target Branch is : $TARGET_BRANCH"
echo "Merged Commit is : $MERGE_COMMMIT"
echo "Target Init is : $TARGET_INIT_COMMIT"
echo ""
echo " ----------------------------------------------------------"
echo ""
# Retrieve the list of modified files since the latest develop commit
MODIFIED_FILES=`git log $TARGET_INIT_COMMIT..$MERGE_COMMMIT --oneline --name-status | egrep "^M|^A" | sed -e "s@^M\t*@@" -e "s@^A\t*@@" | sort | uniq`
NB_TO_FORMAT=0
NB_TOTAL=0
if [ -f oai_rules_result.txt ]
then
rm -f oai_rules_result.txt
fi
if [ -f oai_rules_result_list.txt ]
then
rm -f oai_rules_result_list.txt
fi
for FULLFILE in $MODIFIED_FILES
do
filename=$(basename -- "$FULLFILE")
EXT="${filename##*.}"
if [ $EXT = "c" ] || [ $EXT = "h" ] || [ $EXT = "cpp" ] || [ $EXT = "hpp" ]
then
SRC_FILE=`echo $FULLFILE | sed -e "s#src/##"`
TO_FORMAT=`clang-format -output-replacements-xml ${SRC_FILE} 2>&1 | grep -v replacements | grep -c replacement`
NB_TOTAL=$((NB_TOTAL + 1))
if [ $TO_FORMAT -ne 0 ]
then
NB_TO_FORMAT=$((NB_TO_FORMAT + 1))
echo $FULLFILE
echo $FULLFILE >> ./oai_rules_result_list.txt
fi
fi
done
echo ""
echo " ----------------------------------------------------------"
echo "Nb Files that do NOT follow OAI rules: $NB_TO_FORMAT over $NB_TOTAL checked!"
echo "NB_FILES_FAILING_CHECK=$NB_TO_FORMAT" > ./oai_rules_result.txt
echo "NB_FILES_CHECKED=$NB_TOTAL" >> ./oai_rules_result.txt
exit 0
// *INDENT-OFF* cppcheck doesn't like "astyling" this file!!!!
// /*
// * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
// * contributor license agreements. See the NOTICE file distributed with
// * this work for additional information regarding copyright ownership.
// * The OpenAirInterface Software Alliance licenses this file to You under
// * the OAI Public License, Version 1.1 (the "License"); you may not use this file
// * except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.openairinterface.org/?page_id=698
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// *-------------------------------------------------------------------------------
// * For more information about the OpenAirInterface (OAI) Software Alliance:
// * contact@openairinterface.org
// */
//*****************************************************************************
//-----------------------------------------------------------------------------
// *INDENT-ON*
#!/bin/bash
#/*
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
# * contributor license agreements. See the NOTICE file distributed with
# * this work for additional information regarding copyright ownership.
# * The OpenAirInterface Software Alliance licenses this file to You under
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
# * except in compliance with the License.
# * You may obtain a copy of the License at
# *
# * http://www.openairinterface.org/?page_id=698
# *
# * Unless required by applicable law or agreed to in writing, software
# * distributed under the License is distributed on an "AS IS" BASIS,
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# * See the License for the specific language governing permissions and
# * limitations under the License.
# *-------------------------------------------------------------------------------
# * For more information about the OpenAirInterface (OAI) Software Alliance:
# * contact@openairinterface.org
# */
function usage {
echo "OAI GitLab merge request applying script"
echo " Original Author: Raphael Defosseux"
echo ""
echo "Usage:"
echo "------"
echo ""
echo " doGitLabMerge.sh [OPTIONS] [MANDATORY_OPTIONS]"
echo ""
echo "Mandatory Options:"
echo "------------------"
echo ""
echo " --src-branch #### OR -sb ####"