Cutting a Release
Releasing software is difficult. It is usually tedious and error prone, full of manual steps that need to be completed in a particular order. Worse, it happens at the end of a long period of development when all everyone on the team wants to do is get it out there, which often leads to omissions or short cuts. Finally, once a release has been made, it is usually difficult or impossible to correct mistakes other than to make another, new release.
Once the definition for a release has been set by a team, releases should be consistent every time they are built, allowing them to be highly automated. Maven provides a release plugin that provides the basic functions of a standard release process. The release plugin takes care of a number of manual steps in updating the project POM, updating the source control management system to check and commit release related changes, and creating tags (or equivalent for your SCM).
The release plugin operates in two steps: prepare and perform. The prepare step is run once for a release, and does all of the project and source control manipulation that results in a tagged version. The perform step could potentially be run multiple times to rebuild a release from a clean checkout of the tagged version, and to perform standard tasks, such as deployment to the remote repository.
To demonstrate how the release plugin works, the Proficio example will be revisited, and released as 1.0. You can continue using the code that you have been working on in the previous sections, or check out the following:
C:mvnbookproficio> svn co file://localhost/C:/mvnbook/svn/proficio/tags/proficio-1.0
To start the release process, run the following command:
C:mvnbookproficiotrunk> mvn release:prepare -DdryRun=true
This simulates a normal release preparation, without making any modifications to your project. You’ll notice that each of the modules in the project is considered. As the command runs, you will be prompted for values. Accept the defaults in this instance (note that running Maven in “batch mode” avoids these prompts and will accept all of the defaults).
In this project, all of the dependencies being used are releases, or part of the project. However, if you are using a dependency that is a snapshot, an error will appear.
The prepare step ensures that there are no snapshots in the build, other than those that will be released as part of the process (that is, other modules).This is because the prepare step is attempting to guarantee that the build will be reproducible in the future, and snapshots are a transient build, not ready to be used as a part of a release.
In some cases, you may encounter a plugin snapshot, even if the plugin is not declared in the POM. This is because you are using a locally installed snapshot of a plugin (either built yourself, or obtained from the development repository of the Maven project) that is implied through the build life cycle. This can be corrected by adding the plugin definition to your POM, and setting the version to the latest release (But only after verifying that your project builds correctly with that version!).
To review the steps taken in this release process:
- Check for correct version of the plugin and POM (for example, the appropriate SCM settings)
- Check if there are any local modifications
- Check for snapshots in dependency
- Check for snapshots of plugins in the build
- Modify all POM files in the build, as they will be committed to the tag
- Run mvn clean integration-test to verify that the project will successfully build
- Describe other preparation goals (none are configured by default, but this might include updating the metadata in your issue tracker, or creating and committing an announcement file)
- Describe the SCM commit and tag operations
- Modify all POM files in the build, as they will be committed for the next development iteration
- Describe the SCM commit operation
You might like to review the POM files that are created for steps 5 and 9, named pom.xml.tag and pom.xml.next respectively in each module directory, to verify they are correct. You’ll notice that the version is updated in both of these files, and is set based on the values for which you were prompted during the release process. The SCM information is also updated in the tag POM to reflect where it will reside once it is tagged, and this is reverted in the next POM.
However, these changes are not enough to guarantee a reproducible build – it is still possible that the plugin versions will vary, that resulting version ranges will be different, or that different profiles will be applied. For that reason, there is also a releasepom.xml.tag file written out to each module directory. This contains a resolved version of the POM that Maven will use to build from if it exists. In this POM, a number of changes are made:
- the explicit version of plugins and dependencies that were used are added
- any settings from
settings.xml(both per-user and per-installation) are incorporated into the POM. - any active profiles are explicitly activated, including profiles from
settings.xmlandprofiles.xml
You may have expected that inheritance would have been resolved by incorporating any parent elements that are used, or that expressions would have been resolved. This is not the case however, as these can be established from the other settings already populated in the POM in a reproducible fashion.
When the final run is executed, this file will be release pom.xml in the same directory as pom.xml. This is used by Maven, instead of the normal POM, when a build is run from this tag to ensure it matches the same circumstances as the release build.
Having run through this process you may have noticed that only the unit and integration tests were run as part of the test build. Recall from Chapter 6 that you learned how to configure a number of checks – so it is important to verify that they hold as part of the release. Also, recall that in section 7.5, you created a profile to enable those checks conditionally. To include these checks as part of the release process, you need to enable this profile during the verification step. To do so, use the following plugin configuration:
[...]
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<configuration>
<arguments>-Pci</arguments>
</configuration>
</plugin>
[...]
Try the dry run again:
C:mvnbookproficiotrunk> mvn release:prepare -DdryRun=true
Now that you’ve gone through the test run and are happy with the results, you can go for the real thing with the following command:
C:mvnbookproficiotrunk> mvn release:prepare
You’ll notice that this time the operations on the SCM are actually performed, and the updated POM files are committed.
You won’t be prompted for values as you were the first time – since by the default, the release plugin will resume a previous attempt by reading the release.properties file that was created at the end of the last run. If you need to start from the beginning, you can manually remove the release.properties and any other POM files created using mvn release:clean, or run mvn -Dresume=false release:prepare instead.
Once this is complete, you’ll see in your SCM the new tag for the project (with the modified files), while locally, the version is now 1.1-SNAPSHOT.
However, the release still hasn’t been generated yet – for that, you need to deploy the build artifacts. This is achieved with the release:perform goal. This is run as follows:
C:mvnbookproficiotrunk> mvn release:perform
No special arguments are required, because the release.properties file still exists to tell the goal the version from which to release. To release from an older version, or if the release.properties file had been removed, you would run the following:
C:mvnbookproficiotrunk> mvn release:perform -DconnectionUrl= scm:svn:file://localhost/C:/mvnbook/ svn/proficio/tags/proficio-1.0
If you follow the output above, you’ll see that a clean checkout was obtained from the created tag, before running Maven from that location with the goals deploy site-deploy. This is the default for the release plugin – to deploy all of the built artifacts, and to deploy a copy of the site.
If this is not what you want to run, you can change the goals used with the goals parameter:
C:mvnbookproficiotrunk> mvn release:perform -Dgoals="deploy"
However, this requires that you remember to add the parameter every time. Since the goal is for consistency, you want to avoid such problems. To do so, add the following goals to the POM:
[...]
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<configuration>
<goals>deploy</goals>
</configuration>
</plugin>
[...]
You may also want to configure the release plugin to activate particular profiles, or to set certain properties. Refer to the plugin reference here for more information. It is important in these cases that you consider the settings you want, before you run the release:prepare goal, though. To ensure reproducibility, the release plugin will confirm that the checked out project has the same release plugin configuration as those being used (with the exception of goals).
When the release is performed, and the built artifacts are deployed, you can examine the files that are placed in the SCM repository. To do this, check out the tag:
C:mvnbook> svn co file://localhost/C:/mvnbook/svn/proficio/tags/proficio-1.0
You’ll notice that the contents of the POM match the pom.xml file, and not the release pom.xml file. The reason for this is that the POM files in the repository are used as dependencies and the original information is more important than the release-time information – for example, it is necessary to know what version ranges are allowed for a dependency, rather than the specific version used for the release. For the same reason, both the original pom.xml file and the release pom.xml files are included in the generated JAR file.
Also, during the process you will have noticed that Javadoc and source JAR files were produced and deployed into the repository for all the Java projects. These are configured by default in the Maven POM as part of a profile that is activated when the release is performed.
You can disable this profile by setting the useReleaseProfile parameter to false, as follows:
[...]
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<configuration>
<useReleaseProfile>false</useReleaseProfile>
</configuration>
</plugin>
[...]
Instead, you may want to include additional actions in the profile, without having to declare and enable an additional profile. To do this, define a profile with the identifier release-profile, as follows:
[...]
<profiles>
<profile>
<id>release-profile</id>
<build>
<!-- Extra plugin configuration would be inserted here -->
</build>
</profile>
</profiles>
[...]
After the release process is completed, the release.properties and any other POM files that were created during the preparation are removed.