Maven Central Release Action
ActionsTags
(1)- Maven Central Release Action
Maven Central Release Action offers a convenient "one-click" approach for publishing artifacts to Maven Central
Manually create the first tag on master
branch. For example, 1.0.0
:
git tag -a 1.0.0 -m "1.0.0"
git push origin 1.0.0
We recommend all-number version such as "1.0.0" as opposed to "v1.0.0"
Tip
When the release is done, the action will automatically create and push a new version tag of
MAJOR
.MINOR
.(PATCH
+ 1)
Bumping the MAJOR
or MINOR
version still needs to be done manually using git tag -a vx.x.x -m "vx.x.x"
command
given the assumption that agile software development will change patch version most frequently and almost always
One of the requirements for publishing our artifacts to the Central Repository, is that they have been signed with PGP. GnuPG or GPG is a freely available implementation of the OpenPGP standard. GPG provides us with the capability to generate a signature, manage keys, and verify signatures.
Download the binary of GnuPG or install it with our favorite package
manager and verify it by running a gpg command with the --version
flag
$ gpg --version
gpg (GnuPG) 2.2.19
libgcrypt 1.8.5
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Home: /home/mylocaluser/.gnupg
Supported algorithms:
Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2
A key pair allows us to sign artifacts with GPG and users can subsequently validate that artifacts have been signed by us. We can generate a key with:
gpg --gen-key
Enter our name and email when asked for it and also, the time of validity for the key defaults to 2 years. Once they key is expired we can extend it, provided we own the key and therefore know the passphrase.
Caution
Please keep in deep mind that the GPG key name entered cannot contain white space!
$ gpg --gen-key
gpg (GnuPG) 2.2.19; Copyright (C) 2019 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Note: Use "gpg --full-generate-key" for a full featured key generation dialog.
GnuPG needs to construct a user ID to identify your key.
Real name: my-gpg-keyname
Email address: [email protected]
You selected this USER-ID:
"my-gpg-keyname <[email protected]>"
Change (N)ame, (E)mail, or (O)kay/(Q)uit? O
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: key 8190C4130ABA0F98 marked as ultimately trusted
gpg: revocation certificate stored as
'/home/mylocaluser/.gnupg/openpgp-revocs.d/CA925CD6C9E8D064FF05B4728190C4130ABA0F98.rev'
public and secret key created and signed.
pub rsa3072 2021-06-23 [SC] [expires: 2023-06-23]
CA925CD6C9E8D064FF05B4728190C4130ABA0F98
uid my-gpg-keyname <[email protected]>
sub rsa3072 2021-06-23 [E] [expires: 2023-06-23]
We have to provide our name and email. These identifiers are essential as they will be seen by anyone downloading a software artifact and validating a signature. Finally, we must provide a passphrase to protect our secret key. It is essential that we choose a secure passphrase and that we do not divulge it to any one. This passphrase and gpg's private key are all that is needed to sign artifacts with our signature.
Tip
- Create a GitHub Secret named GPG_PASSPHRASE whose value is the passphrase
- Create a GitHub Secret named GPG_KEYNAME whose value, in the example above, is the "my-gpg-keyname" shown above
Please keep in deep mind that GPG_KEYNAME cannot contain white space!
Caution
Please allow me to press it again - keep in mind that GPG_KEYNAME cannot contain white space!
To export the private key, list it along with any other keys installed:
$ gpg --list-keys
/home/mylocaluser/.gnupg/pubring.kbx
---------------------------------
pub rsa3072 2021-06-23 [SC] [expires: 2023-06-23]
CA925CD6C9E8D064FF05B4728190C4130ABA0F98
uid [ultimate] my-gpg-keyname <[email protected]>
sub rsa3072 2021-06-23 [E] [expires: 2023-06-23]
The output displays the path to the public keyring file. The line starting with pub
shows the size (rsa3072), the
keyid (CA925CD6C9E8D064FF05B4728190C4130ABA0F98
), and the creation date (2023-06-23) of the public key. Some
values may vary depending on our GnuPG version, but we will definitely see the keyid or part of it (called shortID,
last 8 characters of the keyid, in this example 0ABA0F98
, which we can ask gpg to output using
gpg --list-keys --keyid-format short
).
The next line shows the UID of the key, which is composed of a name, a comment, and an email.
Next we export an ascii armored version of the private key:
gpg --output private.pgp --armor --export-secret-key CA925CD6C9E8D064FF05B4728190C4130ABA0F98
Tip
Create a GitHub Secret named GPG_PRIVATE_KEY whose value is the entire output the command above
The maven-central-release-action would need the public key to verify the files, we want to distribute GPG public key to a key server:
gpg --keyserver keyserver.ubuntu.com --send-keys CA925CD6C9E8D064FF05B4728190C4130ABA0F98
Important
The GPG Keyservers supported by Central Servers are:
keyserver.ubuntu.com
keys.openpgp.org
pgp.mit.edu
The --keyserver
parameter identifies the target key server address. The --send-keys
is the keyid of the key we want
to distribute. We can get our keyid by listing the public keys.
Now the action can import our public key from the key server to CI/CD server:
gpg --keyserver keyserver.ubuntu.com --recv-keys CA925CD6C9E8D064FF05B4728190C4130ABA0F98
Releasing to Maven Central requires credentials by generating a user token via the Maven Central account page.
Tip
- Create a GitHub Secret named MAVEN_CENTRAL_USERNAME whose value is the token username
- Create a GitHub Secret named MAVEN_CENTRAL_TOKEN whose value is the token password
As part of the deployment, we are required to submit a POM file. This is the Project Object Model file used by Apache Maven to define our project and its build. When building with other tools we have to assemble it and ensure it contains the following information.
-
Correct Coordinates: The project coordinates, also known as GAV, coordinates determine the location of your project in the repository. The values are
groupId
: the top level namespace level for our project starting with the reverse domain nameartifactId
: the unique name for our artifactversion
: the version string for our artifact
The version can be an arbitrary string and can not end in
-SNAPSHOT
, since this is the reserved string used to identify versions that are currently in development. We must use semantic versioning such as 1.0.0A valid example is
<groupId>com.example.applications</groupId> <artifactId>example-application</artifactId> <version>1.4.7</version>
-
Project Name, Description and URL: For some human-readable information about our project and a pointer to our project website for more, we need the presence of
name
,description
, andurl
:<name>Example Application</name> <description> A application used as an example on how to set up pushing its components to the Central Repository. </description> <url>http://www.example.com/example-application</url>
-
License Information - We need to declare the license(s) used for distributing our artifacts. E.g. if we use the Apache License we can use
<licenses> <license> <name>The Apache Software License, Version 2.0</name> <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> <distribution>repo</distribution> </license> </licenses>
-
Developer Information - In order to be able to associate the project it is required to add a developers section.
<developers> <developer> <name>Jiaqi Liu</name> <url>https://github.com/QubitPi</url> </developer> </developers>
-
SCM Information
<scm> <developerConnection>scm:git:ssh://[email protected]/github-username/repo-name.git</developerConnection> <url>https://github.com/github-username/repo-name.git</url> <tag>HEAD</tag> </scm>
Please modify the
github-username
andrepo-name
above accordinly
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.paiondata.athena</groupId>
<artifactId>athena-parent-pom</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<name>Athena: Parent POM</name>
<url>https://github.com/paiondata/athena</url>
<description>Athena: Parent POM</description>
<developers>
<developer>
<name>Jiaqi Liu</name>
<url>https://github.com/QubitPi</url>
</developer>
</developers>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<developerConnection>scm:git:ssh://[email protected]/paion-data/athena.git</developerConnection>
<url>https://github.com/paion-data/athena.git</url>
<tag>HEAD</tag>
</scm>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<doclint>none</doclint> <!-- Turnoff all checks -->
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.4.0</version>
<extensions>true</extensions>
<configuration>
<tokenAuth>true</tokenAuth>
<autoPublish>true</autoPublish>
<publishingServerId>${gpg.keyname}</publishingServerId>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
<configuration>
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
<keyname>${gpg.keyname}</keyname>
<passphraseServerId>${gpg.keyname}</passphraseServerId>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Note
Note that we've configured project above to use the central-publishing-maven-plugin and Apache Maven GPG Plugin.
In addition, projects with packaging other than pom have to supply JAR files that contain Javadoc and sources. This allows the consumers of our artifacts to automatic access to Javadoc and sources for browsing as well as for display and navigation e.g. in their IDE.
Under regular .github/workflows
directory, create a .yml
file with a preferred name with the following example
contents:
---
name: My App CI/CD
"on":
pull_request:
push:
branches:
- master
jobs:
release:
name: Release to Maven Central
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
steps:
- name: Release
uses: QubitPi/maven-central-release-action@master
with:
user: QubitPi
email: [email protected]
gpg-keyname: ${{ secrets.GPG_KEYNAME }}
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }}
server-username: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
server-password: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
Note
More information can be found at The Central Repository Documentation
-
java-version
: The JDK version used to package up and deploy the artifact. Default to17
-
distribution
: Java distribution. Default toadopt
. See -
version-properties
: The names of all properties (separated by comma, not white space between them) that contains the artifact version. For example, we might have a POM property called<properties> <myproject.version>1.0-SNAPSHOT</myproject.version> </properties>
then we will need to pass
myproject.version
via this option in order to fully set the release properties. Internally the action utilizes Versions Maven Plugin to update all specified properties to the lastly tagged version
- Invalid signature for file: ***-1.0.0-javadoc.jar
- Invalid signature for file: ***-1.0.0-sources.jar
- Invalid signature for file: ***-1.0.0-tests.jar
- Invalid signature for file: ***-1.0.0.jar
- Invalid signature for file: ***-1.0.0.pom
There could be multiple causes for this error. Most likely one or more of the following GPG parameters are not specified correctly:
- gpg-keyname:
- gpg-private-key
- gpg-passphrase
Please note that the gpg-keyname
in the following example is my-gpg-keyname:
$ gpg --list-keys
/home/mylocaluser/.gnupg/pubring.kbx
---------------------------------
pub rsa3072 2021-06-23 [SC] [expires: 2023-06-23]
CA925CD6C9E8D064FF05B4728190C4130ABA0F98
uid [ultimate] my-gpg-keyname <[email protected]>
sub rsa3072 2021-06-23 [E] [expires: 2023-06-23]
If everything seems correct, we could try deleting and re-generating a new GPG key
Tip
There are two types of GPG keys:
- Public keys This type of key ensures data encryption and is used to validate the origin of a message. Public keys are meant to be shared openly as the message can be decrypted only with the corresponding private key.
- Private keys This type of key should be kept confidential for security reasons. A single private key is paired with a single public key counterpart and both are necessary for authentication and decryption.
To list public keys in Linux, run:
gpg --list-keys
To list private keys, use gpg with the --list-secret-keys option:
gpg --list-secret-keys
To ensure successful key removal, delete the private key first and then proceed with deleting the public key:
gpg --delete-secret-key CA925CD6C9E8D064FF05B4728190C4130ABA0F98
gpg --delete-key CA925CD6C9E8D064FF05B4728190C4130ABA0F98
sudo pkill dirmngr
or send GPG key online (assuming keyserver.ubuntu.com
keyserver)
The use and distribution terms for maven-central-release-action are covered by the Apache License, Version 2.0.
Maven Central Release Action is not certified by GitHub. It is provided by a third-party and is governed by separate terms of service, privacy policy, and support documentation.