可以在 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 | 表示更新的仓库是否公开的布尔值 |
|
$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 创建 ref | “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 端点” |
slumlord (#SHA) | 通过 Subversion 提交 | "Subversion 客户端支持" |
web branch create | 通过 Web 界面创建分支 | “创建和删除仓库中的分支” |
可用于拉取请求合并
当触发挂钩的推送由于拉取请求请求合并而成为推送时,以下变量在预接收挂钩环境中可用。
变量 | 说明 | 示例值 |
---|---|---|
$GITHUB_PULL_REQUEST_AUTHOR_LOGIN | 编写拉取请求的帐户的用户名 | octocat |
$GITHUB_PULL_REQUEST_HEAD | 拉取请求的主题分支的名称,格式为 USERNAME:BRANCH | octocat:fix-bug |
$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
来解释策略强制实施。 此外,也可以通过拉取请求接受贡献。 但是,只能从默认分支添加预接收挂钩。 对于测试工作流程,应使用具有配置的仓库的分支。
-
对于 Mac 用户,确保脚本具有执行权限:
sudo chmod +x SCRIPT_FILE.sh
对于 Windows 用户,确保脚本具有执行权限:
git update-index --chmod=+x SCRIPT_FILE.sh
-
在 你的 GitHub Enterprise Server 实例 提交并推送到指定的预接收挂钩存储库。
git commit -m "YOUR COMMIT MESSAGE" git push
-
在 GitHub Enterprise Server 实例上创建预接收挂钩。
在本地测试预接收脚本
在 你的 GitHub Enterprise Server 实例 上创建或更新预接收挂钩脚本之前,你可以在本地对其进行测试。 一种方法是创建本地 Docker 环境以充当可以执行预接收挂钩的远程仓库。
-
创建一个名为
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"]
-
创建一个名为
always_reject.sh
的测试预接收脚本。 此示例脚本将拒绝所有推送,这对于锁定仓库非常有用:#!/usr/bin/env bash echo "error: rejecting all pushes" exit 1
-
确保
always_reject.sh
脚本具有执行权限:chmod +x always_reject.sh
-
从包含
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
-
运行包含生成的 SSH 密钥的数据容器:
docker run --name data pre-receive.dev /bin/true
-
将测试预接收挂钩
always_reject.sh
复制到数据容器中:docker cp always_reject.sh data:/home/git/test.git/hooks/pre-receive
-
运行运行
sshd
并执行挂钩的应用程序容器。 记下返回的容器 ID:$ docker run -d -p 52311:22 --volumes-from data pre-receive.dev > 7f888bc700b8d23405dbcaf039e6c71d486793cad7d8ae4dd184f4a47000bc58
-
将生成的 SSH 密钥从数据容器复制到本地计算机:
docker cp data:/home/git/.ssh/id_ed25519 .
-
修改测试存储库的远程并推送到 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'
请注意,在执行预接收挂钩并回显脚本中的输出后,将拒绝推送。
延伸阅读
- Pro Git 网站中的“自定义 Git - Git 强制实施策略示例”