Montag, 26. November 2012

How has our "release" process matured with Maven...


...without-using-the-maven-release-plugin?!! Below is a simplified account of the advancements in our software release process. This was in an organizationally and technically complex project. Whether all of the efforts were justified or not is only hypothetical - it was just the way it happened - and now everything is much better.

In the Beginning

The project is organized in a standard Maven structure with a CI server responsible for doing builds. In the beginning, the corresponding SNAPSHOT artifacts from the Maven repository were downloaded and manually deployed on the test systems. If any problems were found, the developers would commit the fixes, the modules built again and so on. At that time, a release was a defined list of current SNAPSHOT artifacts. At some point, the testers would ask that the current "release" be deployed to another test environment. Which version was that exactly? We're still on SNAPSHOTs and in the meantime we may have built new SNAPSHOTs! We also tried to use the Maven release plugin, but with 8 hour builds, it just wasn't practical. 

The First Improvement

So a first optimization was that any deployed release was manually copied to the file-system in a directory with a unique release number. This allowed us to re-deploy the "release" artifacts consistently. 

The Second Improvement

Then came some automation. The modules were downloaded using the Maven dependency plugin and stored away the file-system. And while we're at it: why not take the same artifacts, re-pack them (with the assembly plugin) to the appropriate deployment packages, and deploy them to the "release" repository? Said and done. To implement this, the (non-SNAPSHOT) version number must be passed from the outside with a Java system property (-D), which meant that the ${project.version} was no longer a constant and that the "release" POM looked like the following:

      <!-- execution configuration goes here -->
      <!-- use copy goal to download all artifacts 
           being part of your release -->
      <!-- execution configuration goes here -->
      <!-- use copy goal to download and attach all artifacts  
           being part of your release -->
      <!-- execution configuration goes here -->
      <!-- assembly all your artifacts to 
           a distribution (zip, ear, tar etc.) -->
With mvn clean deploy -Ddistribution-id=1.2.3-RC-5 a release is created! The dependency-attach-plugin is a small extension to have artifacts attached to the project and deployed to the "release" repository -- those that belong to the individual release but are either not packaged with the assembly plugin or those that we wanted "re-versioned" from SNAPSHOTs to releases. See [1] to get similiar information about you can set up your builds and why the maven-release-plugin is not practical.

Separation of the Build and the Release

Using the above mechanism, the software build and release creation could be separated. The software build consisted of a Maven build, which built all the artifacts with SNAPSHOT versions. This was done whenever a developer commits a change to the SCM. When the software build was successful and a release was required, the release build (using POMs like the one above) would download SNAPSHOT artifacts, and upload "release" artifacts with the version given as the "distribution-id" system property. Inside of our release artifacts there would still be SNAPSHOT artifacts (e.g., SNAPSHOT JARs inside of WARs). Is that bad? Maven purists would say "yes". However, we found that this process functioned successfully – we never had the wrong SNAPSHOT artifact in one of our deployments. Nevertheless, SNAPSHOTs still make people uncomfortable… 

Another Improvement – Good Bye Snapshots

How do we get completely away from SNAPSHOTs? No, we would not try the release plugin again. Wouldn't it be nice if the POMs could normally be configured with a constant version (e.g., a SNAPSHOT version), and this constant could be overridden at build-time by another value (i.e., a release version) without the POM file being changed? How could this be done? The solution is to adapt the version when Maven is loading the POMs to build the project -- provided that an appropriate system property is set (we have chosen the property name "version.override"). If "version.override" is not set, just leave the constant in the POM. The implementation requires a small Maven extension which must be installed in the extension directory ($M2_HOME/lib/ext) on the CI build server. With this extension installed, we can do mvn clean deploy -Dversion.override=1.2.3-RC-5 and all built artifacts are deployed to the repository with the release version "1.2.3-RC-5". This means that every CI server build can be a real release with easily locatable artifacts in the repository, and that every developer's local build can continue to just use SNAPSHOT versions (and no Maven extension is required on the developer workstations). However, there is one drawback to using this Maven extension. The POMs that are deployed will still contain the original constant version, and not that from the "version.override" property. This can lead to problems if other projects reference the POM and it has transitive dependencies. We are looking into ways of rectifying this issue. As of this writing, the Maven extension has been successfully used for a few weeks in our Continuous Delivery environment.

Links & References

[2] Source code and documentation:
[3] Binaries - Maven Core extension:
                  - dependency-attach-plugin:

Keine Kommentare:

Kommentar posten