Creating a pre-receive hook script
Use pre-receive hook scripts to create requirements for accepting or rejecting a push based on the contents.
In this article
- Writing a pre-receive hook script
- Setting permissions and pushing a pre-receive hook to GitHub Enterprise Server
- Testing pre-receive scripts locally
- Further reading
You can see examples of pre-receive hooks for GitHub Enterprise Server in the
Writing a pre-receive hook script
A pre-receive hook script executes in a pre-receive hook environment on the GitHub Enterprise Server appliance. When you create a pre-receive hook script, consider the available input, output, exit-status and environment variables.
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:
<old-value> SP <new-value> SP <ref-name> LF
This string represents these arguments:
||Old object name stored in the
When you create a new
||New object name to be stored in the
When you delete a
||The full name of the
For more information on
git-receive-pack see "git-receive-pack" in the Git documentation.
For more information about
refs see "Git References" in Pro Git.
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.
exit-status of a pre-receive script determines if the push will be accepted.
|0||The push will be accepted.|
|non-zero||The push will be rejected.|
Outside of the values that are provided to
stdin, there are additional variables that are available to a pre-receive hook script running on GitHub Enterprise Server.
|$GITHUB_USER_LOGIN||The user id who created the
|$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
|$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
|$GITHUB_PUBLIC_KEY_FINGERPRINT||The user's public key fingerprint.|
|$GITHUB_PULL_REQUEST_HEAD||A string in the format:
|$GITHUB_PULL_REQUEST_BASE||A string in the format:
|$GITHUB_VIA||Method used to create the ref.
|$GIT_PUSH_OPTION_COUNT||The number of push options that were sent by the client. For more information about push options, see "git-push" 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" in the Git documentation.|
Setting permissions and pushing a pre-receive hook to GitHub Enterprise Server
A pre-receive hook script is contained in a repository on the GitHub Enterprise Server appliance. 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.
For Mac users, ensure the scripts have execute permissions:
$ sudo chmod +x SCRIPT_FILE.sh
For Windows users, ensure the scripts have execute permissions:
git update-index --chmod=+x SCRIPT_FILE.sh
Commit and push to your designated pre-receive hooks repository on the GitHub Enterprise Server instance.
$ git commit -m "YOUR COMMIT MESSAGE" $ git push
Create the pre-receive hook on the GitHub Enterprise Server instance.
Testing pre-receive scripts locally
You can test a pre-receive hook script locally before you create or update it on your GitHub Enterprise Server appliance. One method is to create a local Docker environment to act as a remote repository that can execute the pre-receive hook.
Ensure Docker is installed locally.
Create a file called
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 rsa -b 4096 -f /home/git/.ssh/id_rsa -P '' && \ mv /home/git/.ssh/id_rsa.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"]
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
always_reject.shscripts has execute permissions:
$ chmod +x always_reject.sh
From the directory containing
Dockerfile.dev, build an image:
$ 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 rsa -b 4096 -f /home/git/.ssh/id_rsa -P ' && mv /home/git/.ssh/id_rsa.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 rsa key pair. > Your identification has been saved in /home/git/.ssh/id_rsa. > Your public key has been saved in /home/git/.ssh/id_rsa.pub. ....truncated output.... > Initialized empty Git repository in /home/git/test.git/ > Successfully built dd8610c24f82
Run a data container that contains a generated SSH key:
$ docker run --name data pre-receive.dev /bin/true
Copy the test pre-receive hook
always_reject.shinto the data container:
$ docker cp always_reject.sh data:/home/git/test.git/hooks/pre-receive
Run an application container that runs
sshdand executes the hook. Take note of the container id that is returned:
$ docker run -d -p 52311:22 --volumes-from data pre-receive.dev > 7f888bc700b8d23405dbcaf039e6c71d486793cad7d8ae4dd184f4a47000bc58
Copy the generated SSH key from the data container to the local machine:
$ docker cp data:/home/git/.ssh/id_rsa .
Modify the remote of a test repository and push to the
test.gitrepo within the Docker container. This example uses
email@example.com:octocat/Hello-World.gitbut you can use any repo you want. This example assumes your local machine (127.0.0.1) is binding port 52311, but you can use a different IP address if docker is running on a remote machine.
$ git clone firstname.lastname@example.org:octocat/Hello-World.git $ cd Hello-World $ git remote add test email@example.com:test.git $ GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p 52311 -i ../id_rsa" git push -u test master > 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 firstname.lastname@example.org:test.git > ! [remote rejected] master -> master (pre-receive hook declined) > error: failed to push some refs to 'email@example.com:test.git'
Notice that the push was rejected after executing the pre-receive hook and echoing the output from the script.
- "Customizing Git - An Example Git-Enforced Policy" from the Pro Git website