Skip to main content

此版本的 GitHub Enterprise 将停止服务 2022-10-12. 即使针对重大安全问题,也不会发布补丁。 为了获得更好的性能、更高的安全性和新功能,请升级到最新版本的 GitHub Enterprise。 如需升级帮助,请联系 GitHub Enterprise 支持

创建预接收挂钩脚本

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

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

编写预接收挂钩脚本

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

输入 (stdin)

推送发生后,在为远程存储库更新任何引用之前,在 your GitHub Enterprise Server instance 上的 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
客户端使用 --push-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 界面中更改文件的内容编辑文件
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 文档中的“创建或更新文件内容

可用于拉取请求合并

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

变量说明示例值
$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

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

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

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

    $ sudo chmod +x SCRIPT_FILE.sh

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

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

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

在本地测试预接收脚本

在 your GitHub Enterprise Server instance 上创建或更新预接收挂钩脚本之前,您可以在本地对其进行测试。 一种方法是创建本地 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'

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

延伸阅读