gitlab-自动删除过期合并分支

323 阅读3分钟

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

可以自动配置的方式