Configuring a Multi-Branch Pipeline in Jenkins – Adventures in Learning

In my previous post, I had demonstrated how to configure a Jenkins Server using Docker. The next step is to create a Jenkins job to build some software. Now, we could just do a simple freestyle job, or a basic Maven build – but that will require configuration of Jenkins every time we want to make a new project, and that makes managing the Jenkins Server via Docker more annoying. So, instead, I’m going to use the Cloudbees Bitbucket Branch Source Plugin and create a Bitbucket Team/Project job that will create the rest of my Jenkins jobs automagically for me. A similar plugin exists for GitHub, though I haven’t looked into it.

Steps

Add the Plugin

You’ll need to install the plugin first. This can be done via the “Manage Jenkins -> Manage Plugins” screen, like any other Jenkins plugin. Once you’ve done this, I would suggest updating the list of managed plugins in your Docker container, so that you won’t need to do this step again.

Create the Job

Once the plugin is installed, you can make the job via the “New Item” menu on the Jenkins dashboard. Enter a name for the job, and select “Bitbucket Team/Project” for the Job type. You’ll then be directed to the Job Configuration Page.

The official docs probably describe this page better than I can, but a quick rundown of the important options are:

Bitbucket Team/Project

  • Owner – the name of the team in Bitbucket cloud. Jenkins will look in every repository that this team has access to.
  • Scan Credentials – the credentials Jenkins will use to scan for repositories. This must be HTTP-compatible, so username & password. I’d suggest having a build-server user in the team, unless that would tip your team over to a new pricing tier, in which case I still recommend have a build-server user. You can, if needed specify a different set of credentials for checking the code out, under the ‘Advanced’ options

Project Recognizers

Determines how Jenkins will know to create a Job for a repository. The default (and, in fact, the only option available in my setup) is to look for a Pipeline Jenkinsfile. If you’ve got one of these at the top of your repository, then Jenkins will try and make a build for it. If you’ve got a branch with it in, Jenkins will make a build for that too.

Build Triggers

The most important part! The ‘right’ way to configure a build trigger is to use webhooks, or to have the build be triggered by changes in the repository. If you’ve got a publicly accessible Jenkins server, with a proper DNS and all, you can have this configured for you automatically. Otherwise, you’ll need to do this by hand. For my test purposes, I’ve got it set up to build “Periodically if not otherwise run”, with an interval of 1 day; the web hooks will come in when/if I deploy this build server to a production environment.

This also becomes the default build trigger for the generated jobs.

Orphaned Item Strategy

When you delete branches (or repositories), Jenkins will clean up the jobs. You can allow Jenkins to keep jobs around for a while, if you want.

Automatic branch project triggering

How will Jenkins know what branches to build? You will tell it, of course. We use a git-flow variant for our branches, so I want to build the master branch, and any branch starting with story/, so I use the regex (master|story\/.*)

And that’s it. Save & Apply, and if you’ve done everything correctly, you will see a “Folder Computation” job running that’s finding all of your Jenkinsfiles. But you probably don’t have any yet, so let’s go make you one.

Add a Maven build tool

My Jenkinsfile is going to end up building a basic Maven project. So before I do that, I need to make sure Maven is available in my build server. Fortunately, Jenkins has an easy way to do that – Tools.

From the Jenkins Dashboard, select “Manage Jenkins”, and then “Global Tool Configuration”. From this page, you can then create a Maven tool. Personally, I give my installations version-specific names, so I’ve made a tool called ‘maven-3.3.9’ to install, surprisingly, Maven 3.3.9. You need to get the name right in the Jenkinsfile later, so you may prefer to leave the version out; YMMV.

Create your Jenkinsfile

#!groovy
node {
// Need to replace the '%2F' used by Jenkins to deal with / in the path (e.g. story/...)
// because tests that do getResource will escape the % again, and the test files can't be found.
// See https://issues.jenkins-ci.org/browse/JENKINS-34564 for more.
ws("workspace/${env.JOB_NAME}/${env.BRANCH_NAME}".replace('%2F', '_')) {
// Mark the code checkout 'stage'....
stage 'Checkout'
checkout scm
// Mark the code build 'stage'....
stage 'Build'
def mvnHome = tool 'maven-3.3.9'
sh "${mvnHome}/bin/mvn clean verify -B"
junit testResults: '**/surefire-reports/*.xml'
}
}

This is a pretty basic build – it checkouts the code, builds it, and then archives the test results.

One key part to note is the creation of a custom workspace, on line 6. The workspace is simply where Jenkins will check the code out to. You could use the default – but if your branch has a slash in it (e.g. story/MyStory), then the slash will get replaced with %2F, which can break a lot of stuff downstream. This is a known problem, and this is the simplest workaround. I was lucky that I was doing my experiment on a story branch instead of master, or I probably wouldn’t have found out about this problem for a while.

Once you commit and push this file, you’ll probably need to run the Folder Computation task for your Jenkins job again (or just wait until it’s run periodically again). Again, if all is going well, it should find your newly added Jenkinsfile and create a sub-job to build it.

UPDATED: I have also built and tested a version that uses the S3 Plugin to copy the build artefacts to S3 for later deployment. Sadly, I’m still lacking a lead on a good tool to manage the deployment (and subsequent promotions through deployment regions) itself – it’s possible I’ll need to build one.

Wrap-up

This covers making a basic Jenkinsfile to do a very basic job – simply running a Maven build and packaging the test results. This would be an opportune time to make a backup of your Jenkins data volume container.

For the next article in this series, I’ll look at what’s involved in creating a real pipeline, where I can promote build artefacts to test environments and release candidates.

After that, I want to explore building a dependency tree – ProjectA -> ProjectB -> ProjectC – and to look at how to do this with branch builds – where BranchA can see builds from BranchA and master, but not from BranchB.

Author: Robert Watkins

My name is Robert Watkins. I am a software developer and have been for over 20 years now. I currently work for people, but my opinions here are in no way endorsed by them (which is cool; their opinions aren’t endorsed by me either). My main professional interests are in Java development, using Agile methods, with a historical focus on building web based applications. I’m also a Mac-fan and love my iPhone, which I’m currently learning how to code for. I live and work in Brisbane, Australia, but I grew up in the Northern Territory, and still find Brisbane too cold (after 22 years here). I’m married, with two children and one cat. My politics are socialist in tendency, my religious affiliation is atheist (aka “none of the above”), my attitude is condescending and my moral standing is lying down.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: