Publish Maven Repository (Android Library) with Sonatype Nexus

Karthik Rathinavel
6 min readFeb 2, 2023

--

Photo by Susan Q Yin on Unsplash

Hello guys. In this post, I am going to explain ‘How to publish a Maven Repository with Sonatype Nexus — step by step’.

Here, I will create a sample android library for showing the complete picture from start to finish. After created the library, we will move on to the Sonatype Account creation, Sonatype project creation, Key generation, Android project setup for publishing and finally publish the library.

Create Android Library

  • Create a new Android project.
  • Create a new ‘Android Library’ by click on File -> New Module and then select ‘Android Library’.
  • Change the name of the library and package name if you want and then click on ‘Finish’.

I created a library with name = mylibrary, package name of ‘com.example.mylibrary’.

  • Inside ‘com.example.mylibrary’, create your library files.

I created a class ‘Greet.java’ with a method (‘greet’) [This method will be used for checking our library after published]:

package com.example.mylibrary;

public class Greet{
public void greet(String name){
System.out.println("Hi, " + name);
}
}

Then, we want to ensure our library is working fine locally.

  • Implement the library ‘mylibrary’ into your app build.gradle to test whether the library works or not.
implementation project(':mylibrary')
  • In MainActivity.java, try to call the ‘greet’ method of ‘mylibrary’. If it works fine, we can move on to the ‘Sonatype JIRA’ account setup.

Setup Sonatype Jira Account

  • Go to https://issues.sonatype.org
  • Create an account, if you don’t have one.
  • Login to your account.
  • Create a new Jira Ticket with project (Community Support — Open-Source Project Repository Hosting (OSSRH)) and Issue Type (New Project)
  • In the next screen, Enter Summary, Description, Group Id, Project URL (GitHub URL), SCM URL (GitHub URL) and set ‘No’ to ‘Already Synced to Central’ and then submit the request.
  • Create a new empty repository in GitHub for verification (repository name must be match with name suggested by sonatype). For me, the name of the repository is ‘OSSRH-88179’. Check your comments.
Sonatype Bot Central-OSSRH Comment
  • Then the created issue is in the state of ‘waiting for response’ or ‘opened’ or something like that. If the repository is approved, the status will change to ‘closed’. The process of approving the issue may take 1–2 days.

Access Nexus Repository ‘profileId’

  • After receiving the approval, Go to https://s01.oss.sonatype.org/ and login with your credentials (same as Jira credentials)
  • Click on ‘Staging Profiles’ in the left panel and then click on the profile with your package name, mode, repo target, release repository…
  • And then the URL of the page will change something like this: https://s01.oss.sonatype.org/#stagingProfiles;<sonatypeStagingProfileId>. Note down this <sonatypeStagingProfileId>, it will be useful in grade script later.

Key Generation

  • Download ‘GnuPg’ software from GnuPG — Download
  • Install the software like any other software.
  • Set ‘Environment Variables’ for this software (up to \bin).
  • Then open ‘Command Prompt’ and navigate to your library folder (cd Project, cd mylibrary), type the following command:
gpg --full-gen-key

This will ask you some series of values. Give the values as it is, What I mentioned below:

gpg (GnuPG) 2.4.0; Copyright (C) 2021 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extend permitted by law.

Please select what kind of key you want:
(1) RSA and RSA
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
(9) ECC (sign and encrypt) *default*
(10) ECC (sign only)
(14) Existing key from card
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: YOUR NAME
Email address: youremail@youremail.com
Comment: Your Comment
You selected this USER-ID:
"YOUR NAME (Your Comment) <youremail@youremail.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
We need to generate lot of random bytes. It is 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 entrophy.
gpg: revocation certificate stored as 'C:\\Users\\USERNAME\\AppData\\Roaming\\gnupg\\openpgp-revocs.d\\1234567890.rev'
public and secret key created and signed.

pub rsa 2023-02-02 [SC]
1234567890
uid YOUR NAME (Your Comment) <youremail@youremail.com>
sub rsa4096 2023-02-02 [E]

Here, 1234567890 is the sample key. You received much longer key.

Upload the key to a Key Server

  • Run the following command:
gpg --keyserver hkp://keys.gnupg.net --send-keys 34567890

Here, 34567890 = Last 8 digit of the public key (keyId), generated in the previous step.

Generate the Secret Ring Key file.

  • Run the following command:
gpg --export-secret-keys 34567890 > SecretRingKey.gpg
  • Make sure to generate the secret key file inside ‘mylibrary’ directory.

Project Setup for Publishing

  • Create a new repository in your GitHub account. The repository we created previously at the Jira account is only for verification. You can delete that repository now. Create a fresh new repository.
  • Add the nexus publish plugin and signing credentials in root project build.gradle:
buildscript {
dependencies {
classpath 'io.github.gradle-nexus:publish-plugin:1.1.0'
}
}
apply plugin: 'io.github.gradle-nexus.publish-plugin'
ext["signing.keyId"] = '34567890'
ext["signing.password"] = 'ENTERPASSPHRASEOFGNUPGHERE'
ext["signing.secretKeyRingFile"] = 'SecretRingKey.gpg'
  • In the project level directory, create a new directory ‘scripts’ with two files: publish-root.gradle and publish-module.gradle
  • In publish-root.gradle, add the following code:
