Here we describe all the ceremonial stuff necessary to publish a Java library to Maven Central.
The whole release process is automated by a GitHub action workflow which utilizes the Maven release plugin. Since we need the possibility to set custom versions (e.g. 2.0.0-beta) we can not use a simple one-push-button solution. Instead, we utilize en event triggered workflow to achieve this. The release process is as following:
First go to the "Actions" tab and select the "Publish Release" workflow:
Then click the "Run workflow" button:
For an ordinary release simply click the green "Run workflow" button and leave the optional text input fields blank:
Now a "Publish Release" workflow run should appear in the list of runs:
Typically, a release by Maven simply means:
- Remove the "-SNAPSHOT" qualifier from the version in the
pom.xml
. E.g. "1.0.0-SNAPSHOT" will become "1.0.0". - Build everything, make a commit with this version and tag this version.
- Upload the resulting artifacts to Sonatype Nexus and stage them.
- Increment to next development version in
pom.xml
. In this example "1.0.1-SNAPSHOT". - Build everything and make a commit.
In the case you want to publish a custom release, e.g. a "1.0.2-beta", it is necessary to pass it to Maven. For this purpose we introduced the two optional text inputs:
- Custom version: Here you add the version with a custom qualifier. E.g. for the development version "1.0.2-SNAPSHOT" and a beta release, it is "1.0.2-beta".
- Next development version: Since Maven simply increments the last number of the semantic version and appends "-SNAPSHOT", automatic increment is not sufficient here because it will end in something like "1.0.2-beta-SNAPSHOT". So you must specify the next development version by hand. In this example still "1.0.2-SNAPSHOT".
After the first release a bot created the Maven Central sync:
Central sync is activated for io.securecodebox. After you successfully release, your component will be available to the public on Central https://repo1.maven.org/maven2/, typically within 30 minutes, though updates to https://search.maven.org can take up to four hours.
Publishing to Maven Central requires authentication in the form of username and password or user token (as username and password). We use user tokens for authentication, which are stored in our password manager.
The MAVEN_USERNAME
and MAVEN_PASSWORD
environment variable needs to be set on the device.
In the GitHub Secrets, we need to add two secrets called MAVEN_USERNAME
and MAVEN_PASSWORD
. They can be accessed in a yaml file with ${{ secrets.MAVEN_USERNAME }}
and ${{ secrets.MAVEN_PASSWORD }}
. We pass both these secrets in the env
block.
For example:
- name: Publish to Maven Central
run: ./gradlew publish
env:
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
This guide is based on Working with PGP Signatures and OpenPGP Best Practices.
Real name: the secureCodeBox authors
Email: [email protected]
Comment: Maven Release Signing Key
Fingerprint: 40AA7D29EB6DE0667D7E723ADE4725604A739BAF
Password: [see our password manager]
We create a new GPG key with:
gpg --full-generate-key
Download private key from password manager and import it locally:
gpg --import private.key
gpg --armor --export-secret-keys $KEYID
In the GitHub Secrets, add the output of this command to the SIGNING_KEY
secret.
Additionally, you must add the corresponding password as SIGNING_PASSWORD
. Both can be accessed in a YAML file with ${{ secrets.SIGNING_KEY }}
and ${{ secrets.SIGNING_PASSWORD }}
. We pass both these secrets in the env
block.
For example:
- name: Publish to Maven Central
run: ./gradlew publish
env:
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
gpg -o private.key --export-secret-key $KEYID
It is recommended to use an expiration date less than two years. We use an interval of two years. This means that we need to extend the expiration date every two years! We use an appointment of the secureCodeBox team calendar to remind us.
- Download the private key file
private.key
from password manager - Import it locally:
gpg --import private.key
- Select the key :
gpg --edit-key $KEYID
- Now set the expiry date (use
2y
for two years):
gpg> expire
- Save it:
gpg> save
- Update the private key in out password manager and GitHub Secrets