Git Hooks — Easily deploy code via git push

Git Hooks to deploy code

The best way to get into any field is to start with a problem statement, and then learn the necessary concepts as you try and solve that problem. The same is true with DevOps and today we will start with a very simple problem and craft a very rudimentary solution for it.

Goal: Deploying code to remote server via git push

Let's say you have a website hosted on a VPS, and you have a local copy of its source code on your laptop. As your needs change, you will inevitably want to make some changes to the website. If you are using git to track changes to your source code, you can use another functionality of git called git hooks to push your local changes to the remote server, and have the server deploy it for you.

This is the crudest, and yet very effective way of getting started with DevOps. A practive which merges the Development workflow closely with that of Operations.

Prerequisites

To follow along this tutorial you will need:

  • A very basic familiarity with the Linux command line.
  • A very basic idea of how to use git, creating a repository and committing changes.
  • A VPS with a public IP for which you have key-based SSH access as root user.

Setting the stage

We will start with a very simple setup where an Nginx Server is serving plain HTML files on port 80 and we will make changes to these files locally and push the changes to the server running Nginx. If you follow along, by the end of the setup, just reloading the webpage at http://your_ip_address will show you all the changes that were pushed without you having to manually login into the server.

Local setup

To start with, you will need git installed on your local computer. Windows users should look at git-scm and if you are using Linux or WSL2 then you can install it via your package manager. Similarly, macOS users can install git via brew package manager or by simplying installing Xcode or Xcode Command Line Tools.

Great! Now that we have git installed, we create a simple git repo, and add some very simple HTML code in it.

$ mkdir html
$ git init
Initialized empty Git repository in C:/Users/r/html/.git/

Create a file in ~/html directory named index.html and add the following contents to it:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to My Website!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to My Website.</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Side Note

If you are using git for the first time. You will have to use git config --global user.name "your_name" and git config --global user.email "[email protected]" to set basic information that git uses to tag your commits.

Next commit those files into your repo:

$ git add index.html
$ git commit -m "Add index.html"

If you have used remote hosted git repositories such as those provided by GitLab, BitBucket or GitHub, you may have come across the git remote command. We will be using the same mechanism to push our files to the remote server. Just replace the your_ip_address part below with the actual IP Address (or FQDN) of your remote server.

$ git remote add production [email protected]_ip_address:/var/www/html.git

This tells git that we have a new remote endpoint for this repository called production located at /var/www/html.git on IP Address your_ip_address, and, to push to this repository as user root. Usually when pushing to GitHub or GitLab our remote is called origin, the user is git and the location is username/reponame.git.

But the remote endpoint does not exist yet. So, let's create that.

Remote Server Setup

The following is a one time setup for which you will have to SSH into your server. We will start by creating a bare directory for git repo. This is where all your git commits will be stored in a format that only git can understand, i.e, as a tree of commits and other objects. You don't have to know about its inner workings, just know that this where all the history of your repo lives.

$ cd /var/www
$ mkdir /var/www/html.git
$ cd /var/www/html.git
$ git init --bare

Next we will use another feature of git which are called git hooks. These are basically shell scripts that get executed when a certain action is performed inside the repository. In case of our production remote we will create a hook that publishes the updated contents of our repository /var/www/html.git to our webserver's root /var/www/html directory.

Inside /var/www/html.git directory you will find a hooks folder with a bunch of sample scripts. We will ignore these and create a new file with the exact name of post-receive.

$ cd /var/www/html.git/hooks

Create a file called post-receive in here with the following content inside it and save it.

#!/bin/sh

# Check out the files
git --work-tree=/var/www/html --git-dir=/var/www/html.git checkout -f

Next, we need to make the post-receive file executable.

$ chmod +x /var/www/html.git/hooks/post-receive

Install Nginx and if the html directory is not present, create it:

$ sudo apt install nginx -y 
$ mkdir -p /var/www/html

This is it for the remote setup. Let's take our setup for a spin!

Testing the workflow

If you've installed Nginx, then /var/www/html is the default root for that webserver, and its contents will be shown in the web browser as follows:

Nginx Default HTML Page

Now let's push our new HTML code from local setup:

$ git push production master

If you now reload the website, you will see that the title and the first heading have been revised to match our "custom" HTML file.

New Commit

 

This is it! You are now free to work on your local git repo and push changes with just a single command.

End Note

The post-receive script is just a general shell script. You can use this to reload nginx or MySQL or any other service as well as perform other actions that are required for more complicated apps running on NodeJS, PHP or the language of your choice. As your project grows you can add other remote servers for building, testing and much much more.

Finally, you should remember that while all the contents of your source code are pushed to the remote endpoint, the same isn't true for metadata like remote endpoints. If you were to push the repo to GitHub as well, you can do it, and they won't be able to see that you have an additional remote endpoint called production. Neither will the git hook script of post-receive ever leave your server. These are not part of the repository's contents.