Building a web deployment pipeline using post-receive hook

Building a web deployment pipeline using post-receive hook

This article will demonstrate how you can build a deployment pipeline from your local test server to a remote using a git hook called post-receive. This tutorial is specifically for unix-based systems such as Linux and MacOS but can also be done on Windows too using Putty or even better: the GNU ssh tool for Windows.

The process

  1. First set up SSH in your server so that you can access the files through your local machine. Normally, with cloud providers, this is fairly straight-forward as they provide a .pem file during the provisioning of the server which can be used to login to the machine directly. However, I would set the server to use my machine's key to let me in and configure the various server values at ~/.ssh/config file for easy access on my local machine with a config that looks like:
Host *
  UseKeychain yes
  AddKeysToAgent yes
  IdentityFile ~/.ssh/id_rsa

Host acme
  User root
  HostName <server-ip>

The IdentityFile is our generated SSH private key. If you haven't already i.e. the file ~/.ssh/id_rsa is missing, you can create it using the ssh-keygen tool. You can follow Github's tutorial for doing this. And also, setup the config file using:

touch ~/.ssh/config
chmod 600 ~/.ssh/config

Next, I will install the public key into the server's ssh config folder. This can be done manually. Some providers provide a section in their cPanel/admin panels to add this public key. However, I will do this using a tool called ssh-copy-id and using the .pem or .ppk file provided during provisioning of the server:

brew install ssh-copy-id
ssh-copy-id -i /path/to/.pem/or/.ppk/file

What this does it essentially copies your machine's SSH key into the server so that you can directly SSH into it so that you can SSH simply by:

ssh acme

The alternative is to declare the IdentityFile manually each time or another simple way would be to declare the IdentityFile within the server's ~/.ssh/config block like:

Host acme
  User root
  IdentityFile /path/to/pem/or/ppk/file
  HostName <server-ip>

In this case, the server will be kept pure i.e. your public key will not be stored in the server. Either way works for setting up the git hook and making our deployment chain.

  1. Now that SSH access is set up, we can go ahead and setup a bare repository in our server so that we can make commits to it. First, we ssh into it:
    ssh acme
    mkdir web.git && cd web.git
    git init --bare
    
    The last command sets up a bare repository with some git hook templates. The post-receive hook that we're going to introduce will NOT exist in the hooks folder (try doing a ls to check) but we will create it manually.
    cd hooks
    touch post-receive
    vim post-recieve
    chmod +x post-receive
    
    I use vim here as my editor of choice, but you can use emacs if you prefer it. Next, I paste the code:
    #!/bin/sh
    GIT_WORK_TREE=’/path/to/public/html’ git checkout -f
    
    And then save and exit from vim. It is important that this file has the executable permission set which is why there is a 3rd chmod command. In fact, you can run ./post-receive command to test if the git hook works. This is because on some shared hosting providers executing scripts is blocked completely. Now cd out and copy the path of this bare repo: cd .. && pwd

Lets assume, for the sake of this tutorial that this path is /home/moz/web.git

  1. Now that the server side pipeline is setup, all that's left is to point our local project repository to it so we can make pushes to it. Start by CDing to it:
    cd ~/projects/acme-project
    git remote add web acme:/home/moz/web.git
    
    And voila, that's it! You can now run:
    git push web master
    
    And it will perform the necessary steps to deploy the project into your public_html folder and it will appear for your users. There is no hard and fast rule that you need to set this remote name as web. Since I work with clients as a freelancer, I typically have 3 remotes: origin, client and web. The origin is my main development private repo which has all the commits, the client remote for is client's private repo for pushing changes whenever the client finishes making a payment, and the web remote for when the client wants me to publish changes.

Sometimes, you want to run some pre-commands every time a deployment is made. For example, on NodeJS-based apps, you may want to fetch any new packages, so you can do this on the post-receive hook like:

GIT_WORK_TREE=/path/to/public/html’ git checkout -f && npm install

You can write more code inside that post-receive file, if you'd like, perhaps run tests before making the actual deployment, or to send an email that a deployment was made, or delete a cache folder within your public_html folder each time before deployment.

If you want to know more about git hooks, I would recommend you to read Skay's introductory article on Git Hooks.