- Validate no local changes against your SCM
- Validate that there are no SNAPSHOT dependencies
- Convert the modules to the to-be released version
- Ensure the build and Unit/Integration tests succeed
- Commit the changes to SCM, then Tag the release
- Checkout the tag to a clean location and build/deploy the artifacts
Dealing with failures on Step #2 is where I want to focus today.
Lets step back a moment and give some background on SNAPSHOTS. In Maven, all artifacts must have unique coordinates comprised of Group, Artifact, Version (there are a few more like classifier and type that aren't relevant for this discussion... see the free maven book for more info on these.)
By definition artifacts are either Releases or SNAPSHOTS. Releases are expected to be immutable and SNAPSHOTS are temporal. A SNAPSHOT version has the SNAPSHOT keyword in the version string like 1.0-SNAPSHOT. When these artifacts are deployed to a remote repository, the SNAPSHOT portion is transformed into a time-stamp (in UTC) and an incremental build number is appended. Thus 1.0-SNAPSHOT becomes something like 1.0-20090105.231034-52 when it's deployed.
The reason you can't release an artifact that depends on a SNAPSHOT is that later on, this artifact may change or simply may not be available. (Remember, SNAPSHOTS are temporal)
If you are attempting to perform a release and you have dependencies on 3rd party SNAPSHOT artifacts, you will need to resolve this before moving on. The first thing you might try is revert back to the previous release, leading to Best Practices #1 and 2:
Rule #1: Only use 3rd party SNAPSHOTS when absolutely necessary
Use SNAPSHOTs temporarily to validate fixes but don't commit your poms this way unless...
Rule #2: If you do need it, record why you need it
Preferably as a comment in the Pom, referring to a bug number.
Adherence to this rule will make it easy to determine if you actually need the new dependency. It will also make it easy to decide if you can work around it or could live without it.
Assuming you really do need it, you have a couple of choices. The first is to request a release of the artifact. Chances are though if you are at this stage of a release, it's too late to wait.
The obvious alternative is to control your own destiny and release anyway.
Not the solution
A common anti-pattern is to find the exact time-stamp-build number of the SNAPSHOT you are depending on and put that as your dependency version. We refer to this as locking to a timestamp.
Rule #3 Never release using the Time-Stamped snapshot
There are several reasons why this is a bad idea.
- First is that Maven and Maven Tools that mimic Maven properly are able to detect a SNAPSHOT based on the timestamp pattern. (See my previous entry showing how this is done correctly.) The release plugin will still fail if you do this because it correctly understands you have SNAPSHOT dependencies. The plugin has a flag to allow bypass this check and people unfortunately use it far too often.
- Since Maven and related tools see these time-stamped artifacts as a SNAPSHOT, you will have to keep this artifact in a SNAPSHOT repository. This might be acceptable if you take that artifact and host it in your repository manager but you must remember to never delete it. This would be complicated if you wanted to share your release with people outside your organization.
- The dependency could disappear from the external repository. If you aren't using a repo manager (What? Didn't you get the memo?) then you are totally subject to the cleanup process of the external repository.Not a good place to be.
There are a few approaches to this that are acceptable. Which one you choose depends on your level of paranoia. This process can be recursive if your SNAPSHOT dependency has other SNAPSHOT dependencies. They are all approaches to rule #4:
Rule #4: Convert the SNAPSHOT version to a Release version and host it in your repository
- Just deploy the artifact: The simplest approach to this is to take the SNAPSHOT binary artifact itself and deploy it to your repository. Remember to adjust the Pom.xml as needed. If you use Nexus, then you can upload via the UI and the artifact will be renamed based on the version you choose. (others can upload via the ui, but I'm not sure if they rename the file for you) If you use this approach, be aware that the artifacts produced by Maven have a MANIFEST that contains the SNAPSHOT version. Maven 3.x uses this for plugins and may refuse to use this artifact based on the version not matching what was expected. The trouble with this approach is that should you need to patch this artifact down the road, you don't know exactly what sources produced it.
- Build from the sources: The next approach (and this is my preferred method) is to checkout the sources and build the snapshot yourself. This way I know exactly what I have should I need to patch it later. If you are uber-paranoyed you can commit the source to your scm for posterity. If you trust the remote SCM (as I do for the Apache projects) then simply recording the scm revision used to retrieve the source is enough. You'll need to change the versions of the artifacts in the POMS and then deploy it to your repository. I like to put the svn revision right into the artifact version such as 1.0-[my company name]-[svnid]. Look at an example of how we did this this for 2.1-SNAPSHOTS of Maven code used by Nexus and M2eclipse.
Rule #5: Change only the version, not the groupId or ArtifactId
When you make these new versions, resist the urge to change the groupid as well since this will change the coordinates and Maven wouldn't detect collisions. You could end up with your copy and the official copy of the same dependencies in your build. Having changed only the version makes it much easier to switch back to the official public release when it becomes available.