在日常工作中,在Gitlab上合并请求的任务并不陌生,在网页上点击几个操作即可完成,但是如果在微服务架构下,如果需要修改多个微服务的配置文件、编排文件,每个微服务都单独由一个Git仓库进行维护,那么在网页上点击合并请求将是一个重复又重复的过程,因此,需要命令式的方式在Gitlab上进行合并请求,在此基础之上,实现批量发起合并请求的快速方式。

1. 获取Access-Token

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
F在这里插入图片描述

可以考虑将权限拉满

在这里插入图片描述

2. 获取项目id

如果公司内网使用Gitlab,需要调整下方的:gitlab.com,例如:你们公司访问路径:https://xxxx/...,那就将gitlab.com换成xxxx即可。具体需看各个公司的Gitlab访问路径。

curl -ks --header "PRIVATE-TOKEN: <Your private-token>" \
     "https://gitlab.com/api/v4/groups/<namespace-urlpath>" \
     | grep -oP '"id":([0-9]+),"description":.*?,"name":".*?"' \
     | grep "<projectName>" | awk -F, '{print $1}' | awk -F: '{print $NF}'

里面有三个参数:

  • <Your private-token>:上一步获取的Token

  • <namespace-urlpath>:命令空间的URL路径,打开你的项目,看上面的URL,如下图。我的项目名为three,前面的路径为“first2671847”,所以,这里<namespace-urlpath>替换成:first2671847
    在这里插入图片描述

  • <projectName>:你的项目名字,如下图所示。
    在这里插入图片描述

3. 获取当前项目下的用户id【可选】

如果你需要指定提交合并请求的:Assignee和Reviewer。接下来需要获取这两个的Id。

在这里插入图片描述

curl -ks --header "PRIVATE-TOKEN: <你的access token>" \
  "https://gitlab.com/api/v4/projects/<项目id>/members/all"

结果类似如下,可以根据usernamename等信息来获取对应的id。

在这里插入图片描述

4. 请求合并

curl -ks --request POST \
  --header "PRIVATE-TOKEN: <你的access token>" \
  --data "source_branch=uat" \
  --data "target_branch=main" \
  --data "title=%E9%A6%96%E6%AC%A1%E7%BC%96%E7%A8%8B" \
  --data "description=%e7%ac%ac%e4%b8%80%e4%b8%aa%e5%90%88%e5%b9%b6%e8%af%b7%e6%b1%82" \
  --data "assignee_ids[]=24746290" \
  --data "reviewer_ids[]=24746290" \
  --data "remove_source_branch=false" \
  "https://gitlab.com/api/v4/projects/64615122/merge_requests"

注意点:

  • source_branch:表示你从那个分支发起合并

  • target_branch:表示你要合并到那个分支

  • title:标题。中文需要使用url编码,否则响应结果为一个:Bad Request。使用Linux进行URL编码后使用。方法如下。

    echo -n "首次编程" | xxd -plain | sed 's/\(..\)/%\1/g' 
    %e9%a6%96%e6%ac%a1%e7%bc%96%e7%a8%8b
    
  • description:描述,同样含有中文需要URL编码,方法如上。

  • assignee_ids:assignee的Id List,如果是多个。另起一行,在新增:--data "assignee_ids[]=<assigneeID>"

  • reviewer_ids:reviewer的Id List,和reviewer_ids的写法一样。

  • remove_source_branch:合并以后是否删除源分支,true表示是,false表示否。

5. 命令化

为了简化,需要将上述的命令处理成一个脚本,这里命名为:mr.sh

#!/bin/bash

if [ "$1" -z ];then
    echo "请输入需要合并的分支"
    exit
fi

if [ "$2" -z ];then
    echo "请输入标题"
    exit
fi


# 变量赋值
source_branch="$1"
# gitlab不直接接收中文,需要URL编码后才可以
title=$(echo -n "$2" | xxd -plain | sed 's/\(..\)/%\1/g')
# 对于描述可有可无
description=$(echo -n "$3" | xxd -plain | sed 's/\(..\)/%\1/g')

# target_branch、assignee_ids、reviewer_ids一般是固定的
# 因此不作为变量传入,需要变化时,使用IDEA进行全量替换


