此版本的 GitHub Enterprise 已停止服务 2021-09-23. 即使针对重大安全问题,也不会发布补丁。 要获得更好的性能、改进的安全性和新功能,请升级到 GitHub Enterprise 的最新版本。 如需升级方面的帮助,请联系 GitHub Enterprise 支持

创建预接收挂钩脚本

使用预接收挂钩脚本创建基于内容来接受或拒绝推送的要求。

您可以在 github/platform-samples 仓库中查看 GitHub Enterprise Server 的预接收挂钩示例。

编写预接收挂钩脚本

预接收挂钩脚本在 您的 GitHub Enterprise Server 实例 上的预接收挂钩环境中执行。 创建预接收挂钩脚本时,请考虑可用的输入、输出、退出状态和环境变量。

输入 (stdin)

推送发生后,在为远程仓库更新任何引用之前,在 您的 GitHub Enterprise Server 实例 上的 git-receive-pack 进程将调用预接收挂钩脚本。 脚本 stdin 的标准输入是一个字符串,对每个要更新的 ref 包含一行。 每行都包含 ref 的旧对象名称、引用的新对象名称和 ref 的全名。

<old-value> SP <new-value> SP <ref-name> LF

此字符串表示以下参数。

参数描述
<old-value>存储在 ref 中的旧对象名称。
当您创建新的 ref 时,值是 40 个零。
<new-value>要存储在 ref 中的新对象名称。
当您删除 ref 时,值是 40 个零。
<ref-name>ref 的全名。

有关 git-receive-pack 的更多信息,请参阅 Git 文档中的“git-receive-pack”。 有关 ref 的更多信息,请参阅 Pro Git 中的“Git 引用”。

输出 (stdout)

脚本 stdout 的标准输出传回客户端。 任何 echo 语句将在命令行或用户界面上对用户可见。

退出状态

预接收脚本的退出状态决定是否接受推送。

Exit-status 值操作
0将接受推送。
非零将拒绝推送。

环境变量

除了预接收挂钩脚本 stdin 的标准输入外,,GitHub Enterprise Server 在 Bash 环境中为您的脚本执行提供以下变量。 有关预接收钩脚本 stdin 的更多信息,请参阅“输入 (stdin)”。

预接收挂钩脚本可使用不同的环境变量,具体取决于触发脚本运行的因素。

始终可用

以下变量在预接收挂钩环境中始终可用。

变量描述示例值
$GIT_DIR
实例上远程仓库的路径/data/user/repositories/a/ab/
a1/b2/34/100001234/1234.git
$GIT_PUSH_OPTION_COUNT
由客户端使用 --pub-option 发送的推送选项数量。 更多信息请参阅 Git 文档中的“git-push”。1
$GIT_PUSH_OPTION_N
其中 N 是一个从 0 开始的整数,此变量包含客户端发送的推送选项字符串。 发送的第一个选项存储在 GIT_PUSH_OPTION_0 中,发送的第二个选项存储在 GIT_PUSH_OPTION_1 中,依此类推。 关于推送选项的更多信息,请参阅 Git 文档中的“git-push”。abcd
$GIT_USER_AGENT
推送更改的 Git 客户端发送的 user-agent 字符串。git/2.0.0
$GITHUB_REPO_NAME
NAME/OWNER 格式更新的仓库名称octo-org/hello-enterprise
$GITHUB_REPO_PUBLIC
表示更新的仓库是否公开的布尔值
  • true:仓库的可见性是公开的
  • false:仓库的可见性是私密或内部的
$GITHUB_USER_IP
发起推送的客户端 IP 地址192.0.2.1
$GITHUB_USER_LOGIN
发起推送的帐户的用户名octocat

可用于从 Web 界面或 API 推送

当触发挂钩的 ref 更新通过 GitHub Enterprise Server 的 Web 界面或 API 进行时,$GITHUB_VIA 变量可用于预接收挂钩环境。 该值描述了更新 ref 的操作。

