The task: coming up with a branching strategy to work with Subversion and Jenkins.
About the challenge
In the previous weekly challenge I started setting up a continuos integration system for myself. The type of tasks I neded covered were listed as
- I check out the sources on a new machine and have a copy of the current production environment up and running
- I make changes to my theme / plugins on a workstation
- I upgrade WP itself or a plugin on a workstation
- I create new content on a workstation
- I edit content on the production server
- I update my local version with the latest comments
- I roll back changes if things go wrong
- I work on a workstation and want to carry on working on another, without the temporary code being deployed
- any of these can happen at the same time - e.g. I could start changing some code in my workstation, then make a quick amend to the content directly on the live site while out and about, then carry on working on the code change expecting the content amend not to be overwritten, and so on
This week I will look into the last two requirements, which boil down to 'establishing a branching strategy'.
Establishing a branching strategy
Feature: Establishing a branching strategy
In order to avoid stale versions being published
As a developer
I need a branching or tagging strategy that ensures not all code commited to SVN is automatically deployed
Even with a single developer branching strategy can be tricky. Although the majority of my time is spent working on feature after feature, sequentially, that's not always the case. The typical case is that I have stable version (let's call it A), then start working on some changes at my normal workstation, therefore creating a new version (let's call it B), then when away from my normal environment I discover a mistake in the content that cannot wait. At that stage somehow I have to go back to version A and do the fix, thereby creating a new version, A'. I deploy A', and then I have to merge A' and B into yet a new version, C. I will then carry on working on my change on version C. Which I eventually deploy. Then I discover something wrong with it, and decide to roll back to A'.
The issue is to decide where in Subersion A, B, A', and C will live, which ones get automatically deployed, and how everything is marked so that it can easily be found. There are many debates over branching strategy, but they seem pretty much to boil down to one of two approaches.
I can either keep the trunk for work in progress, and whenever I need to deploy I'd create a new tag. In the example above, A would be a tag, B would be the trunk. When I realise I need A', I'd push the trunk (B) to a branch, revert the trunk to the tag (A), and do the fix on it. When the fix is ready I create a new tag A' and deploy it. Then I can merge the branch (B) with the new tag (A'), and merge back to trunk.
The benefits are
- making labelling of deployment explicit
- simpler for my typical workflow: I just keep working off the same code base unless something unusual happens
The drawbacks are
- complex when working on different issues in parallel, and in my case that would be something that happens in emergencies, so it needs to be as straightforward as possible
- if I start working on a new feature, commit the code then change my mind, I will have polluted the repository
- deploying from tags is more complex in Jenkins
The alternative is to keep the trunk pristine and deploy from it, do any non trivial changes on a branch, merge back with trunk to deploy, and tag after deployment. So A would be the trunk, but it also exists as a tag. To start work on a new feature I create a branch B; when I realize I need to amend the live version and create A', I can either do that in a new branch or work directly on trunk if it's a small change. Either way, after the amend will have to be merged into trunk to be deployed; and deploying will create a tag for A'. Meanwhile work on B carries on as usual, I will just have to merge A' into it before pushing back to trunk.
The benefits are
- deploying from trunk is easy in Jenkins
- designed for parallel development; I always need to merge the branch back into trunk, whether there have been changes in the meanwhile or not
- easier to discard a branch, or park it for months, if I change my mind about it
- Scales to a larger team
The drawbacks are
- day to day workflow more laborious: I need to keep creating branches and merge them back even if it's just me working on one code base
- need to remember to tag from Jenkins after a deployment
The 'pristine trunk' approach sounds better to me. Conveniently, it also requires the least work, the only change I need to make to what I have set up so far would be to automatically tag the trunk after deployent.
Taggin a build automatically with Jenkins
Feature: Taggin a build automatically with Jenkins
In order to be able to keep track of what was deployed, and roll back if needed
As a developer
I need to automatically tag each build
There task is made simple by the Subversion Tagging Plugin. It is simply a matter of installing, then going into the configuration page for the job. Under Post-build Actions there is now a Perform Subversion tagging on successful build. I enabled it, and set the Tag Base URL parameter as MY_SVN_ROOT/tags/${env['BUILD_NUMBER']}. The Tag Comment default is fine, and the Tag Delete Comment won't be needed as each tag will have a unique name. Tested, and it worked fine.
Challenge 100% complete
That's all there is to it. The only thing to be careful is to have the discipline to always create a new branch before any non-trivial bit of work, but that shouldn't be a problem. . In part 4 I shall deal with rolling back and deployment strategies.
Possible future expansions
- Find a way to create tags using semantic versions rather than build
- Find a way to copy the latest comment from trunk as the tag comment rather than use a default Jenkins comment