Introduction

As a Java/JVM/Android developers we rely on the work of other people through frameworks and libraries. Many of them are open-source. Most of the developers are consumers of such projects. What if we would like to create our own library and distribute it to other developers? We can always create it and share a *.jar or *.aar file with others. Drawback of such solution is the fact that source of distribution may not be trusted. We also have problems with versioning. Consumers of the library have to constantly download and update their files. It’s much better to publish our library to Maven Central Repository and allow others to easily and seamlessly add it as an external dependency to pom.xml file (in case of Maven) or build.gradle file (in case of Gradle). In such case, dependency is managed by the appropriate build system and distributed via trusted source. This may be not easy for the first time that’s why I decided to collect information related to this topic in a single article. Let’s see how to do this.

Generating a GPG Key

Before we upload library, we need to generate GPG key.

In order to generate GPG key, we need to open the terminal and type the following command:

1
gpg2 --gen-key

Then, we should see the output:

1
2
3
...
gpg: key YOUR_KEY_ID marked as ultimately trusted
...

Read more at: https://alexcabal.com/creating-the-perfect-gpg-keypair

Distributing the public key

Next, we need to distribute public key. We can do that as follows:

1
gpg --keyserver hkp://pool.sks-keyservers.net --send-keys YOUR_KEY_ID

We can distribute our key to multiple servers to speed up the synchronization process (pgp.mit.edu, keyserver.ubuntu.com, etc.)

We can also list our keys as follows:

1
gpg2 --list-keys

To list secret keys, we can type:

1
gpg2 --list-secret-keys

Preparing the Gradle configuration

In my case, I used Gradle as a build system, which works well for Java, Kotlin and Android projects. We can use Maven for this purpose as well. As a reference, I used a Gradle script prepared by Chris Banes. You can have a look at it in one of my projects.

In my libraries, I usually have the following structure:

1
2
3
4
5
6
7
8
/
├── library
│ ├── gradle.properties
│ └── build.gradle

├── build.gradle
├── maven_push.gradle
└── gradle.properties

In maven_push.gradle I keep the mentioned release script. In the library/gradle.properties, I keep details about artifact released from a given directory:

1
2
3
POM_NAME=reactivenetwork
POM_ARTIFACT_ID=reactivenetwork-rx2
POM_PACKAGING=aar

It can be jar if you’re releasing pure Java library. Here’s an example for Android library.

In the /gradle.properties file, I keep release configuration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
VERSION_NAME=3.0.3
VERSION_CODE=34
GROUP=com.github.pwittchen

POM_DESCRIPTION=Android library listening network connection state and Internet connectivity with RxJava Observables
POM_URL=https://github.com/pwittchen/ReactiveNetwork
POM_SCM_URL=https://github.com/pwittchen/ReactiveNetwork
POM_SCM_CONNECTION=scm:git@github.com:pwittchen/ReactiveNetwork.git
POM_SCM_DEV_CONNECTION=scm:git@github.com:pwittchen/ReactiveNetwork.git
POM_LICENCE_NAME=The Apache Software License, Version 2.0
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=pwittchen
POM_DEVELOPER_NAME=Piotr Wittchen

org.gradle.daemon=true
org.gradle.jvmargs=-XX:MaxPermSize=1024m -XX:+CMSClassUnloadingEnabled -XX:+HeapDumpOnOutOfMemoryError -Xmx2048m

In the library/build.gradle file I need to include release Gradle script:

1
2
apply from: '../maven_push.gradle'
...

In the $HOME/.gradle/gradle.properties file, I keep system-wide release configuration for Sonatype:

1
2
3
4
5
6
signing.keyId=YOUR_KEY_ID
signing.password=YOUR_SIGNING_PASSWORD
signing.secretKeyRingFile=/home/piotr/.gnupg/secring.gpg

NEXUS_USERNAME=YOUR_NEXUS_USERNAME
NEXUS_PASSWORD=YOUR_NEXUS_PASSWORD

YOUR_NEXUS_USERNAME and YOUR_NEXU_PASSWORD can be defined during account creation on the http://oss.sonatype.org website.

Of course, you need to provide your own path to secretKeyRingFile, which was created during generating key.

If you’re interested in the complete project structure prepared for library release, you can have a look at the following examples:

Creating a Jira ticket for Sonatype

We should create a Sonatype Jira account and a new project ticket. You can have a look at my first issue. It took a bit longer in my case, because I needed to adjust package name.

To avoid my mistakes, have a look at the following guides:

Please note, this step is required for the first time only as well as generating keys. After that, we we’ll be able to release as many artifacts under the given package name as we want. These steps need to be repeated in the case of registering the new package name.

This is reasonable from the security and stability perspective because random people cannot just override widely used packages what might cause serious problems in many projects around the world.

Uploading an artifact

Once, we have everything set up, we can go to our project via terminal, and type:

1
./gradlew uploadArchives

and wait while artifacts are being uploaded.

Releasing an artifact

Next, we need to go to the http://oss.sonatype.org website, log in and on the left-hand side, click “Staging Repositories”. Then, we need to sort artifacts by date (Updated column), to view the recently updated items. We should find our artifact (it should be on the top) and click it. We can verify its contents to ensure that everything is ok (package name, version, etc.) and then, we should press “Release” on our artifact. Once release is done, we should press “Close” on the artifact.

Commenting the Jira ticket

When we are done with the previous steps, we should go back to our Jira ticket and provide the comment:

1
I have promoted my first release. Thanks.

Waiting for the Maven sync

After all of these steps, we need to wait for the acceptance from the people from Sonatype and Maven Sync. Maven Sync can take no longer than 48 hours. It’s usually faster, but it won’t happen immediately after releasing and closing an artifact like in the release of the Python packages.

Summary

We can see, that release process may be overwhelming and time consuming, but once we release the first artifact, we can skip most of these steps like generating keys and creating Jira ticket. We just need to have Gradle or Maven configuration, upload artifacts, release and close them via sonatype website and wait for the Maven Sync. Process of releasing new versions of the same artifact is the same as the first release (excluding mentioned first-time steps). During the next release, we simply need to bump library version in the Gradle configuration before uploading artifacts. In the future, I’m planning to write another article, which shows how to skip manual steps of going to Sonatype website and releasig artifacts via clicking on the page via Gradle plugins, so everything will be automated via CLI.