Skip to main content

创建预接收挂钩脚本

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

可以在 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
客户端使用 --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 创建 refGit 参考的 REST API 终结点
git refs delete api
通过 API 删除 refGit 参考的 REST API 终结点
git refs update api
通过 API 更新 refGit 参考的 REST API 终结点
git repo contents api
通过 API 更改文件的内容存储库内容的 REST API 终结点
merge使用自动合并来合并拉取请求自动合并拉取请求
merge base into head
当基础分支需要严格的状态检查时从基础分支更新主题分支(例如,通过拉取请求中的“更新分支”)关于受保护分支
pull request branch delete button
在 Web 界面中从拉取请求删除主题分支删除和恢复拉取请求中的分支
pull request branch undo button
在 Web 界面中从拉取请求还原主题分支删除和恢复拉取请求中的分支
pull request merge api
通过 API 合并拉取请求 API用于拉取请求的 REST API 终结点
pull request merge button
在 Web 界面中合并拉取请求合并拉取请求
pull request revert button
还原拉取请求还原拉取请求
releases delete button
删除发行版管理仓库中的发行版
stafftools branch restore
从站点管理员仪表板还原分支从 Web UI 管理实例
tag create api
通过 API 创建标记Git 标签的 REST API 端点
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 alpine:latest
    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 .
    [+] Building 4.5s (8/8) FINISHED
     => [internal] load build definition from Dockerfile.dev                                                                            0.0s
     => => transferring dockerfile: 641B                                                                                                0.0s
     => [internal] load .dockerignore                                                                                                   0.0s
     => transferring context: 2B                                                                                                     0.0s
     => [internal] load metadata for docker.io/library/alpine:latest                                                                    1.9s
     => [auth] library/alpine:pull token for registry-1.docker.io                                                                       0.0s
     => [1/3] FROM docker.io/library/alpine:latest@sha256:82d1e9d7ed48a7523bdebc18cf6290bdb97b82302a8a9c27d4fe885949ea94d1              0.0s
     => => resolve docker.io/library/alpine:latest@sha256:82d1e9d7ed48a7523bdebc18cf6290bdb97b82302a8a9c27d4fe885949ea94d1              0.0s
     => => sha256:82d1e9d7ed48a7523bdebc18cf6290bdb97b82302a8a9c27d4fe885949ea94d1 1.64kB / 1.64kB                                      0.0s
     => => sha256:25fad2a32ad1f6f510e528448ae1ec69a28ef81916a004d3629874104f8a7f70 528B / 528B                                          0.0s
     => => sha256:c1aabb73d2339c5ebaa3681de2e9d9c18d57485045a4e311d9f8004bec208d67 1.47kB / 1.47kB                                      0.0s
     => [2/3] RUN   apk add --no-cache git openssh bash &&   ssh-keygen -A &&   sed -i "s/#AuthorizedKeysFile/AuthorizedKeysFile/g" /e  2.3s
     => [3/3] WORKDIR /home/git                                                                                                         0.0s
     => exporting to image                                                                                                              0.1s
     => => exporting layers                                                                                                             0.1s
     => => writing image sha256:938447846e19a4328a85883fbd1ccf5eb919d97448cc7256efebf403d8b5a196                                        0.0s
     => => naming to docker.io/library/pre-receive.dev
    
  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 master
    > Warning: Permanently added '[127.0.0.1]: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@127.0.0.1:test.git
    >  ! [remote rejected] master -> master (pre-receive hook declined)
    > error: failed to push some refs to 'git@192.168.99.100:test.git'
    

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

延伸阅读