Automate Releases to Maven Central via GitHub Actions

November 4, 2021


Automate Releases to Maven Central via GitHub Actions

After a long hiatus from maintaining the CleverTap/apns-http2 repository, some foundational work was required: switch to publishing on Maven Central directly. Earlier, we used to sync most of our artefacts with Bintray, and have Bintray do the heavy lifting of syncing it with Maven Central. Unfortunately, Bintray was sunset earlier this year.

Since I hadn’t done this often, it took me some time to find and read scattered documentation to get it working seamlessly. To be honest, it was my first time setting up this automation via GitHub Actions. Therefore, we thought that we’d document this process, in the hope that it might help somebody else move faster. 🙂

In a nutshell, here are the steps that were taken:

  1. Get publishing rights to com.clevertap on OSSRH (since we already had this group ID)
  2. Generate credentials for OSSRH
  3. Generate a GPG key, and publish it to a well-known key server
  4. Stitch it all together with a GitHub Workflow

Getting Publishing Rights to OSSRH

This part is fairly straightforward, since there are two possible starting points:

  1. Creating a new group ID
  2. Being granted publishing rights to an existing group ID

The entry point for both these starting points is via a JIRA ticket, created at OSSRH’s JIRA project. If you’re starting from scratch, all you have to do is to create a new JIRA account, and create a ticket to own your group ID.

Authenticating with OSSRH

Once you have publishing rights to your chosen group ID, head to the Nexus Repository Manager, and login with the same credentials that you used for OSSRH’s JIRA project.

Go to your profile by clicking on your name in the top right corner:

Now, toggle to the User Token interface:

Once you hit that, your token will be generated. Copy the username and password, or just leave the tab open until it’s time to insert these as GitHub Action Secrets.

The GPG Key

All artefacts published to OSSRH need to be signed by a key. That public key also needs to be published to a well known GPG key server.

If you don’t have a key, you can generate a new one easily by running the following:

$ gpg --gen-key

Then, list your key to ensure that it exists:

$ gpg --list-keys
pub   2048R/D378B393 2021-10-26 [expires: 2026-10-25]
uid                  Jude Pereira <***@***.***>
sub   2048R/A62C33C9 2021-10-26 [expires: 2026-10-25]

From the output above, the key ID is D378B393. Now, it’s time to publish it to a well known key server.

Initially, I tried to use MIT’s key server, however, it appeared to be unavailable (Maven produced an error when attempting to retrieve my key from there). I then decided to use OpenPGP’s key server, which worked.

$ gpg --export -a D378B393 | curl -T -
Key successfully uploaded. Proceed with verification here:

Replace D378B393 with your key ID in the command above. Once your key has been uploaded, a verification link will be printed, which isn’t really needed. All that verification does is makes your key searchable by your email address.

Creating the Necessary GitHub Action Secrets

At the end of this section, you should have the following keys populated:

Note: SONAR_TOKEN isn’t required.


This is the passphrase that you entered when creating your GPG key.


Since the server on which your GitHub Workflow runs is different from where the GPG key was created, it needs to be imported. For importing it, it needs to be accessible, and the best way to make it accessible is to use a secret:

$ gpg --export-secret-keys -a D378B393 | sed -z 's/\n/\\n/g'

This will output the private key in one line, with all new lines replaced with “\n” (if that sed command doesn’t work, simply remove it, copy and paste your key in a text editor, and replace all new lines with “\n” – it should be one line). Set this as the value for GPG_PRIVATE_KEY.


Remember the User Token section from the Nexus Repository Manager? Set this to the second value found there, after clicking on “Access User Token”.


Set this to the first value from the token details popup from above.

The GitHub Workflow

I’m assuming some familiarity with GitHub actions. If you’ve never used one, it’s a good idea to read this.

This part is composed of three parts (or just adapt this commit to your project):

  1. Updates to pom.xml
  2. Creating m2-settings.xml
  3. The GitHub action YAML

The following project structure is assumed:

/.github/workflows/release.yml    <-- New file
/release/m2-settings.xml          <-- New file

Updates to pom.xml

Add your repository and configure the publishing plugin:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns=""

Important: nexusUrl could be for your repository, if it was created after February 2021. See here for more details.

The GitHub Workflow

Create release.yml under your project’s .github/workflows/ directory with the following contents:

name: Publish to Maven Central

    types: [published]

    runs-on: ubuntu-latest
      - uses: actions/checkout@v2

      - name: Set up JDK 1.8
        uses: actions/setup-java@v1
          java-version: 1.8

      - name: Cache local Maven repository
        uses: actions/cache@v2
          path: ~/.m2/repository
          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
          restore-keys: |
            ${{ runner.os }}-maven-
      - id: install-secret-key
        name: Install GPG secret key
        run: |
          cat <(echo -e "${{ secrets.GPG_PRIVATE_KEY }}") | gpg --batch --import
      - name: Deploy
        run: |
          mvn -P gpg_verify \
            --no-transfer-progress \
            --batch-mode -Dgpg.passphrase=${{ secrets.GPG_PASSPHRASE }} \
            --file pom.xml -s release/m2-settings.xml verify deploy
          OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
          OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}

The Maven Settings File

Create m2-settings.xml under your project’s release/ directory with the following contents:


Phew! That was a long write (and read for you ;)).

Merge these changes into your mainline branch (typically master), bump up the version in pom.xml, and create a new GitHub release. If the workflow succeeds, you should have a new version publicly available within a few hours.



Leave a comment

Leave a Reply

%d bloggers like this: