Automated Gradle Publish with GitHub Actions (To Maven Central)

Nikola Stanković
Viascom Publications

--

In my previous article, “Publish with Gradle to Maven Central", I explained how to publish artefacts to Maven Central manually. Now it’s time to show the automated way with GitHub Actions 🤩

If you need to remind yourself how to build the workflow, here you go with my workflow file (yaml):

https://gist.github.com/nik-sta/2ef8c34885ba4e7c120829a7113ca373

Idea

The idea behind our following automation is to get automatically triggered with GitHub Actions in the event of a release. And to build and publish our artefact with human approval on Maven Central.

Process of the automated publishing to Maven Central

Initially, I aimed for a fully automated pipeline to publish on Maven Central. During this practice, I learned that it comes with technical challenges and concerns about unwanted publications. The following points made my decision to aim for automation with human approval:

  • Technical challenges can be overcome, but there is a price to pay. To get it automatically published, you need to know the ID of the staging repository. Using custom code interacting with Sonatype’s REST API or a Gradle plugin would become necessary.
  • Technically, I would love to see it fully automated, but I recommend the approval workflow to mitigate security and compliance concerns. It brings the benefit of leaving the final decision to a human if an artefact will be stored on the internet for “eternity”.

Check out the “References, Useful links” section, and there, you will find a script and a Gradle plugin, which may help you if you decide differently than I did and head to a fully automated solution.

Preconditions

Before we can launch, we’ve to ensure our base is ready. And I assume that if you found this guide, you do

*I’m no advocate nor paid to list these links here. I don't even earn anything if you click on them. I googled them or had them in my bookmarks. So, it is truly here to assist you and have chosen by my personal bias as a developer. Feel free to get the required know-how anywhere else, as you desire — disclaimer end.

Let’s get started! 🥳

Implementation

The implementation will consist of three tasks to execute. Afterwards, we should successfully have implemented our fully automated Pipeline.

Our first target is to provide all the necessary data for our Gradle build. The following image should make you remember what you’ve defined and the mentioned needed data.

Extract from the build.gradle file regarding the publishing configuration

The only thing not visible above is that we also need the GPG private key and his passphrase for the signing task. If we build “real” non-SNAPSHOT versions.

1) GitHub secrets

You may remember my statement: we’re never allowed to store sensitive data, such as our credentials or private key, in Git? It remains still the case! Therefore, we have to make use of encrypted secrets. You will have to create the following secrets:

  • OSSRH_USERNAME
    Your username for the Sonatype Jira login.
  • OSSRH_PASSWORD
    Your password for the Sonatype Jira login.
  • OSSRH_GPG_SECRET_KEY_ID
    Your ID of the GPG certificate.
  • OSSRH_GPG_SECRET_KEY
    Your gpg private key base64 encoded.
  • OSSRH_GPG_SECRET_KEY_PASSWORD
    Your GPG passphrase, if you have set any. Otherwise, you don’t need to set this secret.

If you don’t know how to do that, visit the following step-by-step guide from GitHub:

Choose between storing on the repository or global level. And keep in mind that you can’t review a secret set in GitHub.

⚠️ GitHub will mask any detected secrets and not print them in the log. But don’t think the placed values are secret; it’s not! There are still ways to access this sensitive data. Therefore, read your logs and verify that you don’t expose any sensitive data.

Exporting the private key

It would help if you stored your private key as a secret on GitHub. You have to export it in a format suitable for that purpose. For our goal, this would be a base64 encoded string. To do so, execute the following command after you have replaced the string user@email.xyz with your corresponding e-mail address used for your GPG key.

gpg --output private.pgp --armor --export-secret-key user@email.xyz

2) Workflow file

I’ve created eight steps to automate our publishing process fully. First, please create the publish.yml file at the corresponding place in your Git repository.

// .github/workflows/publish.yml

Copy and paste the content of the following complete example into your newly created publish.yml file. Feel free to delete the comments as soon as you have read them and if they annoy you, too. Or maybe I only have issues with comments in my production files 😅.

Complete Example

publish.yml for automated Publishing to Sonatype’s Maven Central with GitHub Actions

3) GitHub releases

As the idea is first to create a release, we have to do so to trigger the pipeline. If you need help, visit the following documentation:

For development reasons, you can add the following configuration to enable manual workflow triggering.

To manually trigger a workflow, use the workflow_dispatch event. You can manually start a workflow using the GitHub API, GitHub CLI, or the browser interface. For more information, see "Manually running a workflow."

on: workflow_dispatch

By now, you are finally done with your whole automation! Congrats 🎉 Let’s continue with releasing it.

As an example, this is how it looked for my release:

Example of releasing a version on GitHub

4) Releasing to Maven Central

First of all, login into the Nexus Repository Manager:

Legacy: https://oss.sonatype.org/
New:
https://s01.oss.sonatype.org/

If you aren’t sure which one you are using, look into your build.gradle file and verify which releaseRepo you have specified in the publishing/repositories definition block.

You are four clicks away from publishing to Maven Central. What you have to do is verify what the GitHub Action Workflow has uploaded, and if you agree, then to

  • close the staging repository
  • release the staging repository

Following is how this looked for my recent release as a reference:

Closing staging repository
Confirming closing the staging repository
Releasing the staging repository
Confirming to release of the staging repository

To understand what’s happening under the hood, look at my previous guide and read the section “3 Publishing to Maven Central.”

That’s it; we’re done 😎

Details, Explanations, Further Topics, Common Errors

What is an armoured GPG key export?

PGP (including GPG) ‘armouring’ is not encryption. Encryption prevents the unauthorised use of data (formally, it provides confidentiality) by making it unreadable in a way that can only be reversed by someone with the secret key. Armouring is a simple process (mostly base64, although not the same base64 used by uuencode) that can be easily reversed by anybody who reads the specification. Armouring looks like text, while unarmored (binary) data looks like garbage to a person who uses inappropriate tools like cat or a text editor, but they are equally readable by someone competent.

Copied from: https://unix.stackexchange.com/questions/623375/what-is-the-armored-option-for-in-gnupg

Sonatype’s Nexus Login issues

First of all, it happens to me too often that I open the Legacy Sonatype instead of the new one. You don’t see a difference on the web app itself. Only the URL differs. So, double-check if you are on the right site when you receive an invalid username/password message.

To reset the password, you must open Sonatype’s Issue Tracker = Jira if you have lost or forgotten your password. It’s not apparent, but actually, how it works. Unfortunately, there is no option on the Nexus itself to reset.

Here is the direct link to request a password reset: https://issues.sonatype.org/secure/ForgotLoginDetails.jspa

References, Useful links

Feedback and updates matter 📝☕. Enjoy my articles? Show support with claps, follows, and coffee donations. I keep all content regularly updated.

Support: ko-fi.com/niksta | Discord: devhotel.io

Disclosure: This article was assisted by ChatGPT (OpenAI) and refined using Grammarly for spelling and style. Visuals created with Midjourney’s AI tool.

--

--

Nikola Stanković
Viascom Publications

Discover the path to production-grade coding of containerized Spring Boot applications with Kotlin, focusing on cloud integration and application security.