diff --git a/content/admin/policies/creating-a-pre-receive-hook-script.md b/content/admin/policies/creating-a-pre-receive-hook-script.md index 71f3a8e9716f..9bb56024c8b5 100644 --- a/content/admin/policies/creating-a-pre-receive-hook-script.md +++ b/content/admin/policies/creating-a-pre-receive-hook-script.md @@ -1,6 +1,7 @@ --- title: Creating a pre-receive hook script intro: Use pre-receive hook scripts to create requirements for accepting or rejecting a push based on the contents. +miniTocMaxHeadingLevel: 4 redirect_from: - /enterprise/admin/developer-workflow/creating-a-pre-receive-hook-script - /enterprise/admin/policies/creating-a-pre-receive-hook-script @@ -13,59 +14,109 @@ topics: You can see examples of pre-receive hooks for {% data variables.product.prodname_ghe_server %} in the [`github/platform-samples` repository](https://github.com/github/platform-samples/tree/master/pre-receive-hooks). ### Writing a pre-receive hook script -A pre-receive hook script executes in a pre-receive hook environment on the {% data variables.product.prodname_ghe_server %} appliance. When you create a pre-receive hook script, consider the available input, output, exit-status and environment variables. +A pre-receive hook script executes in a pre-receive hook environment on {% data variables.product.product_location %}. When you create a pre-receive hook script, consider the available input, output, exit status, and environment variables. -#### Input (stdin) -After a push occurs and before any refs are updated on the remote repository, the `git-receive-pack` process invokes the pre-receive hook script with the standard input of one line per ref to be updated: +#### Input (`stdin`) +After a push occurs and before any refs are updated for the remote repository, the `git-receive-pack` process on {% data variables.product.product_location %} invokes the pre-receive hook script. Standard input for the script, `stdin`, is a string containing a line for each ref to update. Each line contains the old object name for the ref, the new object name for the ref, and the full name of the ref. -` SP SP LF` +``` + SP SP LF +``` -This string represents these arguments: +This string represents the following arguments. | Argument | Description | | :------------- | :------------- | -| `` | Old object name stored in the `ref`.
When you *create* a new `ref`, this equals 40 zeroes. | -| `` | New object name to be stored in the `ref`.
When you *delete* a `ref`, this equals 40 zeroes. | -| `` | The full name of the `ref`. | +| `` | Old object name stored in the ref.
When you create a new ref, the value is 40 zeroes. | +| `` | New object name to be stored in the ref.
When you delete a ref, the value is 40 zeroes. | +| `` | The full name of the ref. | -For more information on `git-receive-pack` see "[git-receive-pack](https://git-scm.com/docs/git-receive-pack)" in the Git documentation. -For more information about `refs` see "[Git References](https://git-scm.com/book/en/v2/Git-Internals-Git-References)" in *Pro Git*. +For more information about `git-receive-pack`, see "[git-receive-pack](https://git-scm.com/docs/git-receive-pack)" in the Git documentation. For more information about refs, see "[Git References](https://git-scm.com/book/en/v2/Git-Internals-Git-References)" in *Pro Git*. -#### Output (stdout) +#### Output (`stdout`) -The script output (`stdout`) is passed back to the client, so any `echo` statements are visible to the user on the command line or in the user interface. +The standard output for the script, `stdout`, is passed back to the client. Any `echo` statements will be visible to the user on the command line or in the user interface. -#### Exit-status +#### Exit status -The `exit-status` of a pre-receive script determines if the push will be accepted. +The exit status of a pre-receive script determines if the push will be accepted. -| Exit-status Value | Action | -| :-------------: | :-------------: | -| 0 | The push will be accepted. | +| Exit-status value | Action | +| :- | :- | +| 0 | The push will be accepted. | | non-zero | The push will be rejected. | #### Environment variables -Outside of the values that are provided to `stdin`, there are additional variables that are available to a pre-receive hook script running on {% data variables.product.prodname_ghe_server %}. -| Variable | Description | -| :------------- | :------------- | -| $GITHUB_USER_LOGIN | The user id who created the `ref`. | -| $GIT_DIR | The path of the remote repository on the appliance. | -| $GITHUB_USER_IP | The IP Address of the user performing the push. | -| $GITHUB_REPO_NAME | The name in `owner`/`repo` format of the repository being updated. | -| $GITHUB_PULL_REQUEST_AUTHOR_LOGIN | The user ID for the author of a pull request opened on your instance. | -| $GITHUB_REPO_PUBLIC | A boolean value that when `true` represents a public repository, and when `false` represents a private repository. | -| $GITHUB_PUBLIC_KEY_FINGERPRINT | The user's public key fingerprint. | -| $GITHUB_PULL_REQUEST_HEAD | A string in the format: `user:branch` for the HEAD of the PR.
Example: `octocat:fix-bug` | -| $GITHUB_PULL_REQUEST_BASE | A string in the format: `user:branch` for the BASE of the PR.
Example: `octocat:main` | -| $GITHUB_VIA | Method used to create the ref.
**Possible values:**
- `auto-merge deployment api`
- `blob edit`
- `branch merge api`
- `branches page delete button`
- `git refs create api`
- `git refs delete api`
- `git refs update api`
- `merge api`
- `pull request branch delete button`
- `pull request branch undo button`
- `pull request merge api`
- `pull request merge button`
- `pull request revert button`
- `releases delete button`
- `stafftools branch restore`
- `slumlord (#{sha})` | -| $GIT_PUSH_OPTION_COUNT | The number of push options that were sent by the client. For more information about push options, see "[git-push](https://git-scm.com/docs/git-push#git-push---push-optionltoptiongt)" in the Git documentation. | -| $GIT_PUSH_OPTION_N | Where N is an integer starting at 0, this variable contains the push option string that was sent by the client. The first option that was sent is stored in GIT_PUSH_OPTION_0, the second option that was sent is stored in GIT_PUSH_OPTION_1, and so on. For more information about push options, see "[git-push](https://git-scm.com/docs/git-push#git-push---push-optionltoptiongt)" in the Git documentation. |{% if currentVersion ver_gt "enterprise-server@2.21" %} -| $GIT_USER_AGENT | The user-agent string sent by the client that pushed the changes. |{% endif %} +In addition to the standard input for your pre-receive hook script, `stdin`, {% data variables.product.prodname_ghe_server %} makes the following variables available in the Bash environment for your script's execution. For more information about `stdin` for your pre-receive hook script, see "[Input (`stdin`)](#input-stdin)." + +Different environment variables are available to your pre-receive hook script depending on what triggers the script to run. + +- [Always available](#always-available) +- [Available for pushes from the web interface or API](#available-for-pushes-from-the-web-interface-or-api) +- [Available for pull request merges](#available-for-pull-request-merges) +- [Available for pushes using SSH authentication](#available-for-pushes-using-ssh-authentication) + +##### Always available + +The following variables are always available in the pre-receive hook environment. + +| Variable | Description | Example value | +| :- | :- | :- | +|
$GIT_DIR
| Path to the remote repository on the instance | /data/user/repositories/a/ab/
a1/b2/34/100001234/1234.git | +|
$GIT_PUSH_OPTION_COUNT
| The number of push options that were sent by the client with `--push-option`. For more information, see "[git-push](https://git-scm.com/docs/git-push#Documentation/git-push.txt---push-optionltoptiongt)" in the Git documentation. | 1 | +|
$GIT\_PUSH\_OPTION\_N
| Where _N_ is an integer starting at 0, this variable contains the push option string that was sent by the client. The first option that was sent is stored in `GIT_PUSH_OPTION_0`, the second option that was sent is stored in `GIT_PUSH_OPTION_1`, and so on. For more information about push options, see "[git-push](https://git-scm.com/docs/git-push#git-push---push-optionltoptiongt)" in the Git documentation. | abcd |{% if currentVersion ver_gt "enterprise-server@2.21" %} +|
$GIT_USER_AGENT
| User-agent string sent by the Git client that pushed the changes | git/2.0.0{% endif %} +|
$GITHUB_REPO_NAME
| Name of the repository being updated in _NAME_/_OWNER_ format | octo-org/hello-enterprise | +|
$GITHUB_REPO_PUBLIC
| Boolean representing whether the repository being updated is public |
  • true: Repository's visibility is public
  • false: Repository's visibility is private or internal
+|
$GITHUB_USER_IP
| IP address of client that initiated the push | 192.0.2.1 | +|
$GITHUB_USER_LOGIN
| Username for account that initiated the push | octocat | + +##### Available for pushes from the web interface or API + +The `$GITHUB_VIA` variable is available in the pre-receive hook environment when the ref update that triggers the hook occurs via either the web interface or the API for {% data variables.product.prodname_ghe_server %}. The value describes the action that updated the ref. + +| Value | Action | More information | +| :- | :- | :- | +|
auto-merge deployment api
| Automatic merge of the base branch via a deployment created with the API | "[Repositories](/rest/reference/repos#create-a-deployment)" in the REST API documentation | +|
blob edit
| Change to a file's contents in the web interface | "[Editing files in your repository](/github/managing-files-in-a-repository/editing-files-in-your-repository)" | +|
branch merge api
| Merge of a branch via the API | "[Repositories](/rest/reference/repos#merge-a-branch)" in the REST API documentation | +|
branches page delete button
| Deletion of a branch in the web interface | "[Creating and deleting branches within your repository](/github/collaborating-with-issues-and-pull-requests/creating-and-deleting-branches-within-your-repository#deleting-a-branch)" | +|
git refs create api
| Creation of a ref via the API | "[Git database](/rest/reference/git#create-a-reference)" in the REST API documentation | +|
git refs delete api
| Deletion of a ref via the API | "[Git database](/rest/reference/git#delete-a-reference)" in the REST API documentation | +|
git refs update api
| Update of a ref via the API | "[Git database](/rest/reference/git#update-a-reference)" in the REST API documentation | +|
git repo contents api
| Change to a file's contents via the API | "[Repositories](/rest/reference/repos#create-or-update-file-contents)" in the REST API documentation | +|
merge base into head
| Update of the topic branch from the base branch when the base branch requires strict status checks (via **Update branch** in a pull request, for example) | "[About protected branches](/github/administering-a-repository/about-protected-branches#require-status-checks-before-merging)" | +|
pull request branch delete button
| Deletion of a topic branch from a pull request in the web interface | "[Deleting and restoring branches in a pull request](/github/administering-a-repository/deleting-and-restoring-branches-in-a-pull-request#deleting-a-branch-used-for-a-pull-request)" | +|
pull request branch undo button
| Restoration of a topic branch from a pull request in the web interface | "[Deleting and restoring branches in a pull request](/github/administering-a-repository/deleting-and-restoring-branches-in-a-pull-request#restoring-a-deleted-branch)" | +|
pull request merge api
| Merge of a pull request via the API | "[Pulls](/rest/reference/pulls#merge-a-pull-request)" in the REST API documentation | +|
pull request merge button
| Merge of a pull request in the web interface | "[Merging a pull request](/github/collaborating-with-issues-and-pull-requests/merging-a-pull-request#merging-a-pull-request-on-github)" | +|
pull request revert button
| Revert of a pull request | "[Reverting a pull request](/github/collaborating-with-issues-and-pull-requests/reverting-a-pull-request)" | +|
releases delete button
| Deletion of a release | "[Managing releases in a repository](/github/administering-a-repository/managing-releases-in-a-repository#deleting-a-release)" | +|
stafftools branch restore
| Restoration of a branch from the site admin dashboard | "[Site admin dashboard](/admin/configuration/site-admin-dashboard#repositories)" | +|
tag create api
| Creation of a tag via the API | "[Git database](/rest/reference/git#create-a-tag-object)" in the REST API documentation | +|
slumlord (#SHA)
| Commit via Subversion | "[Support for Subversion clients](/github/importing-your-projects-to-github/support-for-subversion-clients#making-commits-to-subversion)" | +|
web branch create
| Creation of a branch via the web interface | "[Creating and deleting branches within your repository](/github/collaborating-with-issues-and-pull-requests/creating-and-deleting-branches-within-your-repository#creating-a-branch)" | + +##### Available for pull request merges + +The following variables are available in the pre-receive hook environment when the push that triggers the hook is a push due to the merge of a pull request. + +| Variable | Description | Example value | +| :- | :- | :- | +|
$GITHUB_PULL_REQUEST_AUTHOR_LOGIN
| Username of account that authored the pull request | octocat | +|
$GITHUB_PULL_REQUEST_HEAD
| The name of the pull request's topic branch, in the format `USERNAME:BRANCH` | octocat:fix-bug | +|
$GITHUB_PULL_REQUEST_BASE
| The name of the pull request's base branch, in the format `USERNAME:BRANCH` | octocat:main | + +##### Available for pushes using SSH authentication + +| Variable | Description | Example value | +| :- | :- | :- | +|
$GITHUB_PUBLIC_KEY_FINGERPRINT
| The public key fingerprint for the user who pushed the changes | a1:b2:c3:d4:e5:f6:g7:h8:i9:j0:k1:l2:m3:n4:o5:p6 | ### Setting permissions and pushing a pre-receive hook to {% data variables.product.prodname_ghe_server %} -A pre-receive hook script is contained in a repository on the {% data variables.product.prodname_ghe_server %} appliance. A site administrator must take into consideration the repository permissions and ensure that only the appropriate users have access. +A pre-receive hook script is contained in a repository on {% data variables.product.product_location %}. A site administrator must take into consideration the repository permissions and ensure that only the appropriate users have access. We recommend consolidating hooks to a single repository. If the consolidated hook repository is public, the `README.md` can be used to explain policy enforcements. Also, contributions can be accepted via pull requests. However, pre-receive hooks can only be added from the default branch. For a testing workflow, forks of the repository with configuration should be used. @@ -80,7 +131,7 @@ We recommend consolidating hooks to a single repository. If the consolidated hoo git update-index --chmod=+x SCRIPT_FILE.sh ``` -2. Commit and push to your designated pre-receive hooks repository on the {% data variables.product.prodname_ghe_server %} instance. +2. Commit and push to the designated repository for pre-receive hooks on {% data variables.product.product_location %}. ```shell $ git commit -m "YOUR COMMIT MESSAGE" @@ -90,36 +141,36 @@ We recommend consolidating hooks to a single repository. If the consolidated hoo 3. [Create the pre-receive hook](/enterprise/{{ currentVersion }}/admin/guides/developer-workflow/managing-pre-receive-hooks-on-the-github-enterprise-server-appliance/#creating-pre-receive-hooks) on the {% data variables.product.prodname_ghe_server %} instance. ### Testing pre-receive scripts locally -You can test a pre-receive hook script locally before you create or update it on your {% data variables.product.prodname_ghe_server %} appliance. One method is to create a local Docker environment to act as a remote repository that can execute the pre-receive hook. +You can test a pre-receive hook script locally before you create or update it on {% data variables.product.product_location %}. One method is to create a local Docker environment to act as a remote repository that can execute the pre-receive hook. {% data reusables.linux.ensure-docker %} 2. Create a file called `Dockerfile.dev` containing: - ```dockerfile - 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. Create a test pre-receive script called `always_reject.sh`. This example script will reject all pushes, which is useful for locking a repository: - - ```shell - #!/usr/bin/env bash + ```dockerfile + 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. Create a test pre-receive script called `always_reject.sh`. This example script will reject all pushes, which is useful for locking a repository: + + ``` + #!/usr/bin/env bash echo "error: rejecting all pushes" exit 1