操作更多信息
auto-merge deployment api
通过 API 创建的部署自动合并基础分支REST API 文档中的“仓库
blob#save
在 Web 界面中更改文件的内容"Editing files"
branch merge api
通过 API 合并分支REST API 文档中的“仓库
branches page delete button
在 Web 界面中删除分支在仓库内创建和删除分支
git refs create api
通过 API 创建 refREST API 文档中的“Git 数据库
git refs delete api
通过 API 删除 refREST API 文档中的“Git 数据库
git refs update api
通过 API 更新 refREST API 文档中的“Git 数据库
git repo contents api
通过 API 更改文件的内容REST API 文档中的“仓库
merge base into head
当基础分支需要严格的状态检查时,从基础分支更新主题分支(通过拉取请求中的更新分支"关于受保护分支"
pull request branch delete button
在 Web 界面中从拉取请求中删除主题分支"删除和恢复拉取请求中的分支"
pull request branch undo button
在 Web 界面中从拉取请求中恢复主题分支"删除和恢复拉取请求中的分支"
pull request merge api
通过 API 合并拉取请求REST API 文档中的“拉取
pull request merge button
Web 界面中拉取请求的合并"合并拉取请求"
拉取请求还原按钮
还原拉取请求"接收拉取请求"
releases delete button
删除发行版管理仓库中的发行版
stafftools branch restore
从站点管理员仪表板中还原分支"站点管理员仪表板"
tag create api
通过 API 创建标签REST API 文档中的“Git 数据库
slumlord (#SHA)
通过 Subversion 提交"支持 Subversion 客户端"
web branch create
通过 Web 界面创建分支在仓库内创建和删除分支

可用于拉取请求合并

当触发挂钩的推送由于拉取请求请求合并而成为推送时,以下变量在预接收挂钩环境中可用。

变量描述示例值
$GITHUB_PULL_REQUEST_AUTHOR_LOGIN
编写拉取请求的帐户的用户名octocat
$GITHUB_PULL_REQUEST_HEAD
拉取请求的主题分支的名称,格式为 USERNAME:BRANCHoctocat:fix-bug
$GITHUB_PULL_REQUEST_BASE
拉取请求的基础分支的名称,格式为 USERNAME:BRANCHoctocat:main

可用于使用 SSH 身份验证的推送

变量描述示例值
$GITHUB_PUBLIC_KEY_FINGERPRINT
推送更改的用户的公钥指纹a1:b2:c3:d4:e5:f6:g7:h8:i9:j0:k1:l2:m3:n4:o5:p6

设置权限并将预接收挂钩推送到 GitHub Enterprise Server

您的 GitHub Enterprise Server 实例 上的仓库中包含预接收挂钩脚本。 站点管理员必须考虑仓库权限,确保只有适当的用户才能访问。

我们建议将挂钩合并到单个仓库。 如果统一的挂钩仓库是公共的,则可以使用 README.md 来解释策略强制实施。 此外,也可以通过拉取请求接受贡献。 但是,只能从默认分支添加预接收挂钩。 对于测试工作流程,应使用具有配置的仓库的分支。

  1. 对于 Mac 用户,确保脚本具有执行权限:

    $ sudo chmod +x SCRIPT_FILE.sh

    对于 Windows 用户,确保脚本具有执行权限:

    git update-index --chmod=+x SCRIPT_FILE.sh
  2. 在 您的 GitHub Enterprise Server 实例 提交并推送到指定的预接收挂钩仓库。

    $ git commit -m "YOUR COMMIT MESSAGE"
    $ git push
  3. 在 GitHub Enterprise Server 实例上创建预接收挂钩

在本地测试预接收脚本

在 您的 GitHub Enterprise Server 实例 上创建或更新预接收挂钩脚本之前,您可以在本地对其进行测试。 一种方法是创建本地 Docker 环境以充当可以执行预接收挂钩的远程仓库。

  1. 确保 Docker 在本地安装

  2. 创建一个名为 Dockerfile.dev 的文件,其中包含:

    FROM gliderlabs/alpine:3.3
    RUN \
      apk add --no-cache git openssh bash && \
      ssh-keygen -A && \
      sed -i "s/#AuthorizedKeysFile/AuthorizedKeysFile/g" /etc/ssh/sshd_config && \
      adduser git -D -G root -h /home/git -s /bin/bash && \
      passwd -d git && \
      su git -c "mkdir /home/git/.ssh && \
      ssh-keygen -t ed25519 -f /home/git/.ssh/id_ed25519 -P '' && \
      mv /home/git/.ssh/id_ed25519.pub /home/git/.ssh/authorized_keys && \
      mkdir /home/git/test.git && \
      git --bare init /home/git/test.git"
    
    VOLUME ["/home/git/.ssh", "/home/git/test.git/hooks"]
    WORKDIR /home/git
    
    CMD ["/usr/sbin/sshd", "-D"]
    
  3. 创建一个名为 always_reject.sh 的测试预接收脚本。 此示例脚本将拒绝所有推送,这对于锁定仓库非常有用:

    #!/usr/bin/env bash
    
    echo "error: rejecting all pushes"
    exit 1
    
  4. 确保 always_reject.sh 脚本具有执行权限:

    $ chmod +x always_reject.sh
  5. 从包含 Dockerfile.dev 的目录中,构建一个镜像:

    $ docker build -f Dockerfile.dev -t pre-receive.dev .
    > Sending build context to Docker daemon 3.584 kB
    > Step 1 : FROM gliderlabs/alpine:3.3
    >  ---> 8944964f99f4
    > Step 2 : RUN apk add --no-cache git openssh bash && ssh-keygen -A && sed -i "s/#AuthorizedKeysFile/AuthorizedKeysFile/g"  /etc/ssh/sshd_config && adduser git -D -G root -h /home/git -s /bin/bash && passwd -d git && su git -c "mkdir /home/git/.ssh && ssh-keygen -t ed25519 -f /home/git/.ssh/id_ed25519 -P ' && mv /home/git/.ssh/id_ed25519.pub /home/git/.ssh/authorized_keys && mkdir /home/git/test.git && git --bare init /home/git/test.git"
    >  ---> Running in e9d79ab3b92c
    > fetch http://alpine.gliderlabs.com/alpine/v3.3/main/x86_64/APKINDEX.tar.gz
    > fetch http://alpine.gliderlabs.com/alpine/v3.3/community/x86_64/APKINDEX.tar.gz
    ....truncated output....
    > OK: 34 MiB in 26 packages
    > ssh-keygen: generating new host keys: RSA DSA ECDSA ED25519
    > Password for git changed by root
    > Generating public/private ed25519 key pair.
    > Your identification has been saved in /home/git/.ssh/id_ed25519.
    > Your public key has been saved in /home/git/.ssh/id_ed25519.pub.
    ....truncated output....
    > Initialized empty Git repository in /home/git/test.git/
    > Successfully built dd8610c24f82
  6. 运行包含生成的 SSH 密钥的数据容器:

    $ docker run --name data pre-receive.dev /bin/true
  7. 将测试预接收挂钩 always_reject.sh 复制到数据容器中:

    $ docker cp always_reject.sh data:/home/git/test.git/hooks/pre-receive
  8. 启动一个运行 sshd 的应用程序容器并执行挂钩。 记下返回的容器 ID:

    $ docker run -d -p 52311:22 --volumes-from data pre-receive.dev
    > 7f888bc700b8d23405dbcaf039e6c71d486793cad7d8ae4dd184f4a47000bc58
  9. 将生成的 SSH 密钥从数据容器复制到本地计算机:

    $ docker cp data:/home/git/.ssh/id_ed25519 .
  10. 修改远程测试仓库并将其推送到 Docker 容器中的 test.git 仓库。 此示例使用了 git@github.com:octocat/Hello-World.git,但您可以使用想要的任何仓库。 此示例假定您的本地计算机 (127.0.0.1) 绑定了端口 52311,但如果 docker 在远程计算机上运行,则可以使用不同的 IP 地址。

    $ git clone git@github.com:octocat/Hello-World.git
    $ cd Hello-World
    $ git remote add test git@127.0.0.1:test.git
    $ GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p 52311 -i ../id_ed25519" git push -u test main
    > Warning: Permanently added '[192.168.99.100]:52311' (ECDSA) to the list of known hosts.
    > Counting objects: 7, done.
    > Delta compression using up to 4 threads.
    > Compressing objects: 100% (3/3), done.
    > Writing objects: 100% (7/7), 700 bytes | 0 bytes/s, done.
    > Total 7 (delta 0), reused 7 (delta 0)
    > remote: error: rejecting all pushes
    > To git@192.168.99.100:test.git
    >  ! [remote rejected] main -> main (pre-receive hook declined)
    > error: failed to push some refs to 'git@192.168.99.100:test.git'

    请注意,在执行预接收挂钩并回显脚本中的输出后,将拒绝推送。

延伸阅读