Properly versioning and releasing code can be fiddly if it’s done properly. If you’re not cutting corners, the process involves several steps. If these steps are performed manually the process is error prone and time consuming. I’ve often found it’s easier just to cut a few corners. Why change the version in the Maven pom when you could perform all releases against a single SNAPSHOT version? Why tag the release build when you could probably work out the release version from a Subversion log? Why put release artifacts in a release repo when you can rebuild from a historic version?
I’m not going to argue that you shouldn’t cut corners when creating a release build (all the same, just don’t!). I will argue that the process need not be fiddly. So long as a Maven project is correctly set up, the whole thing can be done with a single command. Wire that single command up to Hudson, Jenkins, CruiseControl or whatever and you can create properly versioned release builds in a single click or (if you’re so inclined) as a scheduled task.
At the heart of this is the powerful – though slightly inflexible – maven release plugin.
Maven Release Plugin
The Maven Release Plugin will perform the following actions necessary to create a versioned release (some steps omitted – check Maven documentation for full details):
- Update the version number of your project (and subprojects) in the pom file(s) to a full release number (remove SNAPSHOT suffix)
- Run project test suite
- Commit updated POM(s) to SCM
- Create a release tag in SCM
- Update the version number of your project (and subprojects) in the pom files(s) to next SNAPSHOT number
- Commit updated POM(s) to SCM
- Checkout release tag from SCM
- Build and deploy released code
Obviously, if you’re doing this sort of process manually it will take a while. The above steps are done by Maven using two goals – release:prepare and release:perform. These goals can be run interactively allowing the build user full control over new version numbers, SCM tags and so on. They can also be run together non-interactively which is more suited to integration with a CI tool.
Setting up the project
As you may expect from Maven’s Convention over Configuration ethos, your project and release procedure must be set up The Maven Way or not at all. The project must be set up with the following in order for the plugin to work effectively:
The project version number must follow Maven conventions. Development should be done in a SNAPSHOT version and releases will have the minor version incremented by default. The version of all sub-modules should be the same as the version of the parent pom and all sub-modules should be released together.
My parent pom contains the following config:
This version will be changed to 1.2 on release and then 1.3-SNAPSHOT immediately following release.
SCM – Software Configuration Management
The release plugin will run unit tests by default. Any test fails will prevent the release.
Software should not be released with SNAPSHOT dependencies so the release will fail if any SNAPSHOT dependencies exist in the pom.
The release plugin will attempt to deploy the built artifacts so the <dependencyManagement> element must exist in the pom. This should point to a repository manager if you have one. I just want my artifacts deployed to a local file system so I’ve defined my distribution management as follows:
Local file system
Local file system
Executing the plugin
It’s easiest to run the two main goals of the plugin interactively from the command line.
mvn release:prepare -DdryRun=true
This will make all necessary changes to the poms but not actually check in anything to SCM or deploy artifacts. This is useful to check that the plugin does what you think it does.
mvn release:prepare -Dusername=stevie -Dpassword=password
Leaving out the dryRun property will perform all preparation steps for your release. This includes updating the pom to a new version number, creating a tag in SCM, updating the pom to the next SNAPSHOT and checking in all pom changes to SCM. The plugin will prompt you for the version numbers and SCM tag name to use but it will offer sensible defaults.
The username and password here is for the SCM system. They could be defined in the server section of your settings.xml rather than on the command line. This would allow password encryption if you’re into that sort of thing.
The release:prepare goal will check in two changes to the trunk. It will update the pom with the release version number and then update again with the next development SNAPSHOT version number:
It will also create a new tag corresponding to the release:
This will actually build the release prepared in the previous step. A fresh copy of source code will be checked out from the newly created SCM tag, built and deployed to the location defined in <distributionManagement>
Batch mode – non-interactive
It may be more useful to execute the plugin non-interactively. For example if it’s to be run from a CI tool such as Jenkins. The -B or –batch-mode switch will do this. All values usually picked up interactively will be picked up from command line or from a release.properties file. If none are found, the default values will be used.
mvn -B release:clean release:prepare release:perform
I’ve assumed here that the SCM username and password is picked up from settings.xml rather than command line.