【Shell】自动替换指定项目tag并提merge request

背景

日常开发中会涉及到许多升级基础包tag的工作,微服务又比较多,基本都是重复的工作在不同的服务执行,这种情况下用脚本自动完成比较合适。

本文介绍了一个自动化升级Go项目依赖的Shell脚本。该脚本可以批量处理多个微服务项目,自动完成以下操作:

  • 根据配置升级go.mod中的依赖版本
  • 执行go mod tidy
  • 自动创建Git分支并提交代码
  • 推送变更并创建合并请求(MR)

脚本通过配置区域可灵活设置:

  • 需要处理的项目列表
  • 依赖库路径
  • 待升级的依赖包及版本
  • Git分支和提交信息等参数

执行后会输出详细的处理日志,并汇总显示每个项目的处理结果和MR链接,适用于需要批量升级大量微服务依赖版本的场景。

脚本代码

脚本功能 :升级go mod中的包,为UPGRADE_DEPS中配置的版本,并执行go mod tidy,自动提mr,对于超多微服务需要升级tag的工作比较合适。
脚本使用:只需要根据配置区域中的介绍配置即可。

shell 复制代码
#!/bin/bash

# ================= 配置区域 =================

# 1. 业务项目列表
PROJECTS=(
    "testproject"
)

# 2. 依赖库路径
LIBS=(
    "lib"
    "../lib"
)

# 3. 需要升级的依赖列表 "包名:版本"
UPGRADE_DEPS=(
    "https://github.com/DHButterfly/go-spring:v0.0.11"
)

# 4. Git 配置
BASE_BRANCH="master"
TARGET_BRANCH="optimize_dl_update_tag"
COMMIT_MSG="update tag & go mod tidy"
MR_TITLE=$COMMIT_MSG

# ===========================================

GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'

START_DIR=$(pwd)
# 初始化一个数组,用来存储最后生成的 MR 地址
MR_SUMMARY=()

# --- 1. 准备依赖库 (略) ---
echo -e "${YELLOW}>>> 1. 准备本地依赖库环境...${NC}"
for lib_path in "${LIBS[@]}"; do
    if [ -d "$lib_path" ]; then
        (
            cd "$lib_path" || exit
            if [ -n "$(git status --porcelain)" ]; then
                echo -e "${RED}[警告] $lib_path 有未提交更改${NC}"
            else
                git fetch origin "$BASE_BRANCH" > /dev/null 2>&1
                git checkout "$BASE_BRANCH" > /dev/null 2>&1
                git reset --hard "origin/$BASE_BRANCH" > /dev/null 2>&1
                echo -e "${GREEN}$lib_path 已同步${NC}"
            fi
        )
    fi
done

echo -e "${YELLOW}>>> 2. 开始处理业务项目...${NC}"
echo -e "${YELLOW}------------------------------------------------------${NC}"

# --- 2. 处理业务项目 ---