# -k表示不验证证书,-s 表示不显示进度
curl -ks --request POST \
  --header "PRIVATE-TOKEN: <你的access token>" \
  --data "source_branch=$source_branch" \
  --data "target_branch=main" \
  --data "title=$title" \
  --data "description=$description" \
  --data "assignee_ids[]=24746290" \
  --data "reviewer_ids[]=24746290" \
  --data "remove_source_branch=false" \
  "https://gitlab.com/api/v4/projects/64615122/merge_requests"

运行上述脚本:

sh mr.sh "dev" "标题是首次推动" "可有可无的描述"

执行上述脚本的结果就是:在sample项目(id是64615122)中,dev分支发起对main分支的合并请求,标题内容为:“标题是首次推动”,描述为:“可有可无的描述”,assignee和reviewer都为24746290,而且合并以后不会删除dev分支。

这样确实简单了很多,但是如果在微服务架构下,假设有N个微服务,每个服务都由一个Git仓库管理,是不是要准备N份的mr.sh脚本,而且每份还要调整项目id(上面的64615122),是不是要手动运行N次脚本,这是万万不可接受的。由此,引入 批量执行的篇章。

6.批量执行

由于有N个微服务,而且操作都是相同或者相似的,因此能不能只提供一个命令,让程序自动去每个微服务所在仓库中自动去执行一遍这个脚本,这样就可以实现:“一次实现,处处运行”的效果。

先看一下作者的多个微服务的结构图(假设是一些微服务,嘻嘻),然后作者提供一个shell脚本,来完成这种需求。

在这里插入图片描述

提供的"一次实现,处处运行"的脚本就是上述的go.sh,内容如下:

#!/bin/bash

if [ -z "$1" ];then
    echo "请输入有效命令"
    exit
fi

command="$1"

