Maven Shell Features Explored

26 03 2010

In my previous blog, I gave a brief Introduction to Maven Shell and what to expect from the environment. I really like what Maven Shell has to offer so I wanted to follow up with a detailed review of some of the more interesting features of Maven Shell. Leveraging these features should make for a better Maven and Maven Shell experience and possibly allow you to work more efficiently.

ANSI Color
Maven Shell has bash like color coding to the shell, providing easy identification of the different diagnostic messages provided by Maven and Maven Shell. The colors Maven Shell produce are dependent on your shell customizations, however, with a standard terminal of black background and white text color, here are some colors used:

RED — Warnings
BOLD RED — Build errors
GREEN — Reactor summary listing of modules
GREEN — Currently executing plugin name (inside of standard white informational message)
BOLD WHITE — Executing goal of current plugin
BOLD GREEN –Successful build!
LIGHT TEAL — During plugin execution in a multi-module build, identifies which module is currently being worked on.
GOLD — Debug tag

A sample screen shot:
Screenshot2010-03-24at9.01.56AM.NXO0lJpTuMcZ.jpg

Notice, it’s really easy to determine if there is an error or warning during builds now or if it completed successfully. ANSI color is a nice feature.

Growl Notifications
If you have have Growl installed, you will receive build completion notifications via the Growl system. The notification will include whether the build failed or not. At first, I didn’t think this was highly usable as I spend most of my time watching the build perform….and there’s the kicker. If you have a long running build, you can go do other stuff while Maven cranks away and you won’t waste time flipping between windows to check on build status. Growl will popup a notification and you can go back to your mvnsh window. Higher efficiency achieved again!

MiniGrowlNotification.9mLCS1ZLO9QO.jpg
Note: I’m assuming if you have Growl for Windows installed on a Windows based machine, it will work. I do not have a Windows VM to test this out on.

Aliases
If you find yourself typing long winded commands over and over, try using the alias feature. It’s very similar to the alias feature available in Linux. Rather than typing the long winded Maven build command: mvn -Pint-test,oracle,reports -Djdbc.driver=someoraclestring -Dmyprop=val1 clean install, you could only have to type runmybuild instead by using an alias:

mvnsh(/):~/Projects/fyb_maven_workspace/multi-example> alias runmybuild mvn -Pint-test,oracle,reports -Djdbc.driver=someoraclestring -Dmyprop=val1 clean install

Once your alias is set, issue it to perform a build:

mvnsh(/):~/Projects/fyb_maven_workspace/multi-example> runmybuild
[INFO] Scanning for projects...
[WARNING]
[WARNING] Some problems were encountered while building the effective model for com.captechventures.playground:multi-example:pom:0.0.3-SNAPSHOT
...
...

Rather than specifying aliases every time you load up a new Maven shell, put aliases you tend to repeat into the $HOME/.m2/mvnsh.rc file, which is loaded at Maven Shell startup.

Variables
The Variable feature allows the user to set system properties or variables that persist during the life of the Maven Shell session. System properties are JVM system properties and key value pairs you would supply to a Maven execution with -D. For example, if you were the type of person who hated to run unit tests (I hope you aren’t), you could set a system property to skip tests:

mvnsh(/):~/Projects/fyb_maven_workspace/multi-example> set -m property skipTests true
mvnsh(/):~/Projects/fyb_maven_workspace/multi-example>

Now for every time you execute Maven during this shell session, tests will always be skipped. Be sure you’re not in the /pref command group, as there is a set command available in that command group and as you would find out soon enough, the syntax is not the same: you’ll receive a command line error. The -m property of the command identifies that you would like to set a property. You must then provide a key and then a value. If the value contains spaces, put the value in quotes. To set a variable, you would issue -m variable. To see a listing of all currently set properties or variables, issue the set command without specifying a key and value:

mvnsh(/):~/Projects/fyb_maven_workspace/multi-example> set -m property
java.runtime.name='Java(TM) SE Runtime Environment'
sun.boot.library.path='/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Libraries'
java.vm.version='14.3-b01-101'
awt.nativeDoubleBuffering='true'
gopherProxySet='false'
jline.terminal='auto'
mrj.build='10M3025'
...
...
...

