Publishing Your Java Library to Maven Central Using GitHub?Actions
Motivation
Publishing your library to Maven Central allows you to easily share it with other users. This article provides an updated guide to publishing on Maven Central, replacing the outdated guide [Outdated]Open-Source Your Java Library by Publishing to Maven Central Using GitHub Actions , because Sonatype, the company behind Maven Central, has transitioned away from publishing via OSSRH.
Prerequisite
Account Setup
Your namespace corresponds to the groupId of a dependency in the pom.xml file. eg.
<dependency>
<groupId>io.github.innobridge</groupId>
<artifactId>security</artifactId>
<version>0.0.1</version>
</dependency>
If you own a web domain, you can use that as your groupId. If you do not have a domain, you can use your GitHub username. For example, my groupId is io.github.innobridge. Now, click on the "Add Namespace" button, enter your namespace, and click "Submit."
Next, you need to verify your namespace. Click on the “Verify Namespace” button. You will be presented with a verification key. Go to GitHub and create a repository with the same name as the verification key.
Now, click Confirmto complete the verification of your namespace.
Your namespace is now verified.
Repository Setup
In your project’s pom.xml you need to add the following configurations.
<!--Project Coordinates-->
<groupId><!--namespace--></groupId>
<artifactId><!--project name--></artifactId>
<version><!--version number--></version>
<name>${project.groupId}:${project.artifactId}</name>
<description><!--Description--></description>
<url><!--github repo url--></url>
<!--Licence-->
<licenses>
<license>
<name><!--name--></name>
<url><!--license url--></url>
</license>
</licenses>
<!--Developer Information-->
<developers>
<developer>
<id><!--developer id--></id>
<name><!--developer name--></name>
<email><!--developer email--></email>
<organization><!--organization name--></organization>
</developer>
</developers>
<!--SCM Information-->
<scm>
<connection>scm:git:git://github.com/<!--Github Profile-->/<!--Repo Name-->.git</connection>
<developerConnection>scm:git:ssh://github.com:<!--Github Profile-->/<!--Repo Name-->.git</developerConnection>
<url>https://github.com/<!--Github Profile-->/<!--Repo Name-->/tree/main</url>
</scm>
<!--Maven Plugin for Publishing-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source><!--java version--></source>
<target><!--java version--></target>
</configuration>
</plugin>
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.5.0</version>
<extensions>true</extensions>
<configuration>
<publishingServerId>central</publishingServerId>
<tokenAuth>true</tokenAuth>
</configuration>
</plugin>
<!-- GPG Signed Components -->
<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>
</execution>
</executions>
<configuration>
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.3.2</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</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>
</execution>
</executions>
<configuration>
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
Eg.
<groupId>io.github.innobridge</groupId>
<artifactId>security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>${project.groupId}:${project.artifactId}</name>
<description>Spring Security Library for OAuth2, JWT, and Refresh Token Handling</description>
<url>https://github.com/InnoBridge/InnoBridgeSecurity</url>
<licenses>
<license>
<name>InnoBridge License Version 1.0</name>
<url>https://github.com/InnoBridge/License/blob/main/InnoBridgeLicense</url>
</license>
</licenses>
<developers>
<developer>
<id>YilengYao</id>
<name>Yileng Yao</name>
<email>[email protected]</email>
<organization>InnoBridgeTechnology</organization>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/InnoBridge/InnoBridgeSecurity.git</connection>
<developerConnection>scm:git:ssh://github.com:InnoBridge/InnoBridgeSecurity.git</developerConnection>
<url>https://github.com/InnoBridge/InnoBridgeSecurity/tree/main</url>
</scm>
<!--Maven Plugin for Publishing-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source><!--java version--></source>
<target><!--java version--></target>
</configuration>
</plugin>
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.5.0</version>
<extensions>true</extensions>
<configuration>
<publishingServerId>central</publishingServerId>
<tokenAuth>true</tokenAuth>
</configuration>
</plugin>
<!-- GPG Signed Components -->
<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>
</execution>
</executions>
<configuration>
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.3.2</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</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>
</execution>
</executions>
<configuration>
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
Add the settings.xml to your github repository.
<?xml version="1.0" encoding="UTF-8"?>
<settings xsi:schemaLocation="https://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd"
xmlns="https://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance">
<servers>
<server>
<id>central</id>
<username>${env.MAVEN_USERNAME}</username>
<password>${env.MAVEN_PASSWORD}</password>
</server>
</servers>
<profiles>
<profile>
<id>central</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<gpg.executable>gpg</gpg.executable>
<gpg.passphrase>${env.MAVEN_GPG_PASSPHRASE}</gpg.passphrase>
</properties>
</profile>
</profiles>
</settings>
Generating Portal Token for Publishing
2. Press the Generate User Token button
3. Confirm the generation (note: this will invalidate an exiting token)
4. Save the generated credentials for use in your publishing setup
That will be your MAVEN_USERNAME and MAVEN_PASSWORD for deploying to Maven Central.
GPG Key Generation
GPG keys are essential for signing your artifact before publishing to Maven Central Repository, ensuring its authenticity and integrity.
Download and install GPG from https://gnupg.org/download/index.html#sec-1-2 ?.
Run the following command to generate your GPG keys, remember to use the email associated with your OSSRH account, and remember your passphrase.
gpg --gen-key
Then run the following commands to export GPG_PUBLIC_KEY and GPG_SECRET_KEY.
gpg --armor --export YOUR_EMAIL_USED_WHEN_GENERATING_THE_KEY > public-key.gpg
gpg --armor --export-secret-key YOUR_EMAIL_USED_WHEN_GENERATING_THE_KEY > secret-key.gpg
Your public GPG key will be the file public-key.gpg and secret GPG key will be the file secret-key.gpg
Distributing You Public?key
To distribute your public key to Maven Central so it can verify your files, follow these steps:
gpg --list-keys
The public key ID you need to distribute should be the one associated with your username and email in the UID.
gpg --keyserver keyserver.ubuntu.com --send-key <public key id>
Publish your Library with GitHub?Actions
name: Deploy to Maven Central
on:
workflow_dispatch:
inputs:
version:
description: 'Version to publish'
required: true
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Step 1 - Checkout code
uses: actions/checkout@v3
with:
ref: ${{ github.ref_name }} # Dynamically use branch name
- name: Step 2 - Import GPG Key
run: |
echo "${{ secrets.GPG_PUBLIC_KEY }}" | gpg --import
echo "${{ secrets.GPG_SECRET_KEY }}" | gpg --import --no-tty --batch --yes
env:
GPG_PUBLIC_KEY: ${{ secrets.GPG_PUBLIC_KEY }}
GPG_SECRET_KEY: ${{ secrets.GPG_SECRET_KEY }}
- name: Step 3 - Set up Maven Central Repository
uses: actions/setup-java@v3
with:
java-version: '22'
distribution: 'temurin'
server-id: central
server-username: MAVEN_USERNAME
server-password: MAVEN_PASSWORD
gpg-private-key: ${{ secrets.GPG_SECRET_KEY }}
gpg-passphrase: MAVEN_GPG_PASSPHRASE
- name: Step 3 - Publish Package to Maven Central
env:
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
run: mvn clean deploy -P release -DskipTests --batch-modeSetting Up Github Secrets
On your GitHub repository go to Settings then Secrets and variables?, then click Actions
Then click on New repository secret
Then fill the the secret for the secret keys, and click on add secrets
Triggering a Workflow to Publish your?Package
After merging in your changes, go to the Actions tab, then click on Deploy to Maven Central on the right sidebar. Click on the Run workflow dropdown, select the main branch, enter in your version number and click on Run workflow?.
The github action will deploy your package to Maven Central you can go to https://central.sonatype.com/publishing/deployments to check on your deployment
Your deployment will be in a pending state before your packages has been verified.
Once your packages as been verified and deployment validated you can click on the Publish button to publish your library.
After your package has been published, you can find your library on maven central.
You can now consume this dependency by adding the snippet
<dependency>
<groupId><!--namespace--></groupId>
<artifactId><!--project name--></artifactId>
<version><!--version number--></version>
</dependency>
to your pom.xml.
Developing Locally
Since Sonatype has migrated to Maven Central, they have discontinued the Staging repository and no longer support SNAPSHOT releases, creating the need for local development and testing before deployment.
To facilitate this, create a docker-compose.yml file that mounts the repository consuming your library and the?.m2 folder:
---
services:
consumer_application:
image: openjdk:22-slim
container_name: consumer-application
working_dir: /app
extra_hosts:
- "localhost:192.168.65.2"
ports:
- 8080:8080
- 5005:5005
env_file:
- .env
volumes:
- ${PWD}:/app
- /var/run/docker.sock:/var/run/docker.sock
- ./local/root:/root
- ~/.m2:/root/.m2
tty: true
Now, run the following command to build and start the container:
docker-compose build && docker-compose uo
In you library application append -SNAPSHOTto your version and run
./mvnw clean install
Next, in the pom.xml of your consumer application, add the dependency for your library:
<dependency>
<groupId><!--namespace--></groupId>
<artifactId><!--project name--></artifactId>
<version><!--version number--></version>
</dependency>
Now, SSH into the Docker container of your consumer application:
docker exec -it consumer-application sh
Finally, run the following command to start your consumer application:
./mvnw spring-boot:run
Application developer
1 周Hey, Thanks for this post. Used it to publish - https://github.com/Ganeshsivakumar/langchain-beam