for dir in ./*;do
    if [ -d "$dir" ];then
        cd ./"$dir"
        isGit=$(ls -la | grep .git | wc -l)
        if [ $isGit -ge 1 ];then
            # 获取当前项目名
            projectName=$(echo "$dir" | awk -F/ '{print $NF}')
            # 替换后的新命令
            new_command=$(echo $command | sed "s/_name_/$projectName/g")
            # 执行
            /bin/bash -c "$new_command"
            echo -e "\033[31;40m 【$projectName】已执行完毕【$new_command】命令. \033[0m"
        fi
        cd ../
    fi
done

命令格式就是:

sh go.sh "<command>"

<command>就是你想在每个仓库里面执行的命令,这里以一个需求为引,进行举例演示。
需求:在每个仓库里面生成一个test.txt脚本,内容为"Hello World",提交给git,并推送至远程dev。


# 分解任务
# 1.git checkout -b dev
# 2.echo "Hello world" > test.txt
# 3.git add test.txt
# 4.git commit -m "首次提交"
# 4.git push --set-upstream origin dev 

# 注意,如果命令参数里面有双引号,那么外面那个就用单引号。如果参数里面有单引号,那外面那个就用双引号
# 多个命令用;隔开
sh go.sh 'git checkout -b dev;echo "Hello world" > test.txt;git add test.txt;git commit -m "首次提交";git push --set-upstream origin dev'

# 不得不提的例子: 
# 这里的_name_在运行时,会被替换成项目的名字(看go.sh),因此有需要用到项目名字时,可用_name_代替
sh go.sh 'echo _name_'

先看本地

在这里插入图片描述

再看远程Gitlab

在这里插入图片描述

嗯,符合预期,结果还是非常赞的。

现在需要将那个发起合并请求的mr.sh脚本复制到每个仓库中,然后将里面的 项目号(64615122)替换成每个项目真正对应的项目号。然后在使用go.sh脚本,运行mr.sh脚本就可以实现批量推送了。

  1. 先复制mr.sh到每个仓库
 sh go.sh "cp ../mr.sh ./mr.sh"
  1. 替换mr.sh的项目id的脚本都给你准备好了,注意这个脚本在all目录下,记得用你的 TOKEN 、 命名空间URL变量、要替换的项目ID。
#!/bin/bash


# 提供你的Token
TOKEN="your token"
# 提供你的URL路径上的命名空间
NAMESPACE="first2671847"



curl -ks --header "PRIVATE-TOKEN: ${TOKEN}" \
     "https://gitlab.com/api/v4/groups/${NAMESPACE}" \
     | grep -oP '"id":([0-9]+),"description":.*?,"name":".*?"' > ./projects.txt

echo "您的命名空间内包含的项目如下:"
cat ./projects.txt
echo -e "\n"

for dir in ./*;do
    if [ -d "$dir" ];then
        cd ./"$dir" 
        # shellcheck disable=SC2010
        isGit=$(ls -la | grep .git | wc -l)
        if [ $isGit -ge 1 ];then
            # 获取当前项目名
            projectName=$(echo "$dir" | awk -F/ '{print $NF}')
            pid=$(cat ../projects.txt | grep "\"$projectName\"" | awk -F, '{print $1}' | awk -F: '{print $NF}')
            if [ -z "$pid" ];then
                echo "获取项目【$projectName】ID为空"
            else
            	# 这里是将我的项目ID替换,你要换成你自己
               sed -i s/64615122/"$pid"/g ./mr.sh
            fi

        fi
        cd ../
    fi
done

你可以查看一下所有项目的mr.sh有没有被替换.

sh go.sh "cat ./mr.sh"
  1. 批量发起合并请求吧!

    现在就是见证奇迹的时刻,使用go.sh运行mr.sh,假设如下:

sh go.sh "sh ./mr.sh 'dev' '全球首次提交' '我不喜欢描述'"

快去看远程!

在这里插入图片描述

哈哈,大功告成,以后直接使用一行命令发起合并请求,真是太赞了,不值得一个赞吗?

7.结束

有人觉得,还是不够快。那么如果在疯狂一点,就直接增加文件、提交Git、推送远程、发起合并请求,一气呵成。

# 1. git checkout -b sit
# 2. echo "Hello Sit!" > sit.txt
# 3. git add sit.txt
# 4. git commit -m "增加sit.txt文件"
# 5. git push --set-upstream origin sit
# 6. sh mr.sh "sit" "全球第二次提交"

sh go.sh 'git checkout -b sit;echo "Hello Sit!" > sit.txt;git add sit.txt;git commit -m "增加sit.txt文件";git push --set-upstream origin sit;sh mr.sh "sit" "全球第二次提交"'

快去看你的远程验证吧!!

7.1 别名简化命令

或许有人觉得sh go.sh太不简洁了,当然你可以通过起别名的方法来让其简单:

vim ~/.bashrc

# 打开上面的文件,然后新增下面
alias go="sh go.sh"

# 退出文件以后,进行重新加载
source ~/.bashrc

以后,我们就可以使用如下命令:

go "git pull;git checkout -b uat"

# 不仅可以运行Git命令,所有在Git Bash里面的运行的都可以,赶快试试吧!
go "mvn clean package"          
7.2 并行处理加速速度

当前的go.sh脚本中的命令,是逐个项目执行的,是一种阻塞式的执行,如果涉及到 I/O密集型,例如:向远程推送、发合并请求,速度是变慢。因此,可以考虑下方利用shell的后台运行,来提升速度。

#!/bin/bash

if [ -z "$1" ];then
    echo "请输入有效命令"
    exit
fi

command="$1"

for dir in ./*;do
    if [ -d "$dir" ];then
        cd ./"$dir"
        isGit=$(ls -la | grep .git | wc -l)
        if [ $isGit -ge 1 ];then
            # 获取当前项目名
            projectName=$(echo "$dir" | awk -F/ '{print $NF}')
            # 替换后的新命令
            new_command=$(echo $command | sed "s/_name_/$projectName/g")
            # 后台执行
            /bin/bash -c "$new_command" &
            echo -e "\033[31;40m 【$projectName】已执行完毕【$new_command】命令. \033[0m"
        fi
        cd ../
    fi
done

# 等待所有的进程执行完毕
wait
echo -e "\n所有项目执行完毕!"


有此神器,还怕什么微服务调整配置呀,给我来10个,我要搞10个。哈哈

Logo

一起探索未来云端世界的核心,云原生技术专区带您领略创新、高效和可扩展的云计算解决方案,引领您在数字化时代的成功之路。

更多推荐