Triggering Deployment with Git Commands

April 10th 2015 Ian Buchanan in CI, CD, Git

Deployment doesn't get any simpler than just getting the latest versions of some files up to the production server. While rsync, sftp, and scp have long been the tools of the trade for such simple deployments, these approaches have their warts. Even if it is easy to recover, an remote copy that fails in the middle may leave a web site in an incoherent state. If you are already using Git to manage the files as source code, then you may benefit from using Git's native ability to distribute versions of files. While this idea isn't all that new, there is a new feature of Git that makes this much easier than in past. Read on to learn when Git-based deployments are appropriate and how you can use Git to deploy files.

Simple file deployments

Here are some examples where deployment is as simple as making a copy on the production server:

  • A file-based website with just HTML, CSS, fonts, and images.
  • Text configuration files like the typical contents of /etc on Linux.
  • Interpreted source code files like JavaScript, Ruby, PHP, and Python.

This kind of simple deployment makes most sense for web sites on a virtual private server (VPS), where the environment is controlled by a hosting provider and resources like memory are limited. Tools like Capistrano or Fabric may be overkill and/or inappropriate for the environment. The one caveat is that you may have to install Git into your VPS yourself (as I did with my provider).

The main advantage of Git for deployment is its transactional nature. Consider a website where changes to the structure, content, and style. As the files are copied to production, a visitor might get a new page that has a link to an updated page that hasn't yet been copied to production yet. While the copy is in progress, the partial changes are incoherent. To compensate for this problem, there is a notion of Blue Green Deployment. While Blue Green Deployment is essential when changes are made to multiple servers, it seems overkill for the kind of simple case that we're considering. In contrast to ftp and the like, Git will first send all of the changes to the production repository, then it can quickly apply all of the changes. If, for some reason it can't, Git will automatically roll-back. That's just a basic capability of Git so no special compensation is necessary.

Push to deploy

Newer PaaS providers like Heroku and Azure offer push-to-deploy as the default deployment model. The deployment command is simple:

git push remote-server master

Where remote-server is a Git repository living on the production server. If the capability isn't built into your environment, you can set up Git on a VPS yourself. However, those instructions (and dozens of others) were written before Git 2.3, when a small but significant feature was introduced. Prior to Git 2.3, Git refused to modify a branch that is currently checked out. There were many work-arounds: detached work tree, dual repositories, post-receive hooks, and even specialized tooling. Admittedly, these approaches cover other things. For example, some of these restart services upon configuration or code changes. Some also prevent the web server from sharing the .git directory but that can also be achieved by configuring the web server.

Now, with Git 2.3, the following configures Git to override the normal behavior, making Git perform a git reset --hard after a push so that it updates the current branch.

git config receive.denyCurrentBranch updateInstead

Auto-deploy the master branch with a post-push webhook

A common pattern of Git usage is to have a development branch with cutting-edge changes and a master branch that is always kept consistent with production. With this pattern, it is easy to use Git to see what code is currently in production. This pattern depends on automatically pushing the master branch to production so there is never any question that master means production. With a Git-hosting service like Atlassian's Bitbucket, the Git automation is a simple matter of configuring a post-push webhook.

Bitbucket Hook

The trick is that webhooks need to be translated to a local Git command on the target. For reference, I created a simple PHP script to adapt the webhook to a git pull.

Look before you leap

Although the example cases are simple, that is hardly an excuse to blindly deploy code into production. There are appropriate tools for automatically checking web sites and configuration files so use them. These Git-based deployment techniques should come after those checks, whether done by hand on a developing branch, or automatically by a continuous integration server like Bamboo. Even if your continuous integration server makes an automatic decision to deploy, the advantages of the Git-based approach remain.