前言
最近由于团队人员变更,仓库迁移,需要配置某些仓库的目录权限,但是距离上次调整已经过去多年,又是因时间长短而遗忘的知识,故今日进行文章记录(温故而知新)
PS:文章中示例的项目仓库名称 wind-pro ,请根据实际情况更换正确的项目仓库名称
场景说明:
- 核心开发人员:什么目录都能提交。
- 业务开发人员:只能提交这 3 个目录:
目录示例:
wind-pro-java/src/main/java/com/windseeker/apis/business/
wind-pro-vue/src/views/business/
wind-pro-react/src/views/business/
业务开发如果提交其他目录,GitLab 服务器直接拒绝 push。
第 1 步:登录 GitLab 服务器
ssh root@GitLab服务器IP
第 2 步:创建核心开发人员名单
mkdir -p /opt/wind-git-hooks
cat > /opt/wind-git-hooks/core-developers.txt <<'EOF'
Lehman
zhangsan
lisi
EOF
把里面的 Lehman、zhangsan、lisi 换成核心开发人员的 GitLab 用户名。
注意:这里填的是 GitLab 登录用户名,不是姓名。
然后设置权限:
chown root:git /opt/wind-git-hooks/core-developers.txt
chmod 640 /opt/wind-git-hooks/core-developers.txt
第 3 步:获取项目仓库信息
下面两种方式二选一
1.管理员直接获取
进入 GitLab 管理后台:
Admin Area -> Overview -> Projects -> wind-pro
记录两个值:
- Storage name,通常是: default
- Relative path,类似:@hashed/xx/xx/xxxxxxxxxxxxxx.git
2.命令行获取
执行:
gitlab-rails runner "p=Project.find_by_full_path('product/wind-pro'); puts p.repository_storage; puts p.disk_path"
这里的 product/wind-pro 要换成 GitLab 上真实的项目路径。
比如浏览器地址是:
https://gitlab.xxx.com/product/wind-pro
那就是:
product/wind-pro
命令执行后会输出两行,类似:
default
@hashed/xx/xx/xxxxxxxxxxxxxx
最终获取这两个值:
第一行:default
第二行:@hashed/xx/xx/xxxxxxxxxxxxxx.git
第 4 步:创建拦截脚本
执行:
mkdir -p /opt/wind-git-hooks/custom_hooks
cat > /opt/wind-git-hooks/custom_hooks/pre-receive <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
ZERO="0000000000000000000000000000000000000000"
EMPTY_TREE="4b825dc642cb6eb9a060e54bf8d69288fbee4904"
CORE_USERS_FILE="/opt/wind-git-hooks/core-developers.txt"
is_core_user() {
local user="${GL_USERNAME:-}"
[ -n "$user" ] && grep -Fxq "$user" "$CORE_USERS_FILE"
}
is_allowed_path() {
case "$1" in
wind-pro-java/src/main/java/com/windseeker/apis/business/*) return 0 ;;
wind-pro-vue/src/views/business/*) return 0 ;;
wind-pro-react/src/views/business/*) return 0 ;;
*) return 1 ;;
esac
}
changed_paths() {
local oldrev="$1"
local newrev="$2"
local base
if [ "$oldrev" = "$ZERO" ]; then
base="$EMPTY_TREE"
else
base="$oldrev"
fi
git diff --name-status -M "$base" "$newrev" | while IFS=$'\t' read -r status path1 path2 rest; do
case "$status" in
R*|C*)
echo "$path1"
echo "$path2"
;;
*)
echo "$path1"
;;
esac
done
}
if is_core_user; then
exit 0
fi
bad_paths="$(mktemp)"
trap 'rm -f "$bad_paths"' EXIT
while read -r oldrev newrev refname; do
[ "$newrev" = "$ZERO" ] && continue
if [[ "$refname" == refs/tags/* ]]; then
echo "GL-HOOK-ERR: 普通业务开发不允许创建或修改 tag,请联系核心开发人员。"
exit 1
fi
while IFS= read -r path; do
[ -z "$path" ] && continue
if ! is_allowed_path "$path"; then
echo "$path" >> "$bad_paths"
fi
done < <(changed_paths "$oldrev" "$newrev")
done
if [ -s "$bad_paths" ]; then
echo "GL-HOOK-ERR: Push 被拒绝。"
echo "GL-HOOK-ERR: 业务开发只能修改以下 business 目录:"
echo "GL-HOOK-ERR: - wind-pro-java/src/main/java/com/windseeker/apis/business/**"
echo "GL-HOOK-ERR: - wind-pro-vue/src/views/business/**"
echo "GL-HOOK-ERR: - wind-pro-react/src/views/business/**"
echo "GL-HOOK-ERR:"
echo "GL-HOOK-ERR: 本次提交包含以下非业务目录文件:"
sort -u "$bad_paths" | sed 's/^/GL-HOOK-ERR: - /'
echo "GL-HOOK-ERR:"
echo "GL-HOOK-ERR: 如确实需要修改这些文件,请联系核心开发人员二次确认。"
exit 1
fi
exit 0
EOF
设置权限:
chmod 755 /opt/wind-git-hooks/custom_hooks/pre-receive
chown -R root:git /opt/wind-git-hooks
第 5 步:安装到 wind-pro 仓库
先打包:
cd /opt/wind-git-hooks
tar -cf custom_hooks.tar custom_hooks
然后执行安装命令。
假设第 3 步查出来的是:
default
@hashed/xx/xx/xxxxxxxxxxxxxx.git
那么执行:
cat custom_hooks.tar | sudo -u git -- /opt/gitlab/embedded/bin/gitaly hooks set \
--storage default \
--repository @hashed/xx/xx/xxxxxxxxxxxxxx.git \
--config /var/opt/gitlab/gitaly/config.toml
重点:
- `--storage` 后面填第 3 步第一行。
- `--repository` 后面填第 3 步第二行,并且确保末尾为 `.git`。
第 6 步:测试
让业务开发人员提交这个文件,应该成功:
wind-pro-vue/src/views/business/test/index.vue
再让他提交这个文件,应该失败:
wind-pro-vue/package.json
失败时会看到类似:
Push 被拒绝。
业务开发只能修改以下 business 目录。
本次提交包含以下非业务目录文件:
- wind-pro-vue/package.json
然后让核心开发人员提交 package.json,应该成功。
维护说明
新增核心开发人员,只改这个文件:
vim /opt/wind-git-hooks/core-developers.txt
不用重启 GitLab。
如果以后新增业务目录,比如再允许:
wind-pro-vue/src/api/business/
就修改:
vim /opt/wind-git-hooks/custom_hooks/pre-receive
在 is_allowed_path() 里面加一行,然后重新执行第 5 步安装。
这套方案的效果就是:普通业务开发人员在本地可以随便改,但只要 push 到 GitLab,服务器会自动检查,不符合目录规则就直接拒绝。