文档来源:https://archives.docs.gitlab.com/18.9/administration/server_hooks/?tab=Linux+package+(Omnibus)
一、概述
Git Server Hooks 是运行在 GitLab 服务端的自定义逻辑,可用于提交规范校验、分支权限控制、状态触发任务等。
支持三种标准 Git 钩子:
pre-receive:接收推送前执行,可拒绝整个推送update:每个分支更新前执行一次,可拒绝单分支post-receive:全部更新完成后执行,不可拒绝推送
适用版本:GitLab Free/Premium/Ultimate(自托管)
部署方式:Linux package (Omnibus)
二、前置条件
- GitLab 服务器 root 权限
- Gitaly 配置文件路径(默认):
/var/opt/gitlab/gitaly/config.toml - 钩子脚本必须可执行 ,属主为
git:git - 脚本不匹配备份文件后缀(如
*~)
三、全局服务器钩子配置(官方标准)
1. 配置全局钩子目录
在 /etc/gitlab/gitlab.rb 中配置:
ruby
gitaly['configuration'][:hooks][:custom_hooks_dir] = '/var/opt/gitlab/gitaly/custom_hooks'
官方默认推荐目录:
/var/opt/gitlab/gitaly/custom_hooks
2. 创建目录结构
必须按钩子类型创建 .d 目录:
bash
mkdir -p /var/opt/gitlab/gitaly/custom_hooks/pre-receive.d
3. 放置钩子脚本
- 目录:
pre-receive.d/ - 文件名:任意可执行文件(无后缀要求)
- 首行必须指定解释器:
#!/bin/bash
4. 权限配置(关键)
bash
chmod +x /var/opt/gitlab/gitaly/custom_hooks/pre-receive.d/*.sh
chown -R git:git /var/opt/gitlab/gitaly/custom_hooks
chmod -R 755 /var/opt/gitlab/gitaly/custom_hooks
5. 重载生效
bash
gitlab-ctl reconfigure
gitlab-ctl restart gitaly
四、执行顺序与规则
- 优先执行 GitLab 内置钩子
- 执行项目级钩子:
repo.git/custom_hooks/pre-receive.d/* - 执行全局钩子:
custom_hooks_dir/pre-receive.d/* - 按文件名字母序执行
- 任意钩子
exit 1即中断推送
五、支持的环境变量
GitLab 内置变量
| 变量 | 说明 |
|---|---|
| GL_ID | 操作用户/key 标识(user-123 / key-456) |
| GL_USERNAME | GitLab 用户名 |
| GL_PROJECT_PATH | 项目路径 |
| GL_REPOSITORY | 项目 ID(project-1234) |
| GL_PROTOCOL | 协议:ssh / http / web |
Git 内置变量
GIT_OBJECT_DIRECTORYGIT_ALTERNATE_OBJECT_DIRECTORIESGIT_PUSH_OPTION_COUNT
六、自定义错误信息规范
为在终端与 Web 端清晰展示错误,必须使用官方前缀:
bash
echo "GL-HOOK-ERR: 仅允许创建 main 和 dev 分支!"
七、最佳实践:分支硬限制脚本(生产可用)
目标
全局强制仓库仅允许 main + dev 两个分支,禁止创建任何其他分支。
脚本路径
/var/opt/gitlab/gitaly/custom_hooks/pre-receive.d/branch-restrict.sh
脚本内容
bash
#!/bin/bash
while read oldrev newrev refname; do
if [[ "$oldrev" == "0000000000000000000000000000000000000000" ]]; then
if [[ "$refname" != "refs/heads/main" && "$refname" != "refs/heads/dev" ]]; then
echo "GL-HOOK-ERR: 禁止创建分支!仅允许 main 和 dev 分支。"
exit 1
fi
fi
done
exit 0
群组限制
shell
cat > /var/opt/gitlab/gitaly/custom_hooks/pre-receive.d/branch-group-restrict.sh << 'EOF'
#!/bin/bash
# GitLab 全局分支限制策略
# 规则:
# 1. 个人命名空间(username/*):不限制,可自由创建分支
# 2. 群组命名空间(group/*):仅允许 main 和 dev 分支
# 获取环境变量(GitLab 自动注入)
PROJECT_PATH="$GL_PROJECT_PATH"
USERNAME="$GL_USERNAME"
# ===================== 核心逻辑 =====================
# 如果是【个人仓库】 → 直接放行
# 逻辑:判断路径是否以 "用户名/" 开头
if [[ "$PROJECT_PATH" == "${USERNAME}/"* ]]; then
exit 0
fi
# ===================== 群组仓库分支限制 =====================
while read -r oldrev newrev refname; do
# 仅拦截:创建新分支(oldrev 为全 0)
if [[ "$oldrev" == "0000000000000000000000000000000000000000" ]]; then
# 分支白名单校验
if [[ "$refname" != "refs/heads/main" && "$refname" != "refs/heads/dev" ]]; then
echo "GL-HOOK-ERR: [组织合规] 群组仓库仅允许使用 main / dev 分支,禁止创建自定义分支!"
exit 1
fi
fi
done
exit 0
EOF
白名单模式
shell
#!/bin/bash
# ==============================================================================
# 配置区
# ==============================================================================
# 使用正则表达式匹配顶级群组,更高效且易读
# 匹配 ros, hardware, tools, web, algo 下的所有子路径
LIMIT_GROUPS_REGEX="^(ros|hardware|tools|web|algo)/"
# 允许的分支(使用数组配合正则判断)
ALLOWED_BRANCHES_REGEX="^(main|dev)$"
# ==============================================================================
# 核心逻辑
# ==============================================================================
# 获取环境变量
PROJECT="$GL_PROJECT_PATH"
# 1. 快速判定:是否在限制名单内
if [[ ! "$PROJECT" =~ $LIMIT_GROUPS_REGEX ]]; then
exit 0
fi
# 2. 循环处理推送流(标准输入获取 oldrev, newrev, refname)
while read -r oldrev newrev refname; do
# 仅处理【分支】引用 (refs/heads/),忽略 Tags 或其他
if [[ "$refname" != refs/heads/* ]]; then
continue
fi
# 3. 判定是否为【新建分支】动作 (oldrev 为全 0)
if [[ "$oldrev" == "0000000000000000000000000000000000000000" ]]; then
# 提取分支短名称
branch="${refname#refs/heads/}"
# 4. 校验分支名是否符合允许的正则
if [[ ! "$branch" =~ $ALLOWED_BRANCHES_REGEX ]]; then
echo "--------------------------------------------------------"
echo "GL-HOOK-ERR: ❌ [组织合规性拦截]"
echo "项目路径: $PROJECT"
echo "错误原因: 该业务群组受限,禁止创建自定义分支 '$branch'"
echo "允许分支: main, dev"
echo "--------------------------------------------------------"
exit 1
fi
fi
done
exit 0
八、常见问题排查
1. 钩子不生效
- 目录是否为
custom_hooks/pre-receive.d - 权限是否为
git:git+chmod +x - 是否重启
gitaly - 是否在
gitlab.rb正确配置custom_hooks_dir
2. pre-receive hook declined
-
查看钩子输出错误信息
-
查看 Gitaly 日志:
bashgrep PreReceiveHook /var/log/gitlab/gitaly/current
3. SSH 不触发、HTTP 触发
- 新版 GitLab 统一走 Gitaly Hooks,SSH/HTTP 表现一致
- 确认使用全局 Gitaly 钩子,而非旧版 git-shell 目录
九、总结(可直接用于技术规范文档)
- GitLab 18.9 全局服务端钩子唯一标准目录 :
/var/opt/gitlab/gitaly/custom_hooks - 必须使用
.d目录结构存放钩子 - 权限必须是
git:git+ 可执行 - 分支限制推荐使用白名单模式(仅允许 main/dev)
- 错误信息必须以
GL-HOOK-ERR:开头 - 配置完成需重启
gitaly生效