(Semantic) Versioning your Java libraries

There are a lot of ways to version your library but the semantic versioning scheme is the most used and for a good reason, by looking at the version change you can already defer if you can upgrade the dependency without any problems or if you might have to do some refactoring. Semantic versioning proposes a simple set of rules and requirements that dictate how version numbers are assigned and incremented.

Given a version number MAJOR.MINOR.PATCH, increment the:

MAJOR version when you make incompatible API changes
MINOR version when you add functionality in a backward compatible manner
PATCH version when you make backward compatible bug fixed

If you do semantic versioning by hand then mistakes could slip in. I can’t remember how many times I’ve updated a library with a patch version that would then break because the API had changed significantly. For this reason, I’ve created the semver-check maven plugin. This plugin can check if the version of your library is set correctly.

The Semantic Versioning Specification gives us generic guidelines on how Semantic Versioning must be done. Still, it needs to be tailored for a specific language so it leaves room for mistakes when translating to a specific language.

For example, changing the Java compiler output from 11 to 17 without changing any code is often mistaken for a patch version, as no code has been changed. However, this is a breaking change for those who still use your library and are still on Java 11, so therefore, this change must be handled as a major change.

Versioning

Major
When you make incompatible API changes, this is implemented with the following checks:

Changing the java version of the compiled classes to a higher version.
Removal of a public class, method, field or static variable.
Removal of a resource file
Removal of an annotation on a public API

Minor
When you add functionality in a backward compatible manner, this is implemented with the following checks:

Addition of a public class, method, annotation, field or static variable.
Removal of an annotation on a non public API

Patch
When you make backward compatible bug fixes, this is implemented with the following checks:

Any change that changes the byte code
Any change in a resource file (Note that files in META-INF/maven/ are ignored as they are generated by maven)
Any change in a dependency

Integrating

There are two ways of integrating the automatic version check in your current pipeline build.

With commandline arguments for example:
mvn package io.github.jagodevreede:semver-check-maven-plugin:check
and/or add it to the pom.xml (and replace VERSION_NUMBER with the latest released version)

<build>

<plugins>

<plugin>
<artifactId>semver-check-maven-plugin</artifactId>
<groupId>io.github.jagodevreede</groupId>
<version>VERSION_NUMBER</version>
<configuration>
<haltOnFailure>true</haltOnFailure>
<outputFileName>nextVersion.txt</outputFileName>
</configuration>
<executions>
<execution>
<id>check</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>

</plugins>

</build>

You need to run at least the package phase to be able to have valid results of the semver-check

The output of this run will be something like this:

[INFO] — semver-check-maven-plugin:0.4.1:check (default-cli) @ some-module-core —
Downloading from central: https://…/some-module/0.4.1/some-module-core-0.4.1.pom
Downloaded from central: https://…/some-module/0.4.1/some-module-core-0.4.1.pom (1.4 kB at 14 kB/s)
Downloading from central: https://…/some-module/0.4.1/some-module-core-0.4.1.jar
Downloaded from central: https://…/some-module/0.4.1/some-module-core-0.4.1.jar (16 kB at 156 kB/s)
[INFO] Looking up versions of org.acme.example:some-module-core
[INFO] Checking SemVer against last known version 0.4.1
[INFO] Class org.acme.example.MyClass has been changed on byte level
[INFO] Class org.acme.example.OtherClass has been changed on byte level
[INFO] File META-INF/MANIFEST.MF has been changed
[INFO] Determined SemVer type as patch and is currently none, next version should be: 0.4.2

The output gives us a summary of what the next version should be and a summary of why. Please note that the plugin stops searching for patch changes if it already detects a minor update for example. This keeps execution time as low as possible.

There are many configuration options available for the plugin check out the readme for all available options.

The plugin also writes a file in the target folder called nextVersion.txt by default. You can also make your pipeline read this file and set the next version to be released with for example the maven versions:set plugin

Both multimodule projects and single modules are supported. The plugin will suggest the highest version bump encountered in a project.

So try it out in your library and if you encounter any issues please raise them on github.

The post (Semantic) Versioning your Java libraries appeared first on foojay.