// Create variables with empty default values
ext["signing.keyId"] = ''
ext["signing.password"] = ''
ext["signing.secretKeyRingFile"] = ''
ext["ossrhUsername"] = ''
ext["ossrhPassword"] = ''
ext["sonatypeStagingProfileId"] = ''

File secretPropsFile = project.rootProject.file('local.properties')
if (secretPropsFile.exists()) {
// Read local.properties file first if it exists
Properties p = new Properties()
new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) }
p.each { name, value -> ext[name] = value }
} else {
// Use system environment variables
ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME')
ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD')
ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID')
ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID')
ext["signing.password"] = System.getenv('SIGNING_PASSWORD')
ext["signing.secretKeyRingFile"] = System.getenv('SIGNING_SECRET_KEY_RING_FILE')
}

// Set up Sonatype repository
nexusPublishing {
repositories {
sonatype {
stagingProfileId = sonatypeStagingProfileId
username = ossrhUsername
password = ossrhPassword
nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
}
}
}
  • In publish-module.gradle, add the following code:
apply plugin: 'maven-publish'
apply plugin: 'signing'

task androidSourcesJar(type: Jar) {
archiveClassifier.set('sources')
if (project.plugins.findPlugin("com.android.library")) {
from android.sourceSets.main.java.srcDirs
} else {
from sourceSets.main.java.srcDirs
}
}

artifacts {
archives androidSourcesJar
}

group = PUBLISH_GROUP_ID
version = PUBLISH_VERSION

afterEvaluate {
publishing {
publications {
release(MavenPublication) {
// The coordinates of the library, being set from variables that
// we'll set up later
groupId PUBLISH_GROUP_ID
artifactId PUBLISH_ARTIFACT_ID
version PUBLISH_VERSION

// Two artifacts, the `aar` (or `jar`) and the sources
if (project.plugins.findPlugin("com.android.library")) {
from components.release
} else {
artifact("$buildDir/libs/${project.getName()}-${version}.jar")
}

artifact androidSourcesJar

// Mostly self-explanatory metadata
pom {
name = PUBLISH_ARTIFACT_ID
description = PUBLISH_DESCRIPTION
url = PUBLISH_URL
licenses {
license {
name = PUBLISH_LICENSE_NAME
url = PUBLISH_LICENSE_URL
}
}
developers {
developer {
id = PUBLISH_DEVELOPER_ID
name = PUBLISH_DEVELOPER_NAME
email = PUBLISH_DEVELOPER_EMAIL
}
}
scm {
connection = PUBLISH_SCM_CONNECTION
developerConnection = PUBLISH_SCM_DEVELOPER_CONNECTION
url = PUBLISH_SCM_URL
}
}
}
}
}
}

ext["signing.keyId"] = rootProject.ext["signing.keyId"]
ext["signing.password"] = rootProject.ext["signing.password"]
ext["signing.secretKeyRingFile"] = rootProject.ext["signing.secretKeyRingFile"]

signing {
sign publishing.publications
}
  • In the library build.gradle (mylibrary -> build.gradle), add the following code with your credentials and GitHub URLs:
ext {
PUBLISH_GROUP_ID = 'io.github.yourProfile'
PUBLISH_VERSION = '1.0.0'
PUBLISH_ARTIFACT_ID = 'libraryName'
PUBLISH_DESCRIPTION = 'mylibrary Android SDK'
PUBLISH_URL = 'https://github.com/username/libraryName'
PUBLISH_LICENSE_NAME = 'Apache License'
PUBLISH_LICENSE_URL =
'https://github.com/username/libraryName/blob/master/LICENSE'
PUBLISH_DEVELOPER_ID = 'userName'
PUBLISH_DEVELOPER_NAME = 'YOUR NAME'
PUBLISH_DEVELOPER_EMAIL = 'email@email.com'
PUBLISH_SCM_CONNECTION =
'scm:git:github.com/username/libraryName.git'
PUBLISH_SCM_DEVELOPER_CONNECTION =
'scm:git:ssh://github.com/userName/libraryName.git'
PUBLISH_SCM_URL =
'https://github.com/username/libraryName/tree/master'
}

apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"
  • Setup sonatype credentials in local.properties (Add the sonatype account credentials as values of each property here)
ossrhUsername=<SonatypeAccountName>
ossrhPassword=<SonatypeAccountPassword>
sonatypeStagingProfileId=<SonatypeStagingProfileId>

Upload to Staging Repositories

  • Before running the publish command, you need to push the project into git repository you created earlier.
  • Run the following command to publish release publication (Make sure to run this command from project root directory):
gradlew mylibrary:publishReleasePublicationToMavenRepository
  • After successful execution (BUILD SUCCESSFUL) of the above command, you can now see a repository in ‘Staging Repositories’ section of https://s01.oss.sonatype.org/

Release the Library

  • Go to https://s01.oss.sonatype.org/ and login with your credentials.
  • Select the repository you want to release in ‘Staging Repositories’.
  • Click on ‘close’ and give a description and then ‘submit’.
  • Check the closing activity completed, click on ‘release’ and give a description.
  • Thats all! Our library (repository) is published!! Now we can find our library by ‘Artifact search.’

Implement the published library into a Test Project

  • Create a Test Project
  • Add the following code in the app build.gradle (change the groupId, artifactId and version, according to yours)
implementation "io.github.karthikrathinavel:mylibrary:1.0.0"
//implementation "groupId:artifactId:version"

THANK YOU

--

--

Responses (3)