Skip to content
Lars Bruun-Hansen edited this page Jun 10, 2024 · 2 revisions

Publish to Maven Central - using GitHub and Maven

This is an opinionated recipe for creating your POM and your GitHub Workflow so that creating an official release of your library and publishing it on Maven Central is simple, easy and transparent and lends itself towards how modern CI systems (like GitHub) works. As opposed to using some approach that (maybe) was justified in the era before we had Git and before we had CI systems like GitHub or GitLab.

The opinionated part

  • The correct way to version software libraries is to use SemVer.

  • We must have a unambigous way to marry a version string with a Git commit so that we can determine exactly from which commit a given version was build. Git tags provide this feature.

  • It is solely git tagging which should drive the process of how a release is named. This also means that having a version string in your source code is a no-no. But wait! We have a <version> element in the POM. What about that? Well, it ideally shouldn't be there. This is an architectural flaw in Maven. However, since Maven 3.5 (April 2017) we have Maven CI Friendly feature which luckily fixes the problem. In Maven 4 (not yet released as of June 2024), the architectural flaw I'm talking about will be fully resolved with the upcoming separation between the Build POM and the Consumer POM. But for now, the Maven CI Friendly feature in combination with the flatten-maven-plugin works as described on the tin.

  • Never release software from a developer's workstation. Just don't. Make it near impossible to do so. Releasing should only be done from the CI pipeline. As a consequence we need certain secrets to exist in the CI system, for example the credentials to publish to Maven Central, the private key to sign the software, etc. In this recipe there is deliberately no <distributionManagement> section in the POM. Instead such values are set in CI pipeline (where they belong, IMO).

  • Don't be tempted to use legacy approaches like maven-release-plugin. It is from another day and age and is completely redundant if you have a modern CI system.

  • Creating a new release should not be smoke and mirrors. The trigger for the CI pipeline to execute the steps required to publish your library should be a direct human action. You, as a developer, decide when it is time for a new official release of your library. Modern CI systems have a "Release" concept. Use it! At the Git level it will merely create a tag and that is all you really need. Also, don't be tempted to come up with schemes for the pipeline to automatically figure out what the next version number is. I've been down that road. It is fruitless (and pointless). For stuff published to Maven Central there has to be a human decision behind the version naming. Every time.

  • Stay clear of third-party GitHub Actions .. if at all possible. Using a GitHub Action from an untrusted source represent a security risk. For publishing to Maven Central the official setup-java GitHub Action has all we need to get the scaffolding for Maven correct (including PGP signing). There is no need for something more than this.

  • Let Maven do its work. Maven has a good and well-proven phase sequence. For the most part you only need a single invocation of mvn in your CI pipeline and the only thing that should change between CI execution for say a PR, a commit on a branch and when we want the software to be released for public consumption is the phase name we use on the mvn command line. Releasing your software to the outside world is a deploy, everything else is a verify. That's it! If your CI pipeline has several invocations of mvn, I would question if you are the right thing. Work with Maven, not against it !

    Who can release the software?

    In this recipe, we've made checks that the process must be started from the GitHub UI's "Releases" page. If developer just does tagging in git and pushes that tag, it will not create a new release. This is by design and deliberate. We want the developer to create releases from the UI's dedicated feature for that.

    As per GitHub's build-in roles (Read, Triage, Write, Maintain and Admin), by default everyone with Write access to the repo (or higher) will be able to create a release. I find that acceptable. If you've granted someone write-access to the repo you have already granted them the right to wreck everything. And remember we are talking about a library here. The word "releasing" means that the library becomes available for the public to consume your library from Maven Central. It does not mean that it will actually be deployed on any production system.

    If you need the permission to release the software to be a separate role, then I bet there are ways to do it.

Clone this wiki locally