If you’ve used Git for a while, you’ve probably heard of Git hooks. Maybe you’ve even played around with them a bit. Git hooks are awesome in the context of continuous integration, so I want to share three CI-related use cases and point you to some ready-made hooks you can add to your workflow. If you’re new to Git hooks, no worries: we’ll start with the basics.
Understanding Git hooks
Hooks are Git’s native mechanism for triggering custom scripts before or after operations like commit and merge. Think of them as Git's plugin system. If you look in the .git directory of any Git repository you'll see a directory named "hooks" which contains a set of example hook scripts.
Installing Git hooks is straightforward and well-documented, so I won’t get into that here.
There are two broad classes of hooks, client-side and server-side. Client-side hooks run on your local workstation, while server-side hooks run on your Git server.
You can also categorize hooks as pre- or post- hooks. Pre-receive hooks are invoked before certain git operations, and have the option to cancel an operation if needed. They act as bouncers guarding your repo from behind a velvet rope, preventing you and your teammates from committing faulty code. Post-receive hooks run after an operation has completed, and therefore don't have the option to cancel it. Instead, post-receive hooks automate pieces of your development workflow.
Using Git hooks is like having little robot minions to carry out your every wish (mwah-ha-haaaa!)
Git hooks automate things like…
- checking to see if you included the key for the associated issue in your commit message
- enforcing preconditions for merging
- sending notifications to your team's chat room
- setting up your workspace after switching over to a different branch
Enforcing clean builds on feature branches
Server-side pre-receive hooks are an especially powerful complement to continuous integration because they can prevent developers from pushing code to master, unless the code meets certain conditions – like elite ninja guardians, protecting it from bad code.
Developers are generally conscientious enough not to merge to master when there are broken tests on their branch. But sometimes we forget to check. Or, when we're sharing a branch with other people, sometimes more changes get made since we last checked the branch build… whatever.
So we add a server-side hook that looks for incoming merges to master. When it finds one, the script checks the latest build on your branch, and if there are any failing tests, the merge is rejected. My colleague and Atlassian developer advocate Tim Pettersen wrote a hook script for this, designed to work with Bamboo, and made it available on Bitbucket. You can grab it, customize it, and add it to your repo.
Protecting your hard-earned code coverage
Something I've seen lots of teams struggle with is maintaining code coverage. Many times they've had to retroactively cover their code base with tests, and it's frustrating to see that hard-earned coverage slip away as more features get added without tests shoring them up. So Tim also wrote a pre-receive server-side hook to protect master from declining code coverage.
This hook also looks for incoming merges to master. It then calls out to the CI server to check current code coverage on master, and coverage on the branch. If the branch has inferior coverage, the merge is rejected.
Most continuous integration servers don’t expose code coverage data through their remote APIs, so the script pulls down the code coverage report. To do this, the build must be configured to publish the report as a shared artifact, both on master and on the branch build. Once published, you can grab the latest coverage report from master by calling the continuous integration server. For branch coverage, you fetch the coverage report either from the latest build, or for builds related to the reference (commit) being merged.
And just to be clear, this all assumes you already have code coverage running in one of your builds. The hook doesn't magically do that – it just looks for the coverage data in your build results. This one also works with Bamboo by default, as well as Clover (Atlassian’s code coverage tool for Java and Groovy). But it can be customized to integrate with any build server or code coverage tool.
Checking the status of branch builds
Because friends don’t let friends checkout broken branches.
Here’s a chance to play with client-side Git hooks: a post-checkout hook script that exposes branch build status right inside your terminal window, also from Tim. The script gets the branch’s head revision number from your local copy, then queries the CI server to see whether that revision has been built – and, if so, whether the build succeeded.
Let’s say you want to branch from master. This hook will tell you whether the head commit on the master built successfully, which means it’s a “safe” commit from which to create a feature branch. Or let’s say the hook says the build for that revision failed, yet the team’s wallboard shows a green build for that branch (or vice versa). This means your local copy is out of date. It’ll be up to you to decide whether to pull down the updates or continue working on the local copy you have.
Using this hook has saved Atlassian developers from countless headaches. If you can’t convince your team to adopt the server-side hooks discussed above, at least install this one on your local workstation. You won’t regret it.
Automation for the win
All the Git hooks for continuous integration that I’ve shown here work with Bamboo, Clover, and Bitbucket by default. But remember that Git hooks are vendor-neutral, so you can customize them to work with whatever tool stack you have.
For more on using Git in a continuous delivery context, check out The Pipeline – our newest microsite, dedicated to doing continuous delivery with Jira, Bitbucket, and Bamboo. You’ll find conceptual tips like Git workflows for CD, as well as a wealth of information about connecting Atlassian tools to build out a continuous delivery pipeline.
Did you find this post helpful? Please share it on your social network of choice and help spread the word about Git hooks!