Fixing two problems with Maven + Mercurial + Hudson
Today I've made some improvements with my Mercurial + Maven + Hudson
setup - and reached a new level of karma, being able to do automated
releases.
Let's go in order. First let me recap what happens with the Maven
release plugin (mvn release:prepare release:perform) and Mercurial:
- A check is performed that there are no uncommitted changes
and a build is performed as a validity proof. - All the version labels in the current project are updated
(e.g. 1.3.7-SNAPSHOT to 1.3.7) and changes are committed - A tag in the SCM repository is made for the new release
(e.g. release-4.5) - All the version labels are updated again (e.g. 1.3.7 to
1.3.8-SNAPSHOT) and changes are committed again - Another copy of Maven is spawned on a temporary directory
where sources are checked out from the SCM, with the previously created
tag; another build is performed and artifacts deployed.
It's a robust sequence, as it makes sure that there are no
inconsistencies in version sequences and the release is built exactly
from the tagged files.
But there are two specific problems with Mercurial:
- The fact that Mercurial is a distributed SCM is not fully
exploited. You recall that commits are only performed locally and not
shared until a push is performed. It would be a nice thing if the push
was performed at the very end of the sequence, so in case something
went wrong, you can rollback everything. Unfortunately, Maven performs
the push at the end of step #4, and something can still fail during
step #5. I suppose that since step #1 performs a full build, Maven
developers thought that we have a proof of the validity of the build,
but unfortunately in the real world everything can fail at every time
(for instance, an OutOfMemoryError). - The check out performed at step #5 is indeed a Mercurial
clone, that is executed from
the
remote repository. This is a very stupid thing, since
Mercurial
repositories can be huge, so this operation can be time consuming
and expensive; the local repository could be cloned in a more efficient
way, but it sounds as a hard fix for Maven.
I'd say that the former is a real problem, while the latter is a
serious annoyance. Here how I fixed both problems.
First, I've used a property in the SCM section of the pom for
specifying the URL:
<scm>
style="font-family: monospace;">
<connection>scm:hg:http://kenai.com/hg/forceten~src</connection>
style="font-family: monospace;">
<developerConnection>scm:hg:
style="font-weight: bold;">${staging.hg.repo.url}</developerConnection>
style="font-family: monospace;">
<url>http://kenai.com/projects/forceten/sources/src/show</url>
style="font-family: monospace;">
</scm>
style="font-family: monospace;">
<properties>
style="font-family: monospace;">
<staging.hg.repo.url>https://kenai.com/hg/forceten~src</staging.hg.repo.url>
style="font-family: monospace;">
</properties>
In this way the URL can be overridden with a property such as
style="font-family: monospace;">-Dstaging.hg.repo.url=something:
the idea is to prepare a temporary, local repository where Maven pushes
all the changes made for the release; furthermore, the clone at step #5
is performed from that local repository, thus saving time and
bandwidth.
Second, I used the altDeploymentRepository
property of the deploy plugin (which is called by the release plugin)
that allows to override the definition of the target Maven repository
and make it point to a local folder. At the end of the process, I have
that all the
changes are stored locally; in other words, I've used
style="font-style: italic;">staging repositories
both for the sources and the build artifacts. If there are no errors, I
can later push the local Mercurial repository to the public Mercurial
repository and copy the artifacts in the Maven repository to
my public Maven repository.
This is the required configuraton for the maven release plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<configuration>
<arguments>-Dstaging.hg.repo.url=${staging.hg.repo.url}
-DaltDeploymentRepository=release-repo-hudson::default::${staging.mvn.repo.url}
-Dstaging.mvn.repo.url=${staging.mvn.repo.url}</arguments>
</configuration>
</plugin>
Basically we are propagating the relevant properties to
the spawned instance of Maven; there's a little verbosity as we need
the Maven staging repository expressed in form of a URL (see below)
while altDeploymentRepository needs
it prefixed with some specific Maven repository notation.
Maven should be called with these options:
-Dstaging.hg.repo.url=/my/hg-staging-repo
-Dstaging.mvn.repo.url=file:///my/mvn-staging-repo
style="font-family: monospace;">
For copying the Maven artifacts from the staging repository to a public
one the wagon-maven-plugin can be used, as it has an option to merge
two repositories (goal wagon:merge-maven-repos); the required
configuration is:
<plugin>
style="font-family: monospace;">
<groupId>org.codehaus.mojo</groupId>
style="font-family: monospace;">
<artifactId>wagon-maven-plugin</artifactId>
style="font-family: monospace;">
<configuration>
style="font-family: monospace;">
<source>${staging.mvn.repo.url}</source>
style="font-family: monospace;">
<target>https://services.tidalwave.it/nexus/content/repositories/releases/</target>
style="font-family: monospace;">
<targetId>maven2-release-repository.tidalwave.it</targetId>
style="font-family: monospace;">
</configuration>
style="font-family: monospace;">
</plugin>
It is possible to call it by a separate Maven invocation after the main
build, or it can be
specified in the goals of the release plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<configuration>
<goals>deploy wagon:merge-maven-repos</goals>
<arguments>-Dstaging.hg.repo.url=${staging.hg.repo.url}
-DaltDeploymentRepository=release-repo-hudson::default::${staging.mvn.repo.url}
-Dstaging.mvn.repo.url=${staging.mvn.repo.url}</arguments>
</configuration>
</plugin>
In this way, mvn
release:prepare release:perform does all but Mercurial
synchronization; the latter task just needs a regular hg push and can easily
be performed by a post-build script.
If I get an error, nothing gets out of my disk, so I can delete the
staging repos, fix the problem and retry.
It is also possible to skip the synchronization between the staging
repositories and the real ones, in case one only wants to test the
process. For instance, I'm trying to have the artifacts generated by
the assembly plugin deployed and I'm not able to do that yet; my trials
and errors can be done locally without polluting the public
repositories.
- Login or register to post comments
- Printer-friendly version
- fabriziogiudici's blog
- 6115 reads