I’ll be honest, I’m unsure what variables are for. By listing the currently defined variables in my shell, it looks as though they are related to how the shell and user interact with each other. I’d love to hear from some of the developers as to what the distinction is between variables and properties.

Preferences
The Preferences facility allows you to customize your environment in a way that is persisted across mvnsh restarts. While the mechanism to set/get/unset preferences is generic to support functionality in the future, the items you currently need to be concerned with are located using the list -r /mvnsh command.

mvnsh(/pref):/Developer/Applications/mvnsh-0.10/bin> /pref/list -r /mvnsh
/mvnsh
/mvnsh/cli
/mvnsh/commands
/mvnsh/commands/help
/mvnsh/commands/info
/mvnsh/commands/mvn
/mvnsh/commands/nexus
/mvnsh/commands/nexus/connect
/mvnsh/commands/pref
/mvnsh/commands/pref/export
/mvnsh/commands/pref/list

The /mvnsh tree contains the possible nodes where preferences can be set against commands in the mvnsh environment; currently, the most important to us are mvn and nexus. By setting preferences on the mvn command, we can how maven will behave when we issue that command. For instance, we can make maven executions quiet no matter what we’re building:

mvnsh(/pref):/Developer/Applications/mvnsh-0.10/bin> /pref/set /mvnsh/commands/mvn quiet true
mvnsh(/pref):/Developer/Applications/mvnsh-0.10/bin>

If you were to issue the mvn clean install command on a big multi-module project, it would be eerily void of most console output unless it were to error out. Again, this preference is persisted across mvnsh restarts, so this behavior would be exhibited the next time mvnsh is loaded. To unset this preference, merely issue the unset command:

mvnsh(/pref):~/Projects/fyb_maven_workspace/multi-example> /pref/unset /mvnsh/commands/mvn quiet
mvnsh(/pref):~/Projects/fyb_maven_workspace/multi-example>

The big question is, how do I know what preferences I can set? As far as I can tell, there is no documentation for this yet. The only way to pull this up open up source code for these commands and look for attributes annotated with org.sonatype.gshell.util.pref.Preference. The preference key name is the getter/setter convention for the attribute. For example, the quiet preference we just referenced can be found in the org.sonatype.maven.shell.commands.maven.MavenCommand class:

    ...
    ...
    @Preference
    @Option(name="q", longName="quiet")
    private Boolean quiet;
    ...
    ...

I’d love to see some documentation or examples from the developers for this feature as I’m sure it much more robust that the trivial example here.

Buffer/History
Like in a bash shell, there is a buffer/history mechanism. It works very similar to its bash shell counterparts. To see all the commands you’ve executed in mvnsh, execute the history command to retrieve an indexed list:

mvnsh(/):~> history
...
  311 ls
  312 cd wcf_workspace/wcf/
  313 mvn -DskipTests=true clean install
  314 mvn -e -DskipTests=true clean install
  315 mvn -DskipTests=true clean install
  316 mvn -DskipTests=true -Pint-test clean install
  317 mvn clean install
...

You can either use copy and paste within the shell, or you can use the recall command to re-execute a history item with the appropriate index:

mvnsh(/):~/Projects/fyb_maven_workspace/multi-example> recall 315
[INFO] Scanning for projects...
[WARNING]
[WARNING] Some problems were encountered while building the effective model for com.captechventures.playground:multi-example:pom:0.0.3-SNAPSHOT
[WARNING] 'reporting.plugins.plugin.version' is missing for org.apache.maven.plugins:maven-pmd-plugin @ com.captechventures.playground:multi-example:0.0.3-SNAPSHOT, /Users/emiles/Projects/fyb_maven_workspace/multi-example/pom.xml

Completely ignore my warnings there, those are due to some very bad practices in the POM under execution, not with the command itself.

Advertisements




Intro to Maven Shell

23 03 2010

Introduction
Sonatype has released a nifty utility called Maven Shell that allows developers to execute maven goals/phases in a pre-loaded shell environment, significantly reducing the build time. It does this by loading up a JVM, Maven, and any loaded plugin once and leverages that existing load for each subsequent build. I downloaded the .10 binary and played around with it with a very simple multi-module project. Here is a quick primer on some of the things you can expect from Maven Shell.

