Thursday 30 November 2023

Unique versions as pitons out of Maven Dependency Hell

The whole Maven ecosystem, and more, follows the Semantic Versioning proposal from @mojombo who made a success of being right about most things.

Even Oracle use it (no more ojdbc.jar for them).

The problem Semantic Versioning addressed was the opposing forces of

version lock
the inability to upgrade a package without having to release new versions of every dependent package
version promiscuity
assuming compatibility with more future versions than is reasonable
leading to Dependency Hell.

If that is the problem you have then Semantic Versioning will help. Semantic Versioning helps you live within the Maven ecosystem, if you are contributing to that ecosystem then use Semantic Versioning.

That is not the problem that you have if you dug your very own Dependency Hell due to having a modularisation addiction.
If the problem you have given yourself is creating over a hundred inter-dependent modules because of your desire to obey the injunction Do not Repeat Yourself (DRY) within a modular microservice world the problem you have is achieving consistency.

The Semantic Version string MAJOR.MINOR.PATCH is not unique.

What is actually unique is ARTIFACT.MAJOR.MINOR.PATCH.
Or even uniquer (sic) GROUP.ARTIFACT.MAJOR.MINOR.PATCH.

This is given in the Maven Project Object Model (POM) eg

  <groupId>org.melati</groupId>
  <artifactId>melati-parent</artifactId>
  <version>0.8.0-SNAPSHOT</version>
Using Maven and Java and yadayada you can parse this and get the actual unique identifier you want, but life is too short.

We want to use line based unix tools, grep and sed.

Typical Semantic Version strings

3.0.7
6.0.8
2.0.5
2.0.1
5.0.4
2.0.2
1.0.2
1.0.2
2.0.7
1.1.16
3.0.1
1.0.2
1.0.2
5.0.1
5.0.1
5.0.2
5.0.1

These are not all unique and those that are are accidentally unique.

Another way of achieving the same objective would be to duplicate the artifactId in the version. <version>melati-13.0.15-SNAPSHOT</version>

Or to merely ban whitespace: <artifactId>melati</artifactId>
<version>13.0.15-SNAPSHOT</version>
becomes <artifactId>melati</artifactId><version>13.0.15-SNAPSHOT</version>

This is fragile: someone will reformat the POM using their IDE and it will fall apart.

To achieve uniqueness we will prepend with an Artifact Code, this code will retain the alphabetic ordering of the artifactId and be gappy so that we can insert new artifacts without breaking that ordering. To make it easy to separate from the Semantic Version the code will be followed by a hyphen not a full stop.

SMD110-3.0.8
SMD120-6.0.9
SMD130-2.0.6
SMD140-2.0.2
SMD150-5.0.5
SMD160-2.0.3
SMD170-1.0.3
SMD180-1.0.3
SMD190-2.0.8
SMD200-1.1.17
SMD210-3.0.2
SMD220-1.0.3
SMD230-1.0.3
SMD240-5.0.2
SMD250-5.0.1
SMD260-5.0.3
SMD270-5.0.2

Now that we have a unique string we have pitons to climb out of the hell we dug for ourselves.

	find * -name pom.xml | \
	grep -v target | \
	xargs sed -b --in-place  's/old-unique-version/new-unique-version/g'