背景
日常开发中会涉及到许多升级基础包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}"