Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update the version of child modules in Maven?

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.

like image 843
Jose Martinez Avatar asked Aug 25 '15 16:08

Jose Martinez


People also ask

What does Mvn versions commit do?

Description: Removes the initial backup of the pom, thereby accepting the changes.

What is versions Maven plugin?

The Versions Maven Plugin is a Maven plugin which provides you the information of which libraries could be updated.


3 Answers

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 independently specified. As said before, it's a bad practise 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.

like image 94
Juan Avatar answered Nov 04 '22 16:11

Juan


1) I've also tried the version:set in the past, but never got it working right. It's supposed to be doing the same process as release:prepare, but it actually doesn't. So what you could try is mvn release:prepare -DautoVersionSubmodules -DdryRun. That is supposed to make all the updates without checking anything into the repo and without making any tags.

2) I believe the ClearTK project once followed a similar strategy as you do: they maintained a multi-module project with each module having its own release cycle. To stay on top of the situation, they implemented a custom maven plugin to warn them about dependency version inconsistencies.

https://github.com/ClearTK/cleartk/tree/master/consistent-versions-plugin

While such a plugin would not make the updates that you request, it should at least notify you when updates are necessary. To really fix your problem, you might consider following a the same route as ClearTK did and implement your own Maven plugin (or you do what ClearTK eventually ended up doing: switching to a synced release cycle ;) )

like image 24
rec Avatar answered Nov 04 '22 18:11

rec


Ok this is what I came up with. This is based on this continuous-releasing-of-maven-artifacts article.

Parent POM:

<properties>
    <!-- versions of modules -->
    <main.version>1.0</main.version>
    <revision>SNAPSHOT</revision> <!-- default revision -->
    <Module1.revision>${revision}</Module1.revision>
    <Module2.revision>${revision}</Module2.revision>
    <Module3.revision>${revision}</Module3.revision>
    <Module4.revision>${revision}</Module4.revision>
    <Module5.revision>${revision}</Module5.revision>
    <Module1.version>${main.version}-${Module1.revision}</Module1.version>
    <Module2.version>${main.version}-${Module2.revision}</Module2.version>
    <Module3.version>${main.version}-${Module3.revision}</Module3.version>
    <Module4.version>${main.version}-${Module4.revision}</Module4.version>
    <Module5.version>${main.version}-${Module5.revision}</Module5.version>
</properties>

Sample child POM with inter project dependency:

    <groupId>com.xyz</groupId>
    <artifactId>Module4</artifactId>
    <packaging>jar</packaging>
    <version>${Module4.version}</version>

    <parent>
        <groupId>com.xyz</groupId>
        <artifactId>ParentProject</artifactId>
        <version>1.0</version>
    </parent>   

    <dependencies>
        <dependency>
            <groupId>com.xyz</groupId>
            <artifactId>Module1</artifactId>
            <version>${Module1.version}</version>
        </dependency>
        <dependency>
            <groupId>com.xyz</groupId>
            <artifactId>Module2</artifactId>
            <version>${Module2.version}</version>
        </dependency>
        <dependency>
            <groupId>com.xyz</groupId>
            <artifactId>Module3</artifactId>
            <version>${Module3.version}</version>
            <type>jar</type>
        </dependency>
    <dependencies>

1) At the beginning of the cycle how can I set the parent and modules to the same version in one maven command?

You no longer need to. The parent POM can remain at the same version, only changing if the parent POM changes. In which case you can use mvn version:set -DnewVersion=1.1.1. But you don't need to for this approach.

Instead you can dynamically set the version using the property main.version. E.g. mvn clean deploy -Dmain.version=1.1.1 Also, to force dynamic passing of the version number, you can leave out the default main.version property that I included in my parent POM above.

2) How do I update both mod1's version and mod2's reference to mod1's version in one step?

This basically boils down to how to manage revisions. If I do not set the revision property in the mvn command, then all the modules will use SNAPSHOT as the revision. If I set the revision property to RC1 then all the modules will get that revision. Furthermore, if I set revision to RC1 but Module4.revision to RC2 then Module4 gets RC2 and all the other modules get RC1. This fulfills the clients request to have dynamic revisions on a per module basis.

Here are some examples:

  • mvn clean deploy -Dmain.version=1.1.1 Sets all the modules to version 1.1.1-SNAPSHOT.
  • mvn clean deploy -Dmain.version=1.1.1 -Drevision=RC1 Sets all the modules to version 1.1.1-RC1.
  • mvn clean deploy -Dmain.version=1.1.1 -Drevision=RC1 -DModule4.revision=RC2 Sets all the modules to version 1.1.1-RC1 except for Module4 which is set to version 1.1.1-RC2.

There is a caveat that must be mentioned. If you increment the version of a dependent module, for example Module1, to RC2, then you must also increment the version of all the modules that use it, for example Module4 must not be incremented to RC2 (or the next version) also. This is something that the client has been made aware of and it is also why I would prefer all the modules to have the same version. But I really do like how dynamic it came out. Essentially the version is now set via command line with no updates to POM files required.

like image 1
Jose Martinez Avatar answered Nov 04 '22 16:11

Jose Martinez