云计算百科
云计算领域专业知识百科平台

【Git】GitLab 18.9 全局服务器钩子(Server Hooks)官方规范与落地实践

文档来源:https://archives.docs.gitlab.com/18.9/administration/server_hooks/?tab=Linux+package+%28Omnibus%29


一、概述

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 中配置:

    gitaly['configuration'][:hooks][:custom_hooks_dir] = '/var/opt/gitlab/gitaly/custom_hooks'

    官方默认推荐目录:
    /var/opt/gitlab/gitaly/custom_hooks

    2. 创建目录结构

    必须按钩子类型创建 .d 目录:

    mkdir -p /var/opt/gitlab/gitaly/custom_hooks/pre-receive.d

    3. 放置钩子脚本

    • 目录:pre-receive.d/
    • 文件名:任意可执行文件(无后缀要求)
    • 首行必须指定解释器:#!/bin/bash

    4. 权限配置(关键)

    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. 重载生效

    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_DIRECTORY
    • GIT_ALTERNATE_OBJECT_DIRECTORIES
    • GIT_PUSH_OPTION_COUNT

    六、自定义错误信息规范

    为在终端与 Web 端清晰展示错误,必须使用官方前缀:

    echo "GL-HOOK-ERR: 仅允许创建 main 和 dev 分支!"


    七、最佳实践:分支硬限制脚本(生产可用)

    目标

    全局强制仓库仅允许 main + dev 两个分支,禁止创建任何其他分支。

    脚本路径

    /var/opt/gitlab/gitaly/custom_hooks/pre-receive.d/branch-restrict.sh

    脚本内容

    #!/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

    群组限制

    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

    白名单模式

    #!/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 日志:grep 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 生效
  • 赞(0)
    未经允许不得转载:网硕互联帮助中心 » 【Git】GitLab 18.9 全局服务器钩子(Server Hooks)官方规范与落地实践
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!