Comments
Nice post! I will adapt to
by thiagolm - 2010-05-23 12:21
Nice post! I will adapt to git in my projects.Issue with Maven release plugin
by sravya14 - 2010-09-06 01:09
Hi, I followed the above article and implemented the suggested steps in pom.xml. When I run the following goal on the project from command prompt: mvn -e package -Dresume=false release:prepare release:perform Its giving the following exception: Caused by: org.apache.maven.plugin.MojoFailureException: Unable to commit files Provider message: EXECUTION FAILED Execution of cmd : push failed with exit code: 255. Working directory was: /home/test/.../../../ Your Hg installation seems to be valid and complete. Hg version: 1.1.2 (OK) Command output: at org.apache.maven.plugins.release.PrepareReleaseMojo.execute(PrepareReleaseMojo.java:169) at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:490) at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:694) ... 17 more Did I missed any steps? How to solve the above issue? -SravyaOne unpleasant stuff with the
by fabriziogiudici - 2010-09-06 01:57
One unpleasant stuff with the Maven/Hg integration is that it only gives partial diagnostics when a hg command fails. I suggest to reproduce the error and, as soon as it fails, try to manually run from the command line the hg push command exactly in the same way as Maven tried. So you can get a better information about the failure. For me, the most common cause of failure is when there's an extra head, and hg refuses to push unless you specify the -f flag. This can be handled in two ways: either you seldom create extra heads, and in this case you manually review them, manually perform hg push -f and then re-launch Maven; or you specify the -f flag as a default for the hg push command (can be done in $HOME/.hgrc, see the hg manual).
reply
by sravya14 - 2010-09-06 02:38
Hi fabriziogiudici, Thanks for the quick reply. I tried to run the command 'hg push' manually from the commandline after the failure. it worked with out any failures, verion is changed in pom.xml so only that changes in that file are commited. Actually when running the command "mvn -e package -Dresume=false release:prepare release:perform" , it promted for release version of project, release label of tag etc. eventhough the command exection fails , its pushing the changeset (changes in pom.xml) to the default branch in repo. but no tags have been created under tags.same on Windows and altDeploymentRepository
by aebbert - 2010-05-19 13:02
Hi, thanks for your blog about this, it really helped us to look into the right directions. I do have some remarks, and hope that you still notice that there's a reply to such an old blog post and can answer.Hi. That f**ling c:/...
by fabriziogiudici - 2010-05-19 13:09
Hi.
Thanks for replying :)
by aebbert - 2010-05-21 00:05
Hi Fabrizio,Subrepositories
by ebresie - 2009-11-02 11:54
Would Mercurial Subrepositories help?A very neat trick. I've hit
by petermount - 2009-10-30 03:18
A very neat trick.I've hit this problem several times in the past, but it gets worse with a complex project - which I have two: One has modules which have to be built on various platforms (native code) and the other retrieves xsd source from a remote svn repository which goes down every so often.
In either case, using a staging repo ensures that the public repo only gets the change once the release build has worked completely. I'll probably give this a try at the weekend as I have a release for both of those two, and they are always a pita to release.