Simple Continuous Deployment with Git

Since you follow Credera’s blog, you’ve probably been pleased with the new and improved version. For far too long we suffered from “Cobbler’s Children Syndrome”, while we worked on our clients’ sites only to neglect our own. However, this beautiful look and feel coupled with a responsive layout is no trivial investment. It took a lot of man hours by several team members, each with different skills. An individual, no matter how bright, wouldn’t be able to support this growing organizations publishing needs while also upgrading to such an extent.

This was the situation I was tasked with. However, I was acutely aware that we would only get a few hours time from a large number of specialized volunteer Crederians. Because of this, I wanted to make the most of those hours. And, passing around tarballs was no longer going to cut it.

We were looking for a solution that was easy to use, had little to no overhead, could be deployed immediately for instant feedback, was testable in lower environments, and played nicely with our other tools. To reiterate, tarballs were clearly not the answer, we needed some sort of SCM. We’ve already got an internal Gitolite server setup, so why not leverage that. However, a Jenkins CI server seemed overkill for us, especially given our limited time to setup and the fact that we don’t need to compile source files since we have a PHP-based WordPress site. Given the fact that there’s little in the way of solutions inbetween, I started brainstorming homebrewed solutions.

Eventually I came upon the idea of leveraging custom Bash scripts to be executed at specified Git commit hooks. This would solve the need to be easy to use, since everyone contributing would need to push anyways. Additionally, it would have little overhead since it’s a little script, could be deployed anywhere instantly provided we initialized a Git repository, and would play nice with our tools since they were our tools. I was pleased. And despite the relatively large amount of custom scripting that would be involved, the solution seemed elegant, simple, and all around perfect for our needs. I was pleased by the concept, rolled up my sleeves, and began building a proof-of-concept to demo for approval by our CIO (the highly esteemed Erik Weibust).

While developing the deployment scripts, I learned a lot about the internals of Git, not to mention Bash scripting. However, I didn’t quite get as far as I’d like as fast as I had planned. I ended up developing an Access Control List (ACL) capabale of directory based, branch based, and user based access in addition to deployment mechanisms that were dependant on SSH, and chains of commit hooks that all referenced each other. In short, my supposedly elegant scripts (even though they worked) were becoming a Rube-Goldberge machine and difficult for me to keep running even on local VMs, before I even deployed to live servers!

It was during one of the demos to Erik that I ran into another hiccup with a configuration file, that he made a simple,, yet very insightful observation. During the development of the proof-of-concept, I had gotten side-tracked reimplementing a good deal of functionality already provided to us by Gitolite. If we simply used Gitolite to manage our ACL and SSH keys, all we had to worry about was clone the Git repos on the application servers and write a single Git post-update hook that pulled the remote repository whenever we pushed to our Gitolite hub. Now that was elegant, simple, and played nice with our existing tools!

So, six lines of shell code and three “git clone” commands later, and we had the holy grail of Continuous Deployment systems in place that supported our development, QA, Stage, and Production servers with an ACL and versioning built in while only leveraging tools we already had in use. From now on, whenever a developer commits code on our “dev” branch, it is instantly deployed to our QA server for testing. When I think a certain commit is ready to be previewed in Stage by our Business Owner, I simply merge the “dev” branch into our “stage” branch and it is instantly deployed to our Stage environment. Finally, when the commit gets approval from QA and the Business, our IT Administrator merges the “stage” branch into “master” and the code is instantly “live”. Git takes care of all our heavy lifting, while Gitolite takes care of Access Control restrictions.

The best part is that this will work for any site where its backend does not run compiled code (like PHP-based or JavaScript-based sites). All you need to do is setup your Gitolite server (GitHub can work too, though may be less reliable/secure), clone the repository on your application servers, and install a post-update Git hook that SSH’s into your app server to pull down the latest and greatest.

Feel free to contact me with questions/comments @josh_hamit.