Friday, 8 April 2016

Implementing Git Flow with Gitlab and Jenkins

As with my last posts I'm going to cover the updates to the build and test systems that I have been making. In my previous posts I covered using CMake, and moving from SVN to Git. In this post I'm going to cover the branching strategy that I implemented after moving to Git. I will cover the branching model chosen and introduce the tools used which allow continuous integration using that model.

Git branching strategies

Git branching strategies are policies on how to use git for development. This allows you to establish a common work-flow that all team members can use and help make updates easier.

There are a number of branching strategies available and they offer various pros and cons depending on your releasing and development methodologies. For our model we have implemented a slight variation on the git flow model. The main changes from this model that we have implemented are:
  • master is used as the current development branch. 
  • production is used as the release branch. 
  • All changes to master and production must be pushed to Gitlab.
  • All changes to master and production must be tested via Jenkins.
  • All changes to master and production must be code reviewed.
Git flow was chosen as our model because it allows us to release software versions in a consistent and robust manner while still allowing for other developers to continue to work on new features.

Some of the other models such as github flow are more aligned towards software which follows a continuous deployment model where all changes should be pushed to production after testing instead of having to release software to customers.

Other tools

The tools we use to help with our work flow include Gitlab for the central git server and Jenkins for continuous integration.

Gitlab

Gitlab is git repository management tool, which includes user management, code review, merge requests, wiki and more. It could be considered similar to github and is quickly catching up on features and usability. However, the main area where it is ahead of github and what caused me to choose it was that it has an open source community edition which allows for free, easy to install, onsite installations.

Jenkins

Jeninks is a automation server that supports continuous integration and deployment. It is easily extensible and has many plugins to support most common build and integration tools. This allows it to easily integrate with build and source control tools to receive notifications and automatically update, build and test software.

Build System

As previously mentioned this post is about building and testing a software project which is C++ based and uses CMake as the build tool. The core platforms to build for are RedHat based systems including RedHat 5, 6, and 7.

Configuration

In this section I will cover configuration of the various servers. First, I will look at the Gitlab configuration and how the branches and hooks are configured. Secondly, I will cover the Jeninks configuration which builds and tests the software.

Gitlab

The installation of Gitlab is via the Gitlab CE omnibus edition Debian package on an Ubuntu server. This is standard and covered on here.

User accounts, groups and the repository are created. In this example the group is example-group and the project is example-project.

Protected Branches

Two branches master and production are created an set to protected. As described in the Gitlab UI, protect branched are designed to:
  • prevent pushed from everybody except masters
  • prevent anyone from force pushing to the branch
  • prevent anyone from deleting the branch
This means that these branches are sure to exist, only senior developers are allowed to push to them. It also enforces the use of merge requests for code review.

Gitlab Protected Branches

Webhook

Webhooks are configured to sent a HTTP request to the URL http://<jenkins-host>/gitlab/build_now for push events. As described later, this will trigger the Gitlab plugin in Jenkins.

Gitlab webhook

Finally, deploy keys for the Jenkins users are configured on the repository to allow the jenkins user to clone the code.

Jenkins

For this example, Jenkins v1.607 is installed on one server. All builds are performed on the slave nodes buster, earth, and jupiter, where each node has a different version of RedHat installed.

The following are the main plugins used for this example:
  • Git Plugin - Allows you to use git as a SCM with Jenkins.
  • Gitlab Hook Plugin - Allows Jenkins to receive Gitlab web hooks and trigger builds.
Note: There is a Gitlab Merge Request Builder Plugin but I have not had a chance to configure it yet.

Gitlab Hook Plugin

To configure the Gitlab Hook Plugin go to Manage Jenkins > Configure System and find the section "Gitlab Web Hook"


Gitlab Web Hook configuration


Enable "automatic project creation" and set the project master branch to "master".

Combined with the Gitlab webhook configuration above, this will cause Jenkins to create a new project for every branch that is pushed to Gitlab and have it use the project associated with the master branch as a template.

This allows Jenkins to automatically build and test every commit on every branch that is pushed to Gitlab. By having every branch tested before merging we ensure that all changes are working as expected and that they should be safe to merge into the one of the core branches.

Git Plugin

Configuration of the git plugin is from the project configuration page as shown below:

Git Plugin project configuration

The option to "Clean before checkout" will run a git clean on the project repository to remove any temporary build files from any previous runs of the job.

Job configuration

The configuration of the build job and steps is fairly standard multi-configuration project.

A configuration matrix is configured to run the job on each of the relevant build server slaves.

Build Slave Configuration Matrix


I then created an "Execute Shell" action which will call bash scripts that are in a jenkins folder as part of the repository. This allows you to the actual build commands to be under source control as part of the repository instead of in the Jenkins database. It can also allow slight variations of the build per branch.

Jenkins Execute Shell Build Step
The contents of these scripts can be as as simple or as complex as required. In this example the bash scripts are as follows:

build_step.sh runs CMake and make to compile the software.

#!/bin/bash -ex

mkdir build
cd build
cmake ..
make

test_step.sh runs CTest to make sure all unit tests pass.

#!/bin/bash -ex

cd build
ctest -V

rpm_step.sh uses CPack to create both an RPM and .tar.gz package.

#!/bin/bash -ex
cd build
make package  

Finally after the build has completed one of two options can happens:
  • On success the .rpm and .tar.gz build artifacts are archived for use by other jobs.
  • On failure an email is sent to the relevant developer group
Post Build Actions

Branch Jobs

All branches will by default create a new job that is a clone of the above master job. These jobs will be called "example-project_<branch name>" and will build the software on every push to Gitlab.

This is also true for the the production branch where a branch "example-project_production" is create. After creation it is possible to make changes to these jobs to add additional build steps required for customer releasable software, for example, you could add a test to make sure that release notes are available or you can SSH the rpm file to a central release server for installation at customer sites.

Merge Requests

As mentioned in the git flow description we have added the step that all branches must be pushed to Gitlab.

The advantage to this is that it requires a merge request to be issued. These merge requests must be approved by another developer to ensure that:
  • They meet any coding standards.
  • Changes are of sufficient quality.
  • Changes are checked by at least 2 developers to help spread knowledge.

Summary

In this post I have show the how to use Gitlab and Jenkins to help implement the git flow branching strategy. This combination allows for continuous integration code review, and helps to enable the building of high quality software.