for dir in "${PROJECTS[@]}"; do
    cd "$START_DIR" || exit
    if [ ! -d "$dir" ]; then continue; fi

    echo -e "${YELLOW}正在处理项目: ${dir} ...${NC}"
    cd "$dir" || continue

    if [ ! -d ".git" ] || [ -n "$(git status --porcelain)" ]; then
        echo -e "${RED}[跳过] 非 git 仓库或有未提交更改${NC}"
        continue
    fi

    # 切分支
    git fetch origin "$BASE_BRANCH" > /dev/null 2>&1
    git checkout "$BASE_BRANCH" > /dev/null 2>&1
    git reset --hard "origin/$BASE_BRANCH" > /dev/null 2>&1
    git checkout -B "$TARGET_BRANCH" > /dev/null 2>&1

    # 修改依赖
    has_update=false
    for dep_config in "${UPGRADE_DEPS[@]}"; do
        mod_name="${dep_config%%:*}"
        mod_ver="${dep_config#*:}"
        if grep -q "$mod_name" go.mod; then
            echo -e "更新依赖: $mod_name -> $mod_ver"
            go mod edit -require="${mod_name}@${mod_ver}"
            has_update=true
        fi
    done

    # Tidy
    echo -e "执行 go mod tidy..."
    if ! go mod tidy; then
        echo -e "${RED}[失败] tidy 报错${NC}"
        MR_SUMMARY+=("${RED}[失败]${NC} ${dir}: go mod tidy 执行失败")
        continue
    fi

    # 提交 & 推送 & 捕获输出
    if [ -z "$(git status --porcelain)" ]; then
        echo -e "${GREEN}[无变化] 跳过${NC}"
        MR_SUMMARY+=("${YELLOW}[跳过]${NC} ${dir}: 无文件变化")
    else
        echo -e "${GREEN}[提交并推送]...${NC}"
        git add .
        git commit -m "$COMMIT_MSG" > /dev/null 2>&1
        
        # === 关键修改 ===
        # 1. 执行推送,并将 stderr (2) 重定向到 stdout (1),以便变量 capturing
        # 2. 同时保留输出显示在屏幕上 (虽然变量捕获了,但我们用 echo 再打一遍)
        
        echo -e "正在推送请求 GitLab 创建 MR..."
        
        # 捕获输出
        push_output=$(git push \
            -o merge_request.create \
            -o merge_request.target="$BASE_BRANCH" \
            -o merge_request.title="$MR_TITLE" \
            -o merge_request.remove_source_branch \
            origin "$TARGET_BRANCH" --force 2>&1)
        
        # 打印输出给用户看(因为被变量捕获了,屏幕上如果不 echo 就会一片空白)
        echo "$push_output"

        # 使用正则提取 URL
        # 匹配 https://.../merge_requests/数字
        mr_url=$(echo "$push_output" | grep -o "https://.*/merge_requests/[0-9]*" | head -n 1)

        if [ -n "$mr_url" ]; then
            echo -e "${GREEN}[成功] MR: $mr_url${NC}"
            MR_SUMMARY+=("${GREEN}[成功]${NC} ${dir}: $mr_url")
        else
            # 有时候是已经存在 MR,GitLab 不会返回 URL,或者推送失败
            if echo "$push_output" | grep -q "Everything up-to-date"; then
                 MR_SUMMARY+=("${YELLOW}[跳过]${NC} ${dir}: 代码已是最新,未产生新提交")
            else
                 MR_SUMMARY+=("${YELLOW}[未知]${NC} ${dir}: 推送完成但未捕获到 URL (可能 MR 已存在)")
            fi
        fi
    fi
    echo -e "${YELLOW}------------------------------------------------------${NC}"
done

# --- 3. 最终汇总打印 ---
echo -e "\n\n"
echo -e "${YELLOW}================= 自动化执行汇总 =================${NC}"
for item in "${MR_SUMMARY[@]}"; do
    echo -e "$item"
done
echo -e "${YELLOW}==================================================${NC}"
相关推荐
dingdingfish1 天前
GNU Parallel 学习 - 第1章:How to read this book
bash·shell·gnu·parallel
似霰4 天前
Linux Shell 脚本编程——核心基础语法
linux·shell
似霰5 天前
Linux Shell 脚本编程——脚本自动化基础
linux·自动化·shell
偷学技术的梁胖胖yo6 天前
Shell脚本中连接数据库查询数据报错 “No such file or directory“以及函数传参数组
linux·mysql·shell
纵有疾風起15 天前
【Linux 系统开发】基础开发工具详解:软件包管理器、编辑器。编译器开发实战
linux·服务器·开发语言·经验分享·bash·shell
gis分享者17 天前
Shell 脚本中如何使用 here document 实现多行文本输入? (中等)
shell·脚本·document·多行·文本输入·here
柏木乃一17 天前
基础IO(上)
linux·服务器·c语言·c++·shell
angushine17 天前
CPU脚本并远程部署
shell
赵民勇22 天前
Linux/Unix中install命令全面用法解析
linux·shell
gis分享者23 天前
Shell 脚本中如何使用 trap 命令捕捉和处理信号(中等)
shell·脚本·信号·处理·trap·捕捉