bash
#!/bin/bash
# 批量为目标 GitLab 所有项目的默认分支开启 protected branch 保护
# 保护规则:禁止 force push,只有 Maintainer 可以 push 和 merge
set -euo pipefail
# ===================== 配置项 =====================
DST_GITLAB_URL="https://127.0.0.1"
DST_ACCESS_TOKEN="yehMwepEcx8ivwd2ewd"
PER_PAGE=100
# 保护级别:
# 40 = Maintainer
# 30 = Developer
# 0 = No one
PUSH_ACCESS_LEVEL=40
MERGE_ACCESS_LEVEL=40
ALLOW_FORCE_PUSH=false
# ==================================================
echo "========================================"
echo "批量开启目标 GitLab 项目的分支保护"
echo "目标 GitLab: $DST_GITLAB_URL"
echo "保护规则: push=${PUSH_ACCESS_LEVEL}(Maintainer), merge=${MERGE_ACCESS_LEVEL}(Maintainer), force_push=${ALLOW_FORCE_PUSH}"
echo "========================================"
echo ""
PAGE=1
TOTAL=0
SUCCESS=0
SKIP=0
FAIL=0
while true; do
# 获取项目列表(分页)
projects=$(curl -s -k \
--header "PRIVATE-TOKEN: $DST_ACCESS_TOKEN" \
"$DST_GITLAB_URL/api/v4/projects?per_page=$PER_PAGE&page=$PAGE&membership=true")
# 检查是否还有项目
count=$(echo "$projects" | jq 'length')
if [ "$count" -eq 0 ] 2>/dev/null; then
break
fi
echo "$projects" | jq -c '.[]' | while IFS= read -r proj; do
proj_id=$(echo "$proj" | jq -r '.id')
proj_name=$(echo "$proj" | jq -r '.path_with_namespace')
default_branch=$(echo "$proj" | jq -r '.default_branch')
TOTAL=$((TOTAL + 1))
if [ -z "$default_branch" ] || [ "$default_branch" = "null" ]; then
echo " [跳过] $proj_name - 无默认分支(空仓库)"
SKIP=$((SKIP + 1))
continue
fi
# 检查该分支是否已被保护
check=$(curl -s -k \
--header "PRIVATE-TOKEN: $DST_ACCESS_TOKEN" \
"$DST_GITLAB_URL/api/v4/projects/$proj_id/protected_branches/$default_branch")
if echo "$check" | jq -e '.name' > /dev/null 2>&1; then
echo " [已保护] $proj_name ($default_branch)"
SKIP=$((SKIP + 1))
continue
fi
# 设置保护
result=$(curl -s -k -X POST \
--header "PRIVATE-TOKEN: $DST_ACCESS_TOKEN" \
"$DST_GITLAB_URL/api/v4/projects/$proj_id/protected_branches" \
--data-urlencode "name=$default_branch" \
--data "push_access_level=$PUSH_ACCESS_LEVEL" \
--data "merge_access_level=$MERGE_ACCESS_LEVEL" \
--data "allow_force_push=$ALLOW_FORCE_PUSH")
if echo "$result" | jq -e '.name' > /dev/null 2>&1; then
echo " [成功] $proj_name ($default_branch)"
SUCCESS=$((SUCCESS + 1))
else
error_msg=$(echo "$result" | jq -r '.message // .error // "未知错误"' 2>/dev/null)
echo " [失败] $proj_name ($default_branch) - $error_msg"
FAIL=$((FAIL + 1))
fi
done
# 下一页
PAGE=$((PAGE + 1))
done
echo ""
echo "========================================"
echo "执行完毕!"
echo " - 已保护/跳过: $SKIP"
echo " - 新增保护: $SUCCESS"
echo " - 失败: $FAIL"
echo "========================================"