Publish a Kotlin/Multiplatform library on Maven Central

Romain Boisselle
Kodein Koders
Published in
8 min readMar 9, 2021

In our last tutorial, Salomon showed you how to create a Kotlin Multiplatform Mobile library. Now that we have some code ready to be used by the community, let me show you how to publish your future releases to Maven Central.

Before going into details about how to deploy your libs to Maven Central, I want to adress library developers who already have a library published on JCenter.

This tutorial is also for you!

Publishing on Bintray and JCenter was a choice most of us made, because it had less constraints and more flexibility. Now that they will be shutting down quite soon, we must migrate our publication workflow. Maven Central seems to be the right choice these days, but we need to deal with new requirements.

Where Bintray was giving us some latitude on what we could upload and publish, Maven Central has always been more restrictive. Indeed, every uploaded artifact needs to be accompanied at least by the following items:

  • A bundle of the sources of the artifact
  • The documentation that goes with the artifact, in the JVM world this is the Javadoc.
  • A well formatted POM file. For those who are not familiar with Maven, this is the description of your project, with its name, its version, its dependencies, etc, in an XML format
  • And finally, every item needs to be signed to be accepted.

Actually, these requirements are not that hard to fulfill, and Gradle provides almost everything we need to publish our artifacts smoothly, with 2 official plugins, maven-publish and signing.

But… I told you : almost!

When performing a publication, before being available on Maven Central, all your uploaded artifacts with maven-publish are generally stored and referenced in one staging repository that you need to validate as the final step of publishing. While this is true for standard JVM projects, this is not the case for Kotlin/Multiplatform ones.

As Kotlin/Multiplatform developers, you may know that you need two machines to build your project if you are targeting Windows and at least one other native platform. By doing so, each build will invoke the publish task that will implicitly create at least a different staging repository by platform. Why “at least’’? Because, while configuring our open source Kodein-Framework projects that often implies multiple modules or submodules, we found that a unique invocation of the plugin maven-publish, on a unique machine, ended up with a few 10ish implicit staging repositories created. That can become really cumbersome when managing multiple libraries.

This is why we need to control our publication workflow, by being able to publish one release from multiple builds on a single staging repository. We will see how in a minute, right after checking on some requirements.

Before diving into the configuration of your project, you will need to fulfil some requirements.

I won’t go into every detail, but I will give you some hints of what you need to publish on Maven Central.

If you already have a sonatype account, a verified repository and a GPG key pair, you can just move on to the next chapter.

As I just said, in regards to being able to publish your project to Maven Central, you will need to create a Jira account on sonatype and give them the possibility to verify the ownership of the group id you want to publish on. So, go to https://issues.sonatype.org and create your account.

After that, you should be able to open an issue, select "New Project" as the Issue Type, and let sonatype verify the ownership of your group id.

In my use case, I wanted the ownership of our group id org.kodein, where we will be publishing all the future versions of the Kodein-Framework.

After that, Sonatype will ask you to validate your ownership, either by adding a DNS record to your domain, or by creating a specific repository named after the ticket number if you own a Github organisation. Your repository should be ready to store and expose your artifacts within a few minutes. Verification is usually done automatically, but sometimes is manual and longer for security reasons.

Now you can upload whatever you want to your sonatype repository. But, if you want your artifacts to be available on Maven Central, they must pass through a validation process, which will check if these artifacts are correctly signed. In order to do so, you need to generate a GPG key pair.

While there are plenty of ways to generate your GPG keys, here is the quickest one. Note that on MacOS I use GPG Suite to handle my keys.

To create a new GPG key pair, run the command:

With this, you just have to enter your name and email, a strong passphrase, and you’ll end up with a RSA key pair.

You can list your keys with the command:

And, later on we will need to export our private key as “armored”, with the command:

This will print your GPG private key in an ASCII format, surrounded with these key block delimiters.

I must warn you! You should never share your private key with anyone, under no circumstances. You should keep it somewhere safe, and do not lose it!

