By: Lars Kruse
On: Monday, June 23. 2014
Pretested Integration Plugin
The blog post is a comprehensive description and guide to the Pretested Integration Plugin. It’s a supplement to the plugin’s wiki page at Jenkins CI.
Another interesting source of information is our paper on the Continuous Delivery Git Flow - using this plugin. Download the paper as
The plugin is Open Source. The code base is available at GitHub and it is released to the Jenkins CI community. So if you’d like to install it on your own Jenkins master, it’s simply business as usual: Go to “Manage Jenkins” on your Jenkins server and search for pretested integration.
Taking over where the Git SCM plugin left
The Pretested Integration plugin collaborates with other plugins. All the features required to establish a workspace for the build are already implemented in the various SCM plugins available.
The Pretested Integration Plugin depends on Git SCM (or Mercurial SCM if you prefer) to do its thing. Hereafter it will take over and wrap the build step: Initially with a merge and afterwards a commit - if the build was successful.
The Git SCM plugin shall have its branch specifier set to the expression you would like the Pretested Integration plugin to integrate. We’ve come to like the keyword
ready to indicate this, so we usually set the branch specifier like this:
The branch specifier in Git is set to build anything that comes from origin and matches ready/**.
In the section of “Additional Behaviours” we add:
- Clean after checkout to make sure the workspace does not have any left-overs from previous builds hanging around.
- Prune stale remote-tracking branches to make sure that the branches the pretested integration plugin sees are the same as the one on the
Specifying the branch you want to integrate into
The Pretested Integration plugin is really simple to use. The footprint in the GUI isn’t large either:
Simply check the plugin to use it. Git is default and so is the master branch as the target. Choose the strategy to use for the integration. Squashed commit is the default.
Successfully integrated branches are deleted
When the pretested integration plugin has successfully integrated a branch it will delete it even in the
A branch ready to be integrated is like raising a flag to initiate an action. If that action is successful, the flag should be lowered again.
On the other hand; if the integration is not successful and you get a failed build, then it’s likely that you want to investigate what happened. In that case, everything is left intact for you to scrutinize.
You are welcome to fix the issue on the same branch and push it again - the plugin fully supports that.
All work is delivered in one chunk
It’s generally considered good practice that developers squash their commits to match the delivery of a task or a story. If a bugfix is scattered across several commits, the solution can be difficult to review, simply because it is not nicely wrapped up. Instead, squashed commits can be used to accumulate related work.
The Pretested Integration plugin does this for you, by only allowing fast-forward merges in the situation where the contribution is actually contained in just one single commit.
In all other events, you have two choices. Either merge using
--no-ff (no fast forward) and hereby force a new commit with the accumulated changes - This is refered to as Accumulated commits in the GUI.
Or even more elegant, simply use
--squash and leave all the work on the branch behind. It’s like a history rewrite, that doesn’t actually ruin your history.
This strategy is the Squashed commits radio button in the GUI. It’s the default, recommended strategy.
The difference between the two are subtle, but important, to understand.
Have a look at this small sample tree created: Taking offset in the same commit on master, someone did two commits on a
dev branch while someone else did two commits on a
fix branch. The commits are then integrated using the Accumulated strategy (
--no-ff). The picture below is then generated by running the
git show-branch --sparse command.
A brief intro to what this command shows: In the top, above the line marked
---, you see the branch heads in the repository. In this case there are three:
master. Below the same line you see the commits listed in chronological order. The oldest at the bottom and in the margin, a set of markers indicating which branch each commit belongs to. The beauty of Git is then, that a commit can easily belong to more than one branch. E.g. the
initial commit was done on
master, and both
fix branched off from this, so it belongs to all three branches.
Accumulated strategy as you can see, all commits actually belongs to master.
Imagine that you ran the
bisect command (never tried it? read the git manual - it’s awesome!) and you are on the
master branch and go from
HEAD to the commit named
initial: In this situation your bisect would visit all commits.
Maybe a little disappointing, since you probably would claim that the stuff you did on
fix are conceptually delivered to
master and shouldn’t necessarily be regarded as contained.
OK, now let’s have a look at the same situation using the Squashed strategy.
Squashed strategy Now things are starting to look just right: Only the deliveries actually belonging to master. Dev and fix branches are detached.
In this case, if you ran
bisect as before, it would only visit the three commits that you conceptually agree belongs to master. Neat eh?
To wrap up the this story about commit strategies, we will show you the same situation once again using just a plain vanilla merge, where fast-forward merges are the default if possible.
Plain Vanilla merges Using fast forward merges may not give you what you want: Where is the commit that delivered dev to master? It's gone! Well, it was actually never there: When `dev` was merged into master it was fast-forwarded (because it's the default when possible), so at that point the HEAD of `master` is set to the commit named more dev changes and later it's forwarded to the latest on the fix branch. The trace is lost - the big picture becomes pretty blurry.
This is why the Pretested Integration plugin does not let you use plain vanilla merges.
Why not use the squashed strategy - always?
Well, that is actually our recommendation, but it does have a few side effect that you should be aware of:
If I push a branch - say it’s
dev and I push it to
ready/dev like this:
git push origin dev:ready/dev
Meaning that my
ready/dev on my target, then that would trigger the integration. Perfect.
Now, if I wanted to continue using the
dev branch, maybe do more stuff and deliver it again, then it would require that I first bring my local
dev in sync with the remote master - It can be done like this:
git checkout master git fetch origin --prune git pull origin master git checkout dev git merge master -m "restablishing the relationship"
The thing is that since the merge was done using
dev branch in internally recorded as not currently being delivered anywhere. If you wanted to delete the branch, you would be required to use force:
git branch -d dev # this would fail git branch -D dev # while this would work
Not the behaviour you would normally expect from a branch that is - at least conceptually - already delivered.
This peculiarity does not arise from the Pretested Integration plugin itself, but from the
--squash merge. As you saw, using no strategy or even the Accumulated strategy both have other side effects that are also undesired, and none of these are neither related to the Pretested Integration plugin.
It’s pure Git.
If you can learn how to manoeuvre around it, the Squashed strategy is indeed our recommendation. It implements a genuine pristine master and gives you the power of
bisect, which is a powerful feature once you learn to tame it.