Efficiency
I used a simple multi-module JEE project shell that’s mostly void of code and unit tests to test out efficiency. Because some of the overhead associated with a build is loading numerous plugins, I wanted to run this build at least one time before I started comparing it to a build outside of Maven Shell. My first build ran as such, taking 7.420 seconds:

[INFO] ----------------------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] parent Multi Project .............................................. SUCCESS [1.088s]
[INFO] Util Project ...................................................... SUCCESS [3.292s]
[INFO] beans JEE5 EJB .................................................... SUCCESS [0.830s]
[INFO] web JEE5 Webapp ................................................... SUCCESS [0.807s]
[INFO] app JEE5 Assembly ................................................. SUCCESS [1.123s]
[INFO] 
[INFO] ----------------------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ----------------------------------------------------------------------------------------
[INFO] Total time: 7.420s
[INFO] Finished at: Tue Mar 23 13:46:19 EDT 2010
[INFO] Final Memory: 16M/79M
[INFO] ----------------------------------------------------------------------------------------

The subsequent build took 3.519 seconds:

INFO] ----------------------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] parent Multi Project .............................................. SUCCESS [0.264s]
[INFO] Util Project ...................................................... SUCCESS [1.569s]
[INFO] beans JEE5 EJB .................................................... SUCCESS [0.404s]
[INFO] web JEE5 Webapp ................................................... SUCCESS [0.509s]
[INFO] app JEE5 Assembly ................................................. SUCCESS [0.673s]
[INFO] 
[INFO] ----------------------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ----------------------------------------------------------------------------------------
[INFO] Total time: 3.519s
[INFO] Finished at: Tue Mar 23 13:58:24 EDT 2010
[INFO] Final Memory: 25M/79M
[INFO] ----------------------------------------------------------------------------------------

