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

  • Github Account
  • GitHub repository for a Java Maven project

Account Setup

  • Create an account on Maven Central.
  • Create a namespace: After registering your account, click on your profile in the top-right corner of the Maven Central portal. In the dropdown menu, select “View Namespaces” to begin setting up your namespace.Create a namespace

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

  1. Log in to the Central Publisher Portal and navigate to your account at https://central.sonatype.com/account

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:

  • Listing Your Public Key ID: Run the following command to list your keys:

gpg --list-keys        

The public key ID you need to distribute should be the one associated with your username and email in the UID.

  • To distribute your public key to Maven Central, use the following command to send it to a keyserver:

gpg --keyserver keyserver.ubuntu.com --send-key <public key id>        

Publish your Library with GitHub?Actions

  1. Create the folder?.github/workfows/ in your repository
  2. Add themaven-publish.yml configuration file with the content

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

  • MAVEN_USERNAME
  • MAVEN_PASSWORD
  • GPG_SECRET_KEY
  • GPG_PUBLIC_KEY
  • GPG_PASSPHRASE

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        


Ganesh Sivakumar

Integrating LLMs into ETL pipelines using langchian-beam

4 个月

Hey, Thanks for this post. Used it to publish - https://github.com/Ganeshsivakumar/langchain-beam

要查看或添加评论,请登录

Yi leng Yao的更多文章

  • Introduction to MCP in?Java

    Introduction to MCP in?Java

    Model Context Protocol (MCP) is a standard that allows a language models (LLMs) access to tools, resources, and…

  • Building AI Chat Agent with LLMTools and State?Machines

    Building AI Chat Agent with LLMTools and State?Machines

    Motivation I want to create an AI Chat Agent using LLMTools(LLM Function Calling in Java with LLMTools) and InnoBridge…

    2 条评论
  • Creating Distributed State Machines in Java

    Creating Distributed State Machines in Java

    Motivation I want a framework to model complex business processes by breaking them down into simpler steps. Creating my…

  • LLM Function Calling in Java with?LLMTools

    LLM Function Calling in Java with?LLMTools

    Motivation The flow of logic in traditional software is deterministic, meaning that the same input always produces the…

  • Setup Your Remote AI Server

    Setup Your Remote AI Server

    Motivation This is a followup article on How To Build Your Own AI Powered PC, once my PC has been build, I want to off…

  • How To Build You Own AI Powered?PC

    How To Build You Own AI Powered?PC

    Motivation I want to make API calls to LLM models without getting charge for heavy usage and run experiments on LLM…

  • Run LLMs Natively in your Java Application

    Run LLMs Natively in your Java Application

    Motivation When developing a Java application that utilizes Large Language Models (LLMs), relying on external API calls…

    2 条评论
  • Integrate Jupyter Notebook with AI for Free

    Integrate Jupyter Notebook with AI for Free

    Motivation A couple of months ago, I discovered Pretzel, a library that allows me to run code completion and…

  • Bootstrapping your Application with InnoBridge Security

    Bootstrapping your Application with InnoBridge Security

    Motivation InnoBridge Security simplifies the implementation of security in your application by reducing the need for…

  • Bootstrap Your Spring Boot Application With GitHub Actions

    Bootstrap Your Spring Boot Application With GitHub Actions

    Motivation Creating a Spring Boot process on GitHub is a tedious task. It involves the manual task of creating a GitHub…