At HootSuite, we use [Jenkins](http://jenkins-ci.org/) as our continuous integration tool and [Git](http://git-scm.com/) as our source code manager (SCM), which we host in-house with [GitLab](https://www.gitlab.com/).

Our git workflow consists of short-lived *feature-branches* that are merged into the production branch as soon as possible. We build and test each feature branch as it is pushed to the Git remote. This gives our engineers feedback before we merge to production and avoids delays in our continuous deployment pipeline.

Unfortunately, Jenkins is not suited to using Git with a *feature branch* workflow. Jenkin’s Git plugins assume a single branch build which obviously contrasts to using multiple feature branches.

This blog post will detail how we overcame these deficiencies to successfully use Jenkins with a multiple *feature-branch* Git workflow.


## Jenkins And Git
Git is arguably the most popular SCM at present, so it is surprising that Jenkins does not support it out the box. In order for Jenkins to use Git, at least two plugins need to be installed:

1. [Git-Client-plugin](https://wiki.jenkins-ci.org/display/JENKINS/Git+Client+Plugin)
1. [Git-plugin](https://wiki.jenkins-ci.org/display/JENKINS/Git+Plugin)

Even though the above plugins sound the same, they perform different tasks. The first plugin (*Git-Client-plugin*) is a low-level API to execute git operations. The second plugin (*Git-plugin*) adds Git as an SCM option for Jenkins jobs. The second plugin relies on functionality from the first plugin, and so both must be present for Git to work with Jenkins.

## Jenkins And GitLab

Gitlab has the ability to send notifications whenever a push to a repository happens. Since we use GitLab, we wanted to use these *push notifications* to drive the Jenkins build process. This requires us to use an additional plugin: The [Jenkins Gitlab](https://wiki.jenkins-ci.org/display/JENKINS/Gitlab+Hook+Plugin) plugin (a [similar plugin for GitHub](https://wiki.jenkins-ci.org/display/JENKINS/GitHub+Plugin) is also available). This plugin does the following:

1. It allows us to use GitLab’s *notifications* to drive Jenkins.
1. It lets Jenkins react to git push notifications directly without needing to poll.
1. It forces Jenkins to [build the exact branch](https://github.com/jenkinsci/gitlab-hook-plugin#parametrized-projects) that was responsible for the push notification.

While all the items above are important, it is the last item that is the most important: When left to its own devices with only the two first plugins installed, Jenkins will guess what to build based on changes that it tries to detect. Unfortunately when multiple branches come into play, its guess for what to build is normally incorrect.

## Producing Correct Git Information
We are now able to make Jenkins react to GitLab push events and build the correct branch. But this is only half the story: Incorrect data will be inserted into a build’s xml due to Jenkins’s plugins expecting a single branch.

Jenkins obtains its SCM data by “diff’ing” the current build with the previous build. This only works if builds are presented in a linear fashion (i.e. if a there is only one branch). With multiple branches being present, inaccurate information gets associated with a build’s [xml api](https://wiki.jenkins-ci.org/display/JENKINS/Remote+access+API).

### Obtaining Accurate Git Information For A Build
Accurate information is easy to obtain because there is a local Git repository sitting inside the Jenkins workspace that can be queried. The only thing needed for input is a SHA commit revision, which thankfully, the *git plugin* accurately reports. Once accurate information is obtained, it is shared throughout the build via an injected environment variable.

### Embedding The Correct Information Into A Build’s XML With Groovy
Having obtained accurate information, the next problem becomes embedding it in the build’s xml (having this information associated with a build allows us to access Jenkin’s api with accurate information).

This is where [groovy scripting](http://groovy.codehaus.org/) comes in handy: It provides direct access to [Jenkins internal api](http://javadoc.jenkins-ci.org/), which in turn has the ability to embed information in a build.

Using Groovy with Jenkins requires the following:

1. The [groovy script plugin](https://wiki.jenkins-ci.org/display/JENKINS/Groovy+plugin) needs to be installed in Jenkins.
1. [Groovy](http://groovy.codehaus.org/) needs to be installed on the machine that will act as the master (in Ubuntu, this can be done easily by `apt-get install groovy`)

Once the above has been installed, Jenkins will have two additional options available as build steps:

1. Execute Groovy script.
1. Execute system Groovy script.

To embed information inside a build, we added a *system Groovy script* with the following code:

import hudson.model.*

// sets build parameters based on the given map
// only supports StringParameterValue
def setBuildParameters(map) {
def npl = new ArrayList()
for (e in map) {
npl.add(new StringParameterValue(e.key.toString(), e.value.toString()))
}
def newPa = null
def oldPa = build.getAction(ParametersAction.class)
if (oldPa != null) {
build.actions.remove(oldPa)
newPa = oldPa.createUpdated(npl)
} else {
newPa = new ParametersAction(npl)
}
build.actions.add(newPa)
}

setBuildParameters(["GIT_AUTHOR":build.getEnvironment(listener)['GIT_AUTHOR'] , "GIT_COMMIT_MESSAGE":build.getEnvironment(listener)['GIT_COMMIT_MESSAGE'] ])

In the above code snippet, the `GIT_AUTHOR` and the `GIT_COMMIT_MESSAGE` (the *author* and their *commit message* for the current build respectively) are being embedded.

## Conclusion
Jenkins does not have *in house* git functionality, therefore plugins need to be added to give it this functionality. Jenkins Git plugins are deficient in two ways:

1. They expect Jenkins to poll the Git repository for changes.
1. They produce incorrect information if multiple branches are used.

The [gitlab plugin](https://wiki.jenkins-ci.org/display/JENKINS/Gitlab+Hook+Plugin) solves the former issue, and the [Groovy script plugin](https://wiki.jenkins-ci.org/display/JENKINS/Groovy+plugin) solves the latter.

—————–

Interested helping us build cool stuff at HootSuite? Apply here!