Great, but how does this compare to running maven via the command line outside of Maven Shell? Running outside of Maven Shell, the build took 7.085 seconds:

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] parent Multi Project .............................. SUCCESS [1.024s]
[INFO] Util Project ...................................... SUCCESS [3.787s]
[INFO] beans JEE5 EJB .................................... SUCCESS [0.341s]
[INFO] web JEE5 Webapp ................................... SUCCESS [1.029s]
[INFO] app JEE5 Assembly ................................. SUCCESS [0.691s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7.085s
[INFO] Finished at: Tue Mar 23 13:59:43 EDT 2010
[INFO] Final Memory: 13M/79M
[INFO] ------------------------------------------------------------------------

That’s about a decrease of ~4 seconds execution time for this very simple build, with hardly any plugins loaded. I’d imagine multi-module builds with numerous plugins executed in Maven Shell would see an exponential decrease in time to finish.

Note: After completion of this blog, I downloaded the source for Maven Shell 0.11 SNAPSHOT and built that using the .10 binary, to test these efficiency increases on an actual project with real source and real plugin inclusions. I had even better results. The initial mvnsh clean install took ~23 seconds, each subsequent clean install took ~13 seconds and the standard command line build took about ~20 seconds. That’s a savings of about ~7 seconds on every build!

Navigation
Maven Shell provides a bash-esque shell for navigation and binary execution, at least on my Mac; I imagine it would be the same on Linux distros (Windows?). You navigate as you would in a regular shell, using the cd command. The command prompt will look something similar to this :

mvnsh(/logging/logger):~/Projects/fyb_maven_workspace/multi-example> 

The mvnsh prefix helps identify to the user they are in the Maven Shell environment. The value in the parenthesis identifies which command group the user is currently in. Currently we are in the /logging/logger command group. We’ll cover command groups in a subsequent section. After the command group and colon, the current location in our file system is identified. In our example, we are in the ~/Projects/fyb_maven_workspace/multi-example directory.

Executing A Command
Executing a command is as easy as it is in the bash shell, simple type the name of the command and any parameters and press return. Changing a directory:

mvnsh(/):~/Projects/fyb_maven_workspace/multi-example> cd ..
mvnsh(/):~/Projects/fyb_maven_workspace> 

Help
Typing help or ? and pressing return will provide the help menu. The help menu will identify all the commands available to the user. To receive help on a specific command, type help command. For example, to receive help on rm, type help rm and the user will be provided with a man page on the rm command.

Something very important to know is that help is command group aware. If the current command group is /pref, not only will you see all the default commands available, you will be presented with the commands available to the pref command group.

Here is the root(/) help listing:

mvnsh(/):~/Projects/fyb_maven_workspace/multi-example> help
Available pages:
  .                 Alias to: source
  ?                 Alias to: help
  alias             Define an alias or list defined aliases.
  archetype         Command group: archetype
  cd                Changes the current directory.
  dir               Alias to: ls
  echo              Print arguments to standard output.
  encrypt-password  Encrypt passwords.
  exit              Exit the current shell.
  help              Display help pages.
  history           Display history.
  hostname          Displays the name of the current host.
  info              Display information about the shell and environment.
  install-jna       Alias to: source /Developer/Applications/mvnsh-0.10/scripts/install-jna.msh
  logging           Command group: logging
  ls                List the contents of a file or directory.
  mkdir             Create a directory.
  mvn               Execute Maven.
  nexus             Command group: nexus
  pref              Command group: pref
  pwd               Displays the current directory.
  recall            Recall an item from history.
  rm                Remove a file.
  rmdir             Remove a directory.
  set               Set a variable or property.
  shell-env         Shell environment variables overview.
  shell-files       Shell configuration files overview.
  shell-navigation  Shell navigation overview.
  shell-syntax      Shell syntax overview.
  source            Execute commands from a source into the current shell.
  unalias           Undefine an alias.
  unset             Unset a variable or property.
  wget              Fetch a file from a URL.

And the help listing from within the /pref command group:

mvnsh(/pref):~/Projects/fyb_maven_workspace/multi-example> help
Available pages:
  .                 Alias to: source
  ?                 Alias to: help
  alias             Define an alias or list defined aliases.
  archetype         Command group: archetype
  cd                Changes the current directory.
  dir               Alias to: ls
  echo              Print arguments to standard output.
  encrypt-password  Encrypt passwords.
  exit              Exit the current shell.
  export            Export preferences.
  get               Get a preference.
  help              Display help pages.
  history           Display history.
  hostname          Displays the name of the current host.
  import            Import preferences.
  info              Display information about the shell and environment.
  install-jna       Alias to: source /Developer/Applications/mvnsh-0.10/scripts/install-jna.msh
  list              List preferences.
  logging           Command group: logging
  ls                List the contents of a file or directory.
  mkdir             Create a directory.
  mvn               Execute Maven.
  nexus             Command group: nexus
  pref              Command group: pref
  pwd               Displays the current directory.
  recall            Recall an item from history.
  remove            Remove preferences.
  rm                Remove a file.
  rmdir             Remove a directory.
  set               Set a preference.
  shell-env         Shell environment variables overview.
  shell-files       Shell configuration files overview.
  shell-navigation  Shell navigation overview.
  shell-syntax      Shell syntax overview.
  source            Execute commands from a source into the current shell.
  unalias           Undefine an alias.
  unset             Unset a preference.
  wget              Fetch a file from a URL.

Notice the addition of the list, set, get, unset, remove, import, and export commands.

Command Groups
Command groups are a logical way of grouping like commands together, in a hierarchical fashion. It allows for easy execution of specific commands within a command group. For example, the /logging/logger command group has the following commands:

mvnsh(/logging):~/Projects/fyb_maven_workspace/multi-example> help logger
Help pages in group logger:
  set     Set the level of a logger.
  list    List loggers.
  levels  List valid logger levels.

If we are in the /logging/logger command group, we can execute the levels command to print out the list of logging levels available to the Maven reactor during a build:

mvnsh(/logging/logger):~/Projects/fyb_maven_workspace/multi-example> levels
TRACE
DEBUG
INFO
WARN
ERROR
OFF

You can execute a command no matter where you are in the command group hierarchy if you specify it’s full path, ie printing logger levels from within the /pref command group.:

mvnsh(/pref):~/Projects/fyb_maven_workspace/multi-example> /logging/logger/levels
TRACE
DEBUG
INFO
WARN
ERROR
OFF

Features
There is a laundry list of features on the Maven Shell wiki site, look for a subsequent blog on these features in detail.

Conclusion
If you’re a developer using Maven and do a lot of your interaction with Maven from the command line, I highly recommend taking a look at Maven Shell. It’ll shave precious time from builds allowing you to accomplish more in your day all the while providing a nice interface for interacting with Maven. Keep in mind, it won’t speed up unit tests, plugin execution, etc, but will remove the overhead associated with loading these items every launch.

References
Sonatype
Maven Shell
Maven Shell Wiki
James Lorenzen’s Blog





Maven Release Non-supported SCM Structures

8 02 2010

As I’ve mentioned in previous blogs (Maven Release Woes with Flat Project Structures and Maven Release with Flat Structures Revisited), the support for custom SCM structures is spotty in the release plugin. It is highly desirable to leverage the release plugin as it provides a lot of functionality we do not want to have to duplicate. By using profiles and activations, we can still leverage the release plugin with only marginal manual intervention.

First let’s review what functionality the release plugin provides:

  1. Check that there are no uncommitted changes in the sources
  2. Check that there are no SNAPSHOT dependencies
  3. Change the version in the poms from x-SNAPSHOT to a new version (you will be prompted for the versions to use)
  4. Transform the SCM information in the POM to include the final destination of the tag
  5. Run the project tests against the modified POMs to confirm everything is in working order
  6. Commit the modified POMs
  7. Tag the code in the SCM with a version name (this will be prompted for)
  8. Bump the version in the POMs to a new value y-SNAPSHOT (these values will also be prompted for)
  9. Commit the modified POMs
  10. Checkout from an SCM URL with optional tag
  11. Run the predefined Maven goals to release the project (by default, deploy site-deploy)

That’s a lot of functionality we don’t want to reproduce manually. Provided your aggregator and modules are setup in the “flat structure” I’ve used for demonstration purposes in the previous two blogs, you’re in luck.

We’re in a bit of catch-22 here when the aggregator is also used for inheritence. Our modules need the aggregator/parent to be deployed before they can be released. However, our aggregator/parent identifies all the modules to be released which ultimately fail because the release plugin doesn’t support our SCM structures! I recommend placing the modules declaration into a profile that can be activated by your CI server or developers when necessary. This will allow the user to release each module of the multi-module build individually. This will require the releaser to know the order of release to perform this correctly.

        <profiles>
                <profile>
                        <id>modules</id>
                        <activation>
                                <property><name>BUILD_NUMBER</name></property>
                        </activation>
                        <modules>
                                <module>../release-module1</module>
                                <module>../release-module2</module>
                        </modules>
                </profile>
        </profiles>

Here I use a property for activation named BUILD_NUMBER as this is a property Hudson supplies to all Maven builds.

There are some minor things that must be done manually to get all this to work, but this solution is better than the alternative (nothing). Once the modules declaration is located inside a non-activated profile, here are the steps to perform a release for your mutli-module build.

  1. Release aggregator/parent project.
  2. Open all modules of the multi-module build and change the parent version from *-SNAPSHOT to the released version and check in those poms. (easy with an IDE or sed/awk).
  3. Release each module individually, in the order the reactor would perform a release.
  4. Open all modules of the multi-module build and change the parent version from the released version to the new -SNAPSHOT version.

There is also a small byproduct of the release process that might be a bug with the release plugin, I’m not sure. If you’ve structured your projects like mine, the module of a multi-module build do not specify versions allowing the inheritance mechanism to supply that metadata. Once released, the release plugin will update your POM with a version element, something that was not present before. You’ll need to also manually remove these.

For example, before release:

  <parent>
    <artifactId>release-parent</artifactId>
    <groupId>com.captechventures</groupId>
    <version>0.0.12</version>
    <relativePath>../release-parent/pom.xml</relativePath>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.captechventures</groupId>
  <artifactId>release-module2</artifactId>

After release:

  <parent>
    <artifactId>release-parent</artifactId>
    <groupId>com.captechventures</groupId>
    <version>0.0.12</version>
    <relativePath>../release-parent/pom.xml</relativePath>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.captechventures</groupId>
  <artifactId>release-module2</artifactId>
  <version>0.0.13-SNAPSHOT</version>

I’m sure a small global replace, similar to the one we used to change the parent pom version, would work to remove this unnecessary entry.

Reference
Sample Project





Maven Release with Flat Project Structures Revisited

26 01 2010

You may recall my previous blog on Maven Release Woes with Flat Project Structures where I described in detail how the supposed 2.0-beta-10-SNAPSHOT flat structure release support did not perform as expected. After some back and forth with one of the Apache developers, it has come to light that this conclusion is partially correct. The term “flat structure” is contextual and in this particular context, the Apache group’s idea of context was SCM. SCM structure is what needs to be in one of a few known structures for the release plugin to function appropriately. In the end, another JIRA ticket was created for a different use case, albeit very similar to the previously closed ticket. I’ll review what flat structure does work and some variations that don’t.

SCM Structures
First, let’s understand the structures the release plugin does support. Here is your typical nested multi-module project structure that’s supported and recommended (remember SCM structure):

ExpectedSCMNestedStructure.vyHqvDoV95Ju.jpg

Now let’s take a look at the SCM flat structure that the release plugin will support, provided you’re using at least 2.0-beta-10-SNAPSHOT:

ExpectedSCMFlatStructure.aeYlwUeJjVvs.jpg

You can see that all the projects of the multi-module project are flat, behind trunk. If you have multiple projects in your SCM, I’d recommend breaking them up with project names at the SCM root, similar to what I have done here with flat-structure-test. If not, when the release plugin goes to tag your code, it will tag everything behind trunk, not just the folders your multi-module project uses. Another thing recommended to me by the developer was to use a particular configuration, -DcommitByProject=true. For example:

emiles-macbook:release-parent emiles$ mvn release:prepare -DcommitByProject=true

With this diagramed, you must still be mindful that projects must be structured completely flat behind trunk. For testing purposes, I tried a slightly altered flat structure where release-module2 was under a random, non-project folder under trunk named somesubfolder.

WackySCMBefore.wnRv2UyuruE4.jpg

This structure passed the prepare goal just fine, but when it came time to actually perform the release, it failed with an SCM checkout failure.

INFO] [INFO] Scanning for projects...
[INFO] [INFO] ------------------------------------------------------------------------
[INFO] [ERROR] FATAL ERROR
[INFO] [INFO] ------------------------------------------------------------------------
[INFO] [INFO] Error building POM (may not be this project's POM).
[INFO] 
[INFO] 
[INFO] Project ID: unknown
[INFO] 
[INFO] Reason: Could not find the model file '/Users/emiles/Projects/release-workspace/release-parent/target/checkout/release-module2'. for project unknown
[INFO] 
[INFO] 
[INFO] [INFO] ------------------------------------------------------------------------
[INFO] [INFO] Trace
[INFO] org.apache.maven.reactor.MavenExecutionException: Could not find the model file '/Users/emiles/Projects/release-workspace/release-parent/target/checkout/release-module2'. for project unknown

Summary
While a specific flat project structure use case is supported, you must be mindful of the SCM structure as slight variations may not work with the release plugin. As I recommended in my previous blog, use the nested structure for multi-module projects if you can. If your project structures already exist in SCM, if possible, restructure them to model the nested structure.

Be on the lookout for my next blog article providing some suggestions on how to tackle releases if you’re stuck in an SCM structure that the release plugin does not support.





Maven Release Woes with Flat Project Structures

22 12 2009

Introduction
The last two clients I’ve been on, I was asked to determine how Maven could be leveraged (better) at their site. One client was not using Maven at all and the second client was already using Maven for two years. Both clients were using Subversion and both had their own custom repository structure. The first client had a truly unique setup of how trunk/branches/tags were used and was nothing like I had seen before. There was only a single trunk, a single branches, and a single tags folder, and ALL projects were in numerous folders based on system functionality under these. The second client followed a more traditional model, with each project on the root of the Subversion repository with each project having a trunk, branches, and tags subfolders. However, both employed a “flat” project structure to make lives easier with their chosen IDEs.

Both clients had challenges that were were both related and un-related to Maven, but I was able to work through them until I got to the release process. As most of you know, the release plugin does a lot of work that would normally be done manually: incrementing versions, creating tags, updating SCM locations, checking into SCM, etc. The release plugin is particularly handy when dealing with multi-module projects. After spinning my wheels on the first client for a day or two, I came across the following Jira. There wasn’t much else on the net regarding this issue, nor did any of the Maven books I had read cover a flat structured maven project. Most described multi-module projects in your typical nested structure.

This stumped me and proved to be the ultimate decision to scrap the use of Maven at the first client (their odd SCM structure also played a part). The second client was nearly 8 months after the first; I was extremely pleased to see that a patch had been applied to trunk of the release plugin that would support flat project structures. Numerous test cases were added to cover these so I had high hopes. I pulled down the latest snapshot version, 2.0-beta-10-SNAPSHOT, and started to perform some releases on a sample project I threw together. I quickly found the issue is not yet fixed After some minor debugging, I thought maybe it was the SVN SCM provider so I tried several others. The following is an account of my findings.

Project Setup
This is the typical flat project structure, where the parent pom specifices each module like ../release-module1 and the modules specify the parent pom with a relative path ../release-parent/pom.xml.

Subversion
SVN was the first provider I tried. Here is a snippet of the SCM information used by the release plugin.

<scm>
    <connection>scm:svn:http://localhost/dev/release-parent/trunk</connection>
    <developerConnection>scm:svn:http://localhost/dev/release-parent/trunk</developerConnection>
</scm>

While running release:prepare, we received an error regarding directories not being working copies. Here is a snippet of the error received while running release:prepare

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Unable to commit files
Provider message:
The svn command failed.
Command output:
svn: '/Users/emiles/Projects/release-workspace' is not a working copy

CVS
CVS is the next SCM provider I tried. Here is a snippet of the SCM info from the parent POM:

<scm>
    <connection>scm:cvs:local:/Users/emiles/Projects/cvsrepo:release-parent</connection>
    <developerConnection>scm:cvs:local:/Users/emiles/Projects/cvsrepo:release-parent</developerConnection>
</scm>

Again, while running release:prepare with this provider, we receive an error similar to the one we received using SVN. Here is a snippet of the error received while running release:prepare:

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] An error is occurred in the checkin process: /Users/emiles/Projects/release-workspace/release-module1/pom.xml was not contained in /Users/emiles/Projects/release-workspace/release-parent

