Skip to content

Maven Release Plugin

Created on Mar 31, ’24 ・ Updated on Mar 31, ’24

Original document: github.com/nyg/kraken-api-java/blob/master/RELEASE.md

This document explains how a kraken-api-java release is created, describes what are the tools used, and how they interact with each other.

When a release is created, the visible "output" is an updated CHANGLOG.md file, a GitHub release with a changelog, and the release artifact available in the Maven Central repository.

Overview

When wanting to make a release, the following must/will be performed:

  1. Manually execute the prepare goal of the maven-release-plugin, this will create a git tag and push it to the remote repository.
  2. Once the tag is pushed, the user-defined GitHub Actions workflow is triggered and creates a GitHub release with a changelog generated by git-cliff.
  3. Manually execute the perform goal of the maven-release-plugin, this will build and upload the artifacts to Sonatype's servers.
  4. Manually publish to Maven Central the uploaded artifacts using Sonatype's website (this can be done automatically in step 3, see below).

In theory, all of these steps could be merged into a single GitHub Actions workflow. However, for the time being, we prefer to delegate a minimum amount of steps to GitHub. Regarding step 2, it could be run manually from one's own machine as it consists of a single GitHub REST API call.

Also note that we do not use GitHub's own Maven repository, a.k.a. GitHub Packages.

Detailed steps

The Maven release plugin helps automatizing certain tasks when making releases for Maven-based projects. Releasing requires invoking two of the plugin's goals: prepare and perform.

In short, the prepare goal updates version numbers in the POMs and creates a tag in the SCM (e.g. Git). The perform goal checks out the created tag, builds the project artifacts and publishes them on a remote Maven repository.

Prepare release

The detailed list of steps executed by the prepare goal are defined here and here. In scope of this project, the main ones are:

  1. Change the version numbers in the POM files. How the plugin will compute the version number to use for release (and the one to use for the next development version) is defined by the projectVersionPolicyId and projectVersionPolicyConfig goal parameters. As this mechanism does not allow us to specify whether we are performing a patch, minor or major release (if we are even using semantic versioning), we just pass these values via the releaseVersion and developmentVersion parameters (i.e. -DreleaseVersion=2.0.0 -DdevelopmentVersion=2.0.1-SNAPSHOT on the command line).

    Note: in multi-module projects, set autoVersionSubmodules to true to have all POMs updated.

  2. Run the preparation goals. By default, the goals bound to the clean and verify lifecycle phases are executed. In this project we add another goal: exec:exec (of MojoHaus' exec-maven-plugin), in order to execute the scripts/generate-changelog.sh script which will execute git-cliff to update the CHANGELOG.md file. The config for git-cliff is in the cliff.toml file. The preparation goals are defined in the main POM file with the preparationGoals parameter.

    Note: these goals are run for every Maven module, which require us to do some trickery in the generate-changelog.sh script to only run in the parent module. It's also possible to specify a list of profiles to enable via the preparationProfiles parameter.

  3. Create and push a commit with the changes made in the two steps above. The default commit message is something like "[maven-release-plugin] prepare release v2.0.0". The message can be customized with the following parameters: scmCommentPrefix and scmReleaseCommitComment. We only modify the prefix to chore(release): in order to adhere to conventional commits.

    Note: both commits and tags are signed (this requires some Git config, GPG keys and the signTag goal parameter). It is possible to prevent pushing the commit to the remote repository by setting pushChanges to false.

  4. Tag the commit created in step 3 and push the tag to the remote repository. We specify the tag name on the command line, using the tag parameter, e.g. -Dtag=v2.0.0. If the tag is not given, see the projectTagNamingPolicyId and tagNameFormat parameters.

  5. Change the version numbers in the POM files to prepare for the next development version. As mentioned in the first step, this value is passed via the command line, with the developmentVersion parameter.

    Note: if some custom goal needs to be run after this change and before the commit, it can be specified with the completionGoals parameter.

  6. Create and push a commit with the changes made above. The message of the commit can be customized with the scmDevelopmentCommitComment.

The prepare goal also creates a release.properties file that is used by the perform goal in order to know which tag to checkout and build.

Finally, when wanting to make a release, the command line looks like this:

mvn -Dtag=v2.0.0 -DreleaseVersion=2.0.0 -DdevelopmentVersion=2.0.1-SNAPSHOT release:prepare

If we want to make sure thing are correct before committing we can use -DdryRun=true. If we prefer not to push, we can use -DpushChanges=false. If the goal execution fails, or after a dry-run, we can clean the created files using:

mvn release:clean

GitHub release

As described above, a tag is created and pushed to the remote repository. In our case, that's GitHub. GitHub allows setting up workflows to run automatically when certain events are triggered. In .github/workflows/github-release.yml, we define a workflow to run whenever a new tag is pushed. This workflow will execute the following steps:

  1. Checkout the repository using the checkout action.
  2. Generate a CHANGELOG.md file using the git-cliff-action.
  3. Create a GitHub release using the GitHub CLI and give it the CHANGELOG.md file generated above to be used in the description.

Perform release

The perform goal is simpler than the prepare goal. Its documentation can be found here. In scope of this project, the main steps are:

  1. Checkout out the version of the project specified in the release.properties file created by the prepare goal. This is done in the target folder.

  2. Run the perform goals defined by the goals parameter. The default is deploy site-deploy. While this is left unchanged, we set the releaseProfiles to sonatype-release. In this user-defined profile we do the following:

    1. use the maven-source-plugin to create a source JAR (bound to the package phase),

    2. use the maven-javadoc-plugin to create a Javadoc JAR (bound to the package phase),

    3. use the maven-gpg-plugin to sign the JARs with a previously created GPG key (bound to the verify phase),

      Note: these steps are required in order to produce a deployment considered valid by Sonatype, see here.

    4. use the central-publishing-maven-plugin to upload the built artifacts to the Sonatype's website (bound to the deploy phase). Once the artifacts are uploaded, they must be published to Maven Central. This can either be done manually through Sonatype's website, or by asking the plugin to do it for us by setting the autoPublish parameter to true. Also, as we don't want to publish the examples module, we exclude it with the excludeArtifacts parameter.

  3. Remove the files left by prepare goal.