Now comes the fun part, configuring your Kotlin/Multiplatform project with vanilla Gradle. Let’s start by adding the necessary plugins to your configuration. Along with the Kotlin Multiplatform plugin we will apply the maven-publish and signing plugins.

The maven-publish plugin will help us fulfill the sonatype publication requirements. Pointing the configuration to the sonatype OSS repository is quite simple. We can even put a condition onto the published version of our library, to upload them directly on the SNAPSHOT repository, which allows us to upload anything on your group ID, as long as its version ends with the uppercased word SNAPSHOT.

As I said earlier, we prefer to upload our releases onto a unique staging repository to maximize our control over the publication workflow. Thus, we need to replace the release repo URL deploy/maven2 deploy/maven2 by deployByRepositoryId/$repositoryId, where the variable repositoryId can be set upward into Gradle properties, or via environment variables like in the following snippet:

Note that, while doing so fixes the multiple repositories issue, this makes the deployment dependent on a specific environment variable.

What remains is to set up the credentials of your sonatype account, obviously not with hardcoded values, but as external resources. So here is a full snippet of the maven-publish plugin configuration:

Before singing every output item of our Kotlin/Multiplatform build, we need to add some extra configuration, so that the sonatype validation check passes. As I already said, Maven Central requirements are restrictive. In fact, along with our artifacts we also need to upload the related sources, docs and a POM file with a defined set of metadatas.

Sources are already packaged by maven-publish, but we will have to add the docs and the POM file. Here is an example of how to generate a POM file with the publication configuration:

This means that for each Maven Publication task of our Kotlin/Multiplatform project, thus accompanying each target’s artifact, we will add a POM file with the required metadatas.

To provide some documentation to go with your artifacts, you can choose the easy way, like most of the libraries published on Maven Central, and “cheat” by adding an empty javadoc, with the following configuration:

Or, you can configure Dokka, the documentation engine for Kotlin developed by JetBrains, and recently enabled for Kotlin/Multiplatform. Start by adding the dokka plugin to your configuration:

And now add the following snippet to your gradle configuration script:

This defines where the dokka task should put the generated documentation, and provides it to the javadocJar task, that will bundle it with a name like `artifact-javadoc.jar`. The delete Dokka Output Directory task will ensure that the documentation is up to date, by removing the old ones before regenerating it.

Now, like for the empty javadoc, just add the javadocJar task as an artifact of the MavenPublication tasks.

We are almost there, ready to publish to Maven Central! The final rule that we need to validate is that every uploaded item needs to be signed. This is really trivial once you have your GPG key pair generated. You need to export your private key in an ASCII armored format with the following command:

Save that output, and your GPG password, wherever it suits for your build process. In our case we put it in our Github’s secrets, being able to set them as environment variables in our Github Actions workflow.

So signing is now as simple as adding a few lines of gradle configuration:

This defines how to retrieve the signatory configuration, by reading environment variables, and what should be signed. Here, we will be signing every output of all the publications tasks. You can now test your configuration locally by running the task `publishToMavenLocal`, and do not forget to set your environment variables.

Here is an example of the output of a publication for one target of the Kodein-DI library, with the kilb artifact, the sources, the javadoc, the pom file, and everyone of them is signed:

You can now run the publish task on multiple machines to upload your artifacts, depending on your Kotlin/Multiplatform targets.

But, we are not done here. The next step would be to set up a CI/CD workflow to automate everything. We will cover that in a future tutorial, to see how to automate the publication of a Kotlin/Multiplatform project with Github Actions.

I really hope that you have learned something, and that this will be useful for you to share to the community your Kotlin/Multiplatform libraries on Maven Central.

Here is a playground project that contains everything you need to release your libraries on Maven Central. Link is also in the description.

https://github.com/KodeinKoders/playground-publish-kmp-to-mavencentral/

See you soon for more tutorials here or on our KodeinKoders Youtube channel.

This article is brought to you by Kodein Koders, a European service & training provider focused on Kotlin & Kotlin/Multiplatform.

Thanks to Louis CAD for reviewing this article.

--

--