versions-maven-plugin inconsistent interface blog home

Posted Sunday, 03-Mar-2024 by Ingo Karkat

story

Monday morning. The week started with the very first CI/CD build failing. A snapshot version of a third-party dependency had been updated during the weekend, and this introduced a small incompatibility in a manual mock of ours. All right, find out that new method signature, and stub out that method as well; done. But, wait! A feeling in the tummy starts to grow that this isn't good. Previous builds without that fix will still fail, defeating build reproducibility. We really shouldn't be depending on wildcard snapshot versions, but that darned essential dependency does not provide releases at all. What do we do?

This is a small Java project; just a few modules with a close set of similar dependencies: The usual Maven plugins (SureFire, FailSafe, JavaDoc, CheckStyle, PMD, SpotBugs, et al.), Apache's commons-lang3 and Guava and Gson from Google, plus a handful of domain-specific third-parties (of which a few only provide snapshots). I'm already using the Versions Maven Plugin to regularly check for updates via its versions:display-{dependency,plugin}-updates goals, and the dev team has already talked about automating this updating one day (maybe with Dependabot). Seems like this is the day I'll make the next step. Let's go…

latest versions

The plugin has a versions:use-latest-versions goal; great! That page has a lot of auto-generated information, details about all parameters, but the actual description is a single sentence: Replaces any version with the latest version found in the artifactory. Does this include plugin dependencies? Sadly, like most other Maven plugins, there's this JavaDoc-like specification and a few examples, which consist of an XML fragment and (hopefully) some description of the use case. That's easy to produce for the developers, but not very satisfying for (potential) users. Stack Overflow has many questions and answers around Maven plugins, but there, too, it's mostly this did it for me: … and very little background information or insights into the design of a plugin. Bummer.

Looks like I need to try it out for myself. Okay, so an outdated Maven plugin is not updated. Where's the corresponding goal for Maven plugins?! Dang, there doesn't seem to be one! What now? While skimming the overview, I'd noticed a versions:update-properties goal. Extracting the version numbers from the individual Maven coordinates sounds like a good idea, anyway (a previous much larger project I'd worked on did that). So now the Guava dependency looks like this:

pom.xml
<project>
    <properties>
        <guava.version>33.0.0-jre</guava.version>
        [...]
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>${guava.version}</version>
                <scope>provided</scope>
            </dependency>
            [...]
        </dependencies>
    </dependencyManagement>
</project>
	

And… it also works with Maven plugins! 🎉

        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-enforcer-plugin</artifactId>
                    <version>${maven-enforcer-plugin.version}</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>${maven-compiler-plugin.version}</version>
                    <configuration>
                        <release>17</release>
                    </configuration>
                </plugin>
                [...]
    

snapshots

Okay, back to the original problem: For reproducible builds (with unavoidable snapshot artifacts), we need to replace the generic -SNAPSHOT part with a concrete timestamped snapshot, and do this regularly.
Fortunately, the Versions Maven Plugin offers the versions:unlock-snapshots / versions:lock-snapshots goals for this. These can even be combined in a single Maven invocation: $ mvn --non-recursive versions:unlock-snapshots versions:lock-snapshots. (We do not need to recurse because all versions are defined in the top-level POM.)

For consistency, let's also move the snapshot versions to properties; then, all versions are neatly segregated into a properties block at the beginning of the POM. I'm sorry, Dave. I'm afraid I can't do that.
Locked snapshot versions must not be extracted, as [un]lock-snapshots does not consider properties; what a mess!

discussion

The Versions Maven Plugin is useful, and apparently well written. As open source, it's free to use, and a great service to the Java community. I don't want to disparage the plugin nor its authors. In fact, most other Maven plugins I've come in contact with have similarly structured documentation: a short introduction page and a few concrete examples for its main use cases. The Versions plugin at least also offers complete lists of their goals and all arguments; for some other plugins, even that has to be deducted from the few examples given. (Which is especially gruesome for XML, as there're no syntax hints at all given by the generic language.)
It might be obvious to its authors, but for someone who's new, the given examples might not perfectly fit what they want to do, but there's only the detailed technical descriptions (and just as chaotic Stack Overflow posts). Another example with this plugin, the versions:use-next-snapshots Replaces any release versions with the next snapshot version (if it has been deployed)., and the use-latest-snapshots Replaces any release versions with the latest snapshot version (if it has been deployed). A beginner might ask what's the exact different between next and latest here.

For a widely-used project, a few examples (that likely scratched the itch the developer had when they implemented the feature) plus generated technical docs aren't enough. Ideally, there's both the complete technical specs (for contributors and power users) and a gentle tutorial / usage guide for casual users. Vim does this very well: There's both a Reference Manual and a separate User Manual (that frequently links to the reference).

Ingo Karkat, 03-Mar-2024

ingo's blog is licensed under Attribution-ShareAlike 4.0 International

blog comments powered by Disqus