0. 背景
随着各种开发人员在某个项目的尝试维护之后,无可避免的会产生大量的分支,而且很多人默认是不会在merge release之后删除源分支(可能处于随时二次修复重新使用历史分支等目的),也不会在创建MR的时候将 Delete source branch when merge request is accepted. 勾上,导致大量已合并的但是无用的分支存在,对后续持续维护有一定影响,专门抽时间一个个删除又显得很低效,本着重复动作超过三次就使用自动工具解决的想法,基于此想到通过gitlab的定时流水线任务来实现定期删除分支任务。
1. 配置方式
1.1. 流水线任务配置
首先需要在仓库中配置一个流水线任务;
首先新增一个阶段, eg: branh-auto-delete
将以下任务逻辑添加:
Custom-Auto-Delete-Branch:
stage: branh-auto-delete
image: 你能获取到的一个docker镜像,需要包含shell, git, curl, jq等命令
variables:
<<: *gitlab-variables
tags:
- official
script:
- echo Job:Custom
- echo "will try to delete"
- echo $PATH
- export PATH=$PATH:$MAVEN_HOMEN_HOME/bin
- |
# 使用预定义的环境变量
GITLAB_URL="$GITLAB_HOST"
PRIVATE_TOKEN="$GITLAB_TOKEN"
PROJECT_ID="$CI_PROJECT_ID"
MONTHS_AGO="${MONTHS_AGO:-3}" # 默认值为3个月
deleted_branch=""
# 获取当前时间的三个月前的日期
three_months_ago=$(date -d "-${MONTHS_AGO} months" +%s)
echo "$three_months_ago"
# 项目的tag
tags=$(curl --silent --header "PRIVATE-TOKEN: $PRIVATE_TOKEN" "$GITLAB_URL/api/v4/projects/$PROJECT_ID/repository/tags")
# 循环获取所有页面的分支
# 初始化分页参数
page=1
per_page=100
branches=""
echo "get all branch"
while true; do
response=$(curl --silent --header "PRIVATE-TOKEN: $PRIVATE_TOKEN" "$GITLAB_URL/api/v4/projects/$PROJECT_ID/repository/branches?per_page=$per_page&page=$page")
branch_count=$(echo "$response" | jq '. | length')
# 如果当前页面没有分支,退出循环
if [ "$branch_count" -eq 0 ]; then
break
fi
# 累积所有分支(jq可以处理)
branches="$branches$response"
page=$((page + 1))
done
echo "$branches"
# 解析分支信息
echo "$branches" | jq -c '.[] | select(.protected == false)' | while read -r branch; do
branch_name=$(echo "$branch" | jq -r '.name')
echo -e "\r\n"
echo " "
echo "-------- [$branch_name] --------"
echo "check branch: [$branch_name]"
# 跳过 'release' 和 'master' 分支
if [ "$branch_name" == "release" ] || [ "$branch_name" == "master" ]; then
echo "Skipping branch: [$branch_name]"
continue
fi
# 获取分支的最后一次提交时间
last_commit_date=$(echo "$branch" | jq -r '.commit.committed_date')
last_commit_timestamp=$(date -d "$last_commit_date" +%s)
echo "[$branch_name] last_commit_date: $last_commit_date"
echo "[$branch_name] last_commit_timestamp: $last_commit_timestamp, $three_months_ago"
# 检查最后一次提交时间是否早于三个月前
if [ "$last_commit_timestamp" -ge "$three_months_ago" ]; then
echo "Branch $branch_name has recent activity. Skipping..."
continue
fi
# 检查分支是否合并到 release
merge_requests=$(curl --silent --header "PRIVATE-TOKEN: $PRIVATE_TOKEN" "$GITLAB_URL/api/v4/projects/$PROJECT_ID/merge_requests?state=merged&source_branch=$branch_name")
has_merged_to_release=$(echo "$merge_requests" | jq -e '.[] | select(.target_branch == "release")' > /dev/null; echo $?)
echo "[$branch_name] merge status: $has_merged_to_release"
# 如果没有合并到 release,则跳过
if [ "$has_merged_to_release" -ne 0 ]; then
echo "Branch [$branch_name] has not been merged to release. Skipping..."
continue
fi
# 检查分支是否有标签
has_tag=$(echo "$tags" | jq -e --arg branch "$branch_name" '.[] | select(.commit.id == $branch)' > /dev/null; echo $?)
# 如果没有标签,则删除分支
if [ "$has_tag" -ne 0 ]; then
echo "Deleting branch: [$branch_name]"
curl --request DELETE --header "PRIVATE-TOKEN: $PRIVATE_TOKEN" "$GITLAB_URL/api/v4/projects/$PROJECT_ID/repository/branches/$branch_name"
deleted_branch="$deleted_branch$branch_name"$'\n'
fi
echo "delete: $deleted_branch"
done
only:
- schedules
实际效果是 已经合并到release且没有tag标记的分支才会被删除
1.2. 添加定时任务
当然这个任务默认是不会每次cici的流水线任务中执行,默认只在被定时任务调度的时候才会执行
进入到仓库的 build→pipeline_schedules
添加定时流水线的配置:
- desc: 随便写
- Interval Pattern: 定时的频率,
15 14 * * 5表示 每周五的14:15:00 执行一次 - 时区默认北京
- MONTHS_AGO变量表示多久前未合并的分支才会被删除,不填默认3个月
保存之后可以手动执行一次看一下效果:
1.3. done!
2. 逻辑实现说明
本质上是一个shell脚本,核心逻辑为:
-
为了实现定期删除已合并到release的分支,我们首先需要获取到所有的分支,通过
$GITLAB_URL/api/v4/projects/$PROJECT_ID/repository/branches?per_page=$per_pagepage=$page"这个接口即可遍历获取到所有的分支 -
通过
jq命令拆解获取所有的分支名,并跳过release|master分支 -
通过
jq -r '.commit.committed_date'获取分支对应的最后提交时间 -
过滤掉早于一定时间的分支
-
通过
$GITLAB_URL/api/v4/projects/$PROJECT_ID/merge_requests?state=merged source_branch=$branch_name获取分支是否存在合并的MR,并check是否已经合并到release -
check是否存在人为标注的 tag
-
通过
$GITLAB_URL/api/v4/projects/$PROJECT_ID/repository/branches/$branch_name删除分支
3. todo
可以自动配置的方式