Skip to main content

Enterprise Server 3.20 目前作为发布候选版本提供。

创建预接收挂钩脚本

使用预接收挂钩脚本创建根据内容接受或拒绝推送的条件。

编写预接收挂钩脚本

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

输入 (stdin)

推送发生后,在为远程存储库更新任何引用之前,你的 GitHub Enterprise Server 实例 上的 git-receive-pack 进程将调用预接收挂钩脚本。 脚本的标准输入 stdin 是一个字符串,对每个要更新的 ref 包含一行。 每行都包含 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 语句将在命令行或用户界面上对用户均可见。

退出状态

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

退出状态值操作
0将接受推送。
非零将拒绝推送。

环境变量

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

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

  •         [始终可用](#always-available)
    
  •         [可用于从 Web 界面或 API 推送](#available-for-pushes-from-the-web-interface-or-api)
    
  •         [可用于拉取请求合并](#available-for-pull-request-merges)
    
  •         [可用于使用 SSH 身份验证的推送](#available-for-pushes-using-ssh-authentication)
    

始终可用

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

变量说明示例值
$GIT_DIR
实例上的远程仓库路径/data/user/repositories/a/ab/
a1/b2/34/100001234/1234.git
$GIT_OBJECT_DIRECTORY
包含推送中对象的临时目录的路径/data/user/repositories/a/ab/
a1/b2/34/100001234/1234.git/
objects/ghq_luvYC864B9j
$GIT_QUARANTINE_PATH
包含与 $GIT_OBJECT_DIRECTORY 相同的值/data/user/repositories/a/ab/
a1/b2/34/100001234/1234.git/
objects/ghq_luvYC864B9j
$GIT_ALTERNATE_OBJECT_
DIRECTORIES
实例中存储库对象目录的路径/data/user/repositories/a/ab/
a1/b2/34/100001234/1234.git/objects
$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 创建的部署自动合并了基础分支
          [AUTOTITLE](/rest/deployments#create-a-deployment) |

|

blob#save
| 在 Web 界面中更改文件的内容 | 编辑文件 | |
branch merge api
| 通过 API 合并分支 | 分支的 REST API 终结点及其设置 | |
branches page delete button
| 在 Web 界面中删除分支 | 在库中创建和删除分支 | |
git refs create api
| 通过 API 创建引用 | Git 参考的 REST API 端点 | |
git refs delete api
| 通过 API 删除 ref | Git 参考的 REST API 端点 | |
git refs update api
| 通过 API 更新 ref | Git 参考的 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 端点 | |
web branch create
| 通过 Web 界面创建分支 | 在库中创建和删除分支 |

可以用于合并拉取请求

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

变量说明示例值
$GITHUB_PULL_REQUEST_AUTHOR_LOGIN
编写拉取请求的帐户的用户名octocat
$GITHUB_PULL_REQUEST_HEAD
拉取请求的主题分支的名称,格式为 USERNAME:BRANCH
          <span style="white-space: nowrap;">octocat:fix-bug</span> |

|

$GITHUB_PULL_REQUEST_BASE
| 拉取请求的基础分支的名称,格式为 USERNAME:BRANCH | octocat: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 来解释策略强制实施。 此外,也可以通过 Pull Request 接受贡献。 但是,只能从默认分支添加预接收挂钩。 对于测试工作流程,应使用具有配置的仓库的分支。

  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'
    

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

其他阅读材料