How to update the version of child modules in Maven?

Jose Martinez picture Jose Martinez · Aug 25, 2015 · Viewed 16.7k times · Source

How to update the version of child modules? There are a lot of Stackoverflow questions like this but I was not able to find one that fit this exact scenario... would love if this is a duplicate.

Consider the following project.

parent
  --mod1
  --mod2

At the beginning of a dev release cycle I need to update the parent and modules to the same version. If the version of the parent and modules stayed the same throughout the release then I would would just omit the <version> tag from the modules and execute versions:set -DnewVersion=1.1.1 to kick off the dev cycle. But as it turns out the modules do not all end the cycle with the same version. As bugs and fixes materialize only those modules with the bugs and such get updated. For example the parent and mod2 might be at version 1.1.1-RC1, but mod1 might be at 1.1.1-RC2.

As such I need to:
1) Include a <version> tag in the modules to track each modules version independently.

2) If mod2 requires mod1 as a dependency I need to make sure mod2 references the latest version of mod1.

This leads to the following two questions.

1) At the beginning of the cycle how can I set the parent and modules to the same version in one maven command? I tried version:set -DnewVersion=1.1.1, but this only updates the parent's version across all the POM's but not the module's version. I also tried -N versions:update-child-modules, but I think I am using it wrong because it does nothing, just shows skipped for all the modules.

2) This is a bit harder and matches to item 2 above. How do I update both mod1's version and mod2's reference to mod1's version in one step? I know how to do it in 2 steps:

parent pom:

<properties>
    <!-- update this manually if mod1's version no longer matches parent -->
    <mod1.version>${project.version}</mod1.version>
</properties>

mod2 pom:

    <dependency>
        <groupId>com.xxx</groupId>
        <artifactId>mod1</artifactId>
        <version>${mod1.version}</version>
    </dependency>

When mod1 bumps up to 1.1.1-RC2 I update the parent POM and mod1 POM to reflect this. These are two steps. Anyway to turn it into one step?

My example was small but in real life there are many modules which makes this important time saver, plus I'm curious.

Answer

Juan picture Juan · Jan 8, 2017

Question 1)

The best way to manage application lifecycle and releases is to use the release plugin.

As you may know, Maven philosophy is convention over configuration. Maven convention is to use snapshot versions (those ending with -SNAPSHOT) during development and assigning a non snapshot version only for releases.

Say you're developing version 1.1.1. While under development, you just use 1.1.1-SNAPSHOT. Maven will take care of updates of the snapshot. If using an artifact repository, you can use -U to make sure you always have the last version of the snapshots.

When the release is ready, release plugin generates and deploys version 1.1.1 and updates POM's with the new development version, such as 1.1.2-SNAPSHOT.

About multimodule projects, there are two scenarios: modules are related but independent (such as several web applications) or they are modules of a single big application or library and they share versions. You seem interested in the latter.

The best way in this case is just to inherit the same parent (maybe also root) module, including its version. You reference parent group:artifact:version and you do not specify a version for the children. Generally you also inherit group, so your child pom can look like:

<parent>
   <groupId>com.mycompany.myproject</groupId>
   <artifactId>myproject-parent</artifactId>
   <version>1.1.1-SNAPSHOT</version>
   <relativePath>../myproject-parent</relativePath>
</parent>
<artifactId>myproject-module1</artifactId>

Now you just need to take care of children pointing to the right version of the parent with the help of the release plugin.

To help it know about children, you should make your parent pom also a root pom by including the modules section as shown later.

Question 2) I usually declare properties in the parent with all versions of all artifacts which may be referenced. If several modules share version, you just need one property. The parent can look like:

<groupId>com.mycompany.myproject</groupId>
<artifactId>myproject-parent</artifactId>
<version>1.1.1-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
   <myproject.version>1.1.1-SNAPSHOT</myproject.version>
</properties>

.......

<modules>
   <module>../myproject-module1</module>
   ...
</modules>

Children can reference other modules using

<version>${myproject.version}</version>

It is a very bad practise to declare dependencies using LATEST. Say you do this for version 1.1.1. Now you're working with version 1.1.2-SNAPSHOT and probably you have artifacts with this version installed in your local repo.

Now say for some reason you need to rebuild version 1.1.1, for instance because of a bug in production. Your build will use the new version. If you're lucky, this will break the build. If you're unlucky it may even go unnoticed to production.

Last but not least, some people like using property values to declare children versions. This is strongly discouraged and will be reported as a warning by maven. I personally don't ever do it. The reasons are also related to reproducibility of builds and the fact that maven assumes that a release build will never change. Having a module version be externally tweakable is not really a good idea.

EDIT:

Case when module versions are not aligned.

Actually both scenarios can be mixed. You can have, for instance:

Parent

---Component1

---Component2

---Component3

------Comp3Module1

------Como3Module2

------Comp3Module3

Where parent and the three component versions are different and the three modules of component3 share its same version as explained before.

Question 1) In this case each module has its version infependently specified. As said before, it's a bad prsctise to use a property to specify module version, reason why I can only recommend to literally specify versions. As already said, to manage versioning, the best way is to use release plugin, and integrate it with the version control system, such as SVN. Other answers give details on how to use it, so I won't elaborate it further, unless requested.

Question 2) The recommended approach is the same explained for the case of sharing the same version, only that you need several properties. The parent can look like:

<properties>
   <myproject.group>com.mycompany.myproject</myproject.group>
   <component1.version>1.1.1-RC1</component1.version>
   <component2.version>1.1.1-RC2</component2.version>
   <component3.version>2.0.0</component3.version>
<properties>

Then you can use dependency management to centralise version management in the parent.

For instance, in parent pom,

<dependencyManagement>
   <dependencies>
      <dependency>
         <groupId>${myproject.group}</groupId>
         <artifactId>component1</artifactId>
         <version>${component1.version}</version>
      </dependency>
      <dependency>
        <groupId>${myproject.group}</groupId>
         <artifactId>component2</artifactId>
         <version>${component2.version}</version>
         <type>war</type>
      </dependency>
      <dependency>
         <groupId>${myproject.group}</groupId>
         <artifactId>comp3module1</artifactId>
         <version>${component3.version}</version>
        <type>ejb</type>
      </dependency>
      <dependency>
         <groupId>${myproject.group}</groupId>
         <artifactId>comp3module1</artifactId>
         <version>${component3.version}</version>
        <type>ejb-client</type>
      </dependency>
      <dependency>
         <groupId>${myproject.group}</groupId>
         <artifactId>comp3module2</artifactId>
         <version>${component3.version}</version>
        <type>war</version>
      </dependency>
   </dependencies>
</dependencyManagement>

Now, to reference any module from any other module, it's as easy as:

<dependency>
   <groupId>${myproject.group}</groupId>
   <artifactId>component1</artifactId>
</dependency>
<dependency>
   <groupId>${myproject.group}</groupId>
   <artifactId>comp3module1</artifactId>
   <type>ejb-client</type>
</dependency>

Versions are automatically managed from the parent. You don't need to maintain them in children dependencies, which become also less verbose.