Dan's thoughts
A release script to enforce Semantic Versioning
I’ve written before about our strategy and process for Continuous Deployment, but as we’ve built more tools and, particularly, more libraries and support modules with it, we’ve hit a snag. Not a major one by any means, but one requiring some attention.
Like any good PHP developers, we manage dependencies in our code using Composer, which allows any project to specify which other project it depends upon and, more specifically, which versions.
This means we can say, for example, that a project relies on version 1.1.x of a particular library. According to the Semantic Versioning convention this means that a Patch update, which fixes incorrect behaviour but changes no functionality, may be applied during an update, however any Minor or Major updates, which add or change functionality, may not.
Thus we can safely run updates knowing that nothing ought to break, and if we wish to take advantage of a release which does change functionality we must manually update the version used (e.g. 1.2.x) and take responsibility for ensuring our code is compatible.
This is a great setup but it gives us a problem. Composer needs to know how to find a particular version of a package so that it can be installed. It’s possible to specify a version in the package’s composer.json file but we’d be faced with a problem keeping that up to date when we release new code (more on this problem later). It’s also possible for Composer to determine the version from git branch or tag names.
We already have tags applied to released code automatically by Jenkins when a build succeeds, but they’re not in a form that conforms to the Semantic Versioning standard. All we have is a build number and build ID, which although useful are largely meaningless (“build-100-2012-12-12_12-17-37” conveys no information about the version or differences from the previous build).
What we need is some way to get Jenkins to apply a Semantic Versioning tag to released code, but this is problem too: how can Jenkins know what type of release it is (Major, Minor or Patch) in order to create the correct version number? We could use build parameters to force a user to input the version, but we want the build process to be automatic, so we need another solution.
A plugin for Jenkins, “EnvInject”, allows environment variables which we can use when creating the tag, to be injected from outside sources, such as a file. We can therefore create a file in our project, in Java property format (simple key=value text) containing the version string, which we can then use for creating the tag. If the file contains the line “VERSION=x.y.z” we can set our tag to be “${VERSION}” and the release will be tagged with x.y.z.
This is part way towards solving the problem in that we can now automatically tag released code with a version number. However, you’ll remember way back at the start of this post where I said we couldn’t just add the version number to composer.json because we’d never remember to update it. Well, now we’re faced with the same problem only in our version file instead. We need a way to keep the file up to date and ensure we don’t forget.
We need a script.
At the moment, our process for releasing a new version of the code is to merge the new code into the master branch, then push it to the central repository on our dev box. From here it will be picked up by Jenkins, built and deployed (applications are deployed to their beta environments whilst packages are pushed to an externally available git repository where applications can access them). We need to add a step to this process to update the version file and if we can go one step further by automating the process, we can help ensure it’s followed correctly.
What we’ve ended up with is a Perl script (I like Perl) which is run whenever code is ready to be released. This script needs to be told what type of release is being made (Major, Minor or Patch) and will then take responsibility for all other aspects of the release. It will check the repository status to ensure there is no uncommitted code. It will then update and commit the version file according to the type of release being made and then push the master branch to the dev box.
Using this script means a developer can make a minor release by typing “release –type minor” in the project’s working directory, everything from there will be handled automatically. Assuming all the prerequisites are met and the code builds successfully, we will have a correctly versioned package deployed to the either the external repository or the beta site. From the beta site we can then use the deployment script (see my previous article) to make the release live.
Using a script and an automated process means we can minimise the risk for human error and make the process as painless as possible for developers. I used to dread releasing code to live, but it’s now both straightforward and relatively low-risk. I think this is the best approach to take, but the need to maintain a version file, even if it’s automatically updated, still feels a little sub-optimal.
I’d love to hear any suggestions for a better approach.