GIT
I hadn’t used GIT before, but read the SVN to GIT crash course and tried this SCM provider. Here is a snippet of the SCM info from the parent POM:

<scm>
    <connection>scm:git:file://localhost/Users/emiles/Projects/gitrepo/release-parent.git</connection>
    <developerConnection>scm:git:file://localhost/Users/emiles/Projects/gitrepo/release-parent.git</developerConnection>
</scm>

As with the previous two providers, an error occurred during release:prepare. While the error message is not exactly like the one received for SVN nor CVS, it seems as though there are some similarities with being “outside a working repository”.:

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Unable to commit files
Provider message:
The git-add command failed.
Command output:
fatal: '/Users/emiles/Projects/release-workspace/release-module1/pom.xml' is outside repository

Conclusion
Flat project structures are still not supported by the release plugin. The good news is that the parties responsible for the release plugin have reopened the previously closed defect, however it took them 2+ years to get this first patch together. It seems the issue is with trying to commit all the updated POMs of a flat structured multi-module project in a single transaction. If each of these were committed one at a time, there would probably be no problem. However, as with any transaction, we want all these POMs committed at once or none at all.

I love Maven, however I’m perplexed as to why this defect has been open this long without resolve. It seems it would be a major issue for numerous Maven “switchers” from other build frameworks. My advice is if you’re starting from scratch, setup your multi-module project in the expected nested structure. A lot of other tools such as Hudson and Continuum expect multi-module projects to be in a nested structure and by doing so will save yourself a lot of headache. If your project already exists and it is not in a nested structure, if it’s a small project, you might be able to live with the manual release process. If you can’t live with the manual process or if your project is a large one, you should think about re-structuring your SCM repository to provide for the expected nested project structure.

Attached are the sample projects (currently configured for SVN) and output from all three SCMs used.