Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 21, 2021 02:29 pm GMT

Publishing Android libraries to MavenCentral in 2021

Introduction

This is an updated version of an article published two years ago on blog.autsoft.hu. This new version supports non-Android libraries, uses command line GPG handling instead of a specific Windows GUI app, includes new best practices, and uses GitHub Actions for its CI integration.

Last Update, April 2021: addressed new Sonatype infra, moved to using AGP Maven Publish plugin integration for release components, and to using the new gradle-nexus/publish-plugin.

Creating a great library is hard work. Coming up with the idea, implementing it, making sure you have a nice, stable public API that you control carefully and maintain Thats already lots to do.

After all that, you need to make your library available to the public. Technically, you could distribute the .aar file any way you want, but the norm is publishing it to a publicly available Maven repository. Its a good idea to use one of the well-established repositories that people are already likely to have in their projects, to make getting started with your library as easy as possible.

The simplest choice would be JitPack, which might not give you much in terms of customization or control, but is very easy to get started with. All you have to do is publish your project on GitHub, and JitPack should be able to build and distribute it immediately. If youre new to libraries, this is a great choice for getting your code out there.

The next step up is Jcenter Which no longer accepts new packages and will stop serving them in February 2022. Using it had its issues anyway.

Finally, the fanciest place you can be in is The Central Repository via Sonatype OSSRH (OSS Repository Hosting), which Ill refer to as simply MavenCentral from here on. This is the place to be if youre a Maven dependency. Artifacts on MavenCentral are well trusted, and their integrity can be verified, as they are all required to be signed by the author.

The publication process, however, and especially automating it, can be quite a headache. Its easy to get stuck at many of the various steps no matter what tutorials youre following, especially if theyre out of date, and this can get demotivating very quickly. Its not uncommon to give up and just use Bintray/Jcenter instead which is not an option anymore.

So, if you feel up for a bit of a challenge, and want to do things the right way, heres how you can get a library into MavenCentral, in 2021.

Overview

Here's a quick overview of the steps we'll go through:

  1. Registering a Jira account with Sonatype, and verifying your ownership of the group ID you want to publish your artifact with
  2. Generating a GPG key pair for signing your artifacts, publishing your public key, and exporting your private key
  3. Setting up Gradle tasks that can sign upload your artifacts to a staging repository
  4. Manually going through the process of checking your artifacts in the staging repo and releasing them via the Sonatype web UI
  5. Automating the close & release flow with a Gradle plugin
  6. Configuring CI workflows with GitHub Actions to automate all of the above

A lot of ground to cover - lets go!

Prerequisites

Well be using the following tools for this tutorial. You are free to use alternatives, but these are our favourites, and they work well for us.

  • The command line gpg tool
    • GPG Suite for macOS or Gpg4win for Windows are great choices, which also come with helpful GUIs. Many alternatives for different platforms are available here.
  • GitHub as the public host of the librarys repository
  • GitHub Actions as the continuous integration solution

For this article, well assume that you already have your library developed, and have uploaded it to a public GitHub repository.

Well use our very own Android Chat SDK in our examples. This SDK is made up of multiple artifacts, but for simplification, well just talk about publishing the low-level networking client, which lives in the stream-chat-android-client module of the GitHub repository.

Shameless plug: if you need a chat solution in your Android app, check out our documentation and our Android Chat tutorial.

Registering a Sonatype account

First things first, youll need an account in the Sonatype Jira. Head over there and hit Sign up. Registration is straightforward, it just requires a username, an email, and a password.

The login page to Sonatype Jira

After youve logged in, youll need to open an issue, asking for access to the group ID that youll want to publish your project under. For us, based on our domain name (getstream.io), our group ID is io.getstream. If you own a domain, its best to choose the reversed version of that as your group ID. Otherwise, youll have to stick with having a GitHub-based group ID (see Choosing your Coordinates for more details).

After choosing a language and an avatar, youll end up on this landing page - click on Create an issue:

Sonatype Jira landing page

Select Community Support - Open Source Project Repository Hosting and then New Project:

Creating a new issue in Jira

On the next page, fill out the following fields:

  • Summary: Create repository for your.group.id.here
  • Description: An optional, quick summary of what your project is.
  • Group Id: Your group ID, as described a few sections earlier.
  • Project URL: If your project has a webpage, the URL of that page. This can also be just the GitHub repository.
  • SCM url: Your source control URL, i.e.the GitHub repository link.
  • Username(s): If you want additional users (on top of the one youre using for this process) to have deploy access for your group ID, you can list them here.
  • Already Synced to Central: If youre just getting started, this should be No.

The filled out issue details page for a new repository

Soon after opening it, your issue will get a comment telling you to verify that you own the domain corresponding to your group ID:

![Comment requesting domain verification](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/da8ea3aa9e7861342114d89a78f7e7ef/please_verify_2-0-00-00-00.png?auto=compress%2Cformat&ixlib=php-3.3.0](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hgsfecmkirg0f8bmf18g.PNG)

To comply with this, add the required TXT record to your domain - how to do this will depend on where your domain is registered, but it should be a fairly simple task.

@    TXT    1800    OSSRH-12345

Make sure to replace the ticket number above with your actual number.

When done, dont forget to leave a comment on the issue so that Sonatype knows to check the record. Youll eventually get a response telling you that you now have deploy rights - congrats!

![The Sonatype comment confirming domain verification]](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/niwgnp6n317gkapz14cb.PNG)

Generating a GPG key pair

As we eluded to earlier, artifacts published on MavenCentral have to be signed by their publishers. Youll need a GPG key for this.

MavenCentral also has its own documentation for Working with PGP Signatures which you can reference if you get stuck along the way.

This part requires access to the gpg command. There are several ways to install this via package managers, and there are many distributions available for different platforms on gnupg.org.

GPG Suite is an easy-to-use distribution for macOS, and you can use Gpg4win if youre on Windows. Both of these come with GUI tools that make managing keys easier.

We'll stick to the command line here, but note that the old article shows how to use the Kleopatra GUI (included in Gpg4win) on Windows for generating and managing keys.

To generate a new key, run the following command:

gpg --full-gen-key

Youll be prompted to enter a few details:

  • Kind of key: Accept the default value, which is (1) RSA and RSA.
  • Key size: 4096.
  • Expiration: You can input 0 to generate a key that never expires. You can also create a key that has an expiry date and then renew it periodically, if you prefer to do so.
  • Real name, email: Should be obvious.
  • Comment: Freeform text, can be left empty.

After entering these details, youll be prompted to enter a passphrase to secure your key with.

Heres the full flow you'll go through, with a bit of truncation:

Please select what kind of key you want:   (1) RSA and RSA (default)   ...Your selection? 1RSA keys may be between 1024 and 4096 bits long.What keysize do you want? (3072) 4096Requested keysize is 4096 bitsPlease specify how long the key should be valid.         0 = key does not expire         ...Key is valid for? (0) 0Key does not expire at allIs this correct? (y/N) yGnuPG needs to construct a user ID to identify your key.Real name: Marton BraunEmail address: [email protected]: Example key for tutorialYou selected this USER-ID:    "Marton Braun (Example key for tutorial) <[email protected]>"Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? Ogpg: key 36271B955BEF072A marked as ultimately trustedgpg: revocation certificate stored as '.../gnupg/openpgp-revocs.d\7A5D73CFEDDDBC915986998A36271B955BEF072A.rev'public and secret key created and signed.pub   rsa4096 2021-02-03 [SC]      7A5D73CFEDDDBC915986998A36271B955BEF072Auid                      Marton Braun (Example key for tutorial) <[email protected]>sub   rsa4096 2021-02-03 [E]

You can always check the keys you have on your system by running gpg --list-keys:

gpg: checking the trustdb.../gnupg/pubring.kbx-----------------------------------------------pub   rsa4096 2021-02-03 [SC]      7A5D73CFEDDDBC915986998A36271B955BEF072Auid           [ultimate] Marton Braun (Example key for tutorial) <[email protected]>sub   rsa4096 2021-02-03 [E]

Your keys ID is the last eight digits of its fingerprint (the long hexadecimal string above). In this case, this is 5BEF072A - take note of this, as youll use this later.

Youve generated a pair of keys - a private and a public key. Youll keep the private one hidden and use it to sign your artifacts. The public key has to be uploaded to the server so that anyone can check that it belongs to you, which you can do by running the following (use your own key ID!):

gpg --keyserver hkp://pool.sks-keyservers.net --send-keys 5BEF072A

Your private key will need to be referenced by your project when it signs the artifacts. You can export it into a file by running the following:

gpg --export-secret-keys 5BEF072A > 5BEF072A.gpg

Enter your passphrase youve set earlier when prompted.

Setting up publication in your project

Thats a lot of work without touching your project, but the time has come to do that now. In the next few steps, you will:

  1. Add Gradle scripts that set up the publication plugin required to push artifacts to a repository.
  2. Configure the properties of the library youre releasing.
  3. Grab the necessary authentication details along with the private key youve just exported.

Root project Gradle configuration

To easily automate publishing later, you'll use the gradle-nexus/publish-plugin tool. This has to be added in your project level (root) build.gradle file as a dependency.

You can do this either with a plugins block:

plugins {    id("io.github.gradle-nexus.publish-plugin") version "1.1.0"}

Or with the classic buildscript dependency syntax, and then applying it in your project:

buildscript {    dependencies {        classpath 'io.github.gradle-nexus:publish-plugin:1.1.0'    }}apply plugin: 'io.github.gradle-nexus.publish-plugin'

Check the releases page of the plugin to get the latest version.

Next, create a new file called publish-root.gradle in a new scripts folder inside your project. This will contain global configuration you need for publishing, grabbing input values for your scripts, and defining the MavenCentral repository.

Note that here well go through the contents of these scripts part by part, with explanations. You can always find the complete, up-to-date files we use in production in the Stream Chat repository.

In this file, you'll grab some configuration parameters. Using the script below, youll first set all the variables to a dummy empty string. This will let the project sync and build without the publication values set up, which would otherwise be an issue for your contributors.

// Create variables with empty default valuesext["signing.keyId"] = ''ext["signing.password"] = ''ext["signing.secretKeyRingFile"] = ''ext["ossrhUsername"] = ''ext["ossrhPassword"] = ''ext["sonatypeStagingProfileId"] = ''

The first three variables will be used to sign the artifacts after theyre built:

  • signing.keyId: the ID of the GPG key pair, the last eight characters of its fingerprint
  • signing.password: the passphrase of the key pair
  • signing.secretKeyRingFile: the location of the exported private key on disk

osshrUsername and ossrhPassword: are your account details for MavenCentral, which youve chosen at the Jira registration step. Well get back to where sonatypeStagingProfileId comes from later on.

Next, youll try to fetch the values of the variables from a local.properties file in the root of the project if it exists, otherwise youll look for them in the environment variables. The former lets you easily input these values locally on your machine, while the latter will help with setting up CI.

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')}

Make sure that youve set these variables either in the aforementioned local.properties file or in your environment variables. If you want to use the property file, the syntax for it should look something like this (replace all the data here with your own!):

signing.keyId=5BEF072Asigning.password=signingPass123signing.secretKeyRingFile=~/gpg-keys/5BEF072A.gpgossrhUsername=yourSonatypeUserossrhPassword=yourSonatypePassword

The last piece of code to add to this file will define the MavenCentral (Sonatype) repository where publishing should upload the artifacts. This relies on parameters fetched above to authenticate:

// Set up Sonatype repositorynexusPublishing {    repositories {        sonatype {            stagingProfileId = sonatypeStagingProfileId            username = ossrhUsername            password = ossrhPassword        }    }}

Important: If you're on the new Sonatype infrastructure (happens if you've registered after 2021-02-24 or requested it specifically), you have to add explicit URLs pointing to s01.oss.sonatype.org in this config block next to the existing parameters, like so:

nexusPublishing {    repositories {        sonatype {            /* Existing params here... */            nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))            snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))        }    }}

As the last step, apply this script in your root build.gradle file like so:

apply from: "${rootDir}/scripts/publish-root.gradle"

This completes the configuration needed in the root project. This gist contains the code blocks detailed above in a single snippet. The final, up-to-date script we use for publishing our SDK can be found in the Stream Chat repository.

Per-module Gradle setup

With the repository defined and parameters fetched, it's time to create a script that will define the artifacts in each module you want to publish. Create a new file called publish-module.gradle in the scripts folder. As its name suggests, this will be applied to each module that you want to publish an artifact from.

In this file, first youll apply two plugins for publishing and signing.

apply plugin: 'maven-publish'apply plugin: 'signing'

Our publishing file also applies the Dokka plugin in this block, which you have to configure separately, if you want to use it.

Then you declare the sources artifact for the library. This is really important - it will make sure that the source files are packaged along with the executable, compiled code, so that your users can easily jump to the definitions that theyre calling into within their IDE.

task androidSourcesJar(type: Jar) {    archiveClassifier.set('sources')    if (project.plugins.findPlugin("com.android.library")) {        // For Android libraries        from android.sourceSets.main.java.srcDirs        from android.sourceSets.main.kotlin.srcDirs    } else {        // For pure Kotlin libraries, in case you have them        from sourceSets.main.java.srcDirs        from sourceSets.main.kotlin.srcDirs    }}artifacts {    archives androidSourcesJar}

Once again, our real publishing file also adds an additional javadocJar task which ships Dokka-generated documentation - again, you can skip this if you want to.

Youll set two properties on the Gradle project itself here, the group ID and the version of the artifact. Youll see where these uppercase values come from later on, when you apply this publication script in the module level build.gradle files.

group = PUBLISH_GROUP_IDversion = PUBLISH_VERSION

Here comes the complicated part, providing all the metadata for the library youre releasing. See the inline comments for the play-by-play explanation.

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                artifact javadocJar                // Mostly self-explanatory metadata                pom {                    name = PUBLISH_ARTIFACT_ID                    description = 'Stream Chat official Android SDK'                    url = 'https://github.com/getstream/stream-chat-android'                    licenses {                        license {                            name = 'Stream License'                            url = 'https://github.com/GetStream/stream-chat-android/blob/main/LICENSE'                        }                    }                    developers {                        developer {                          id = 'zsmb13'                          name = 'Mrton Braun'                          email = '[email protected]'                        }                        // Add all other devs here...                    }                    // Version control info - if you're using GitHub, follow the                     // format as seen here                    scm {                        connection = 'scm:git:github.com/getstream/stream-chat-android.git'                        developerConnection = 'scm:git:ssh://github.com/getstream/stream-chat-android.git'                        url = 'https://github.com/getstream/stream-chat-android/tree/main'                    }                }            }        }    }}

Finally, this piece of code grabs signing related values from the root project, and then tells the signing plugin to sign the artifacts youve defined above as a publication.

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}

Thats the publish-module.gradle script all built up, ready to use. This gist contains the code blocks shown above in a single snippet. The final, up-to-date script we use for publishing our SDK can be found in the Stream Chat repository.

Time to include this script in a module! Head to the build.gradle file of your library module - in our case, this is the stream-chat-android-client module - and add the following code:

ext {    PUBLISH_GROUP_ID = 'io.getstream'    PUBLISH_VERSION = '4.5.2'    PUBLISH_ARTIFACT_ID = 'stream-chat-android-client'}apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"

Here you finally see the group ID, artifact ID, and version being set, so that the publication script can make use of them. Then, the script itself is applied. This is all the code you need to add per-module if you are publishing your library in multiple artifacts, everything else is done by the common script.

Your first release

With all of that set up, youre now ready to publish the first version of your library!

For each repository you have defined in the publishing script, a Gradle task will be created to publish to that repository. In our example, our first module to publish is stream-chat-android-client, and weve named the repository sonatype. Therefore, we need to execute the following command to start publication (replace the module name with your own here):

gradlew stream-chat-android-client:publishReleasePublicationToSonatypeRepository

This will create a so-called staging repository for your library, and upload your artifacts (aar and sources) to that repository. At this point, you can check that all the artifacts you wanted to upload have made it, before hitting the release button.

To view the repository, go to https://oss.sonatype.org/ and log in. In the menu on the left, select Staging repositories.

The Sonatype menu

Find your repository (might be the only one in the list, might have to scroll around a bit for it), which has your group ID in its name. If you select it and look at the Content tab, youll see the files that have been uploaded.

The contents of the staging repository

If everything looks good, and youre done uploading files to the repository, you have to Close it. With the repository selected, hit the Close button in the toolbar on top. Confirm your action in the dialog (you dont need to provide a description here).

Closing a staging repository

This will take just a few moments, you can follow along with it happening in the Activity tab.

Repository Activity tab showing successful close operation

With the repository closed, you now have two final options available to you. Drop will throw away the repository, and cancel the publication process. Use this if something went wrong during the upload or youve changed your mind.

Release, on the other hand, will publish the contents of your staging repository to MavenCentral. Again, you get a confirmation dialog, and you can choose Automatically Drop so that the staging repository is cleaned up after the release completes.

Release dialog for the staging repository

The time this process takes can vary a bit. If you get lucky, your artifact will show up on MavenCentral in 10-15 minutes, but it could also take an hour or more in other cases. You can check whether your artifact is available by going to https://repo1.maven.org/maven2/ and browsing for it.

For example, for our client, we can keep refreshing the following page to see if the new version is there: https://repo1.maven.org/maven2/io/getstream/stream-chat-android-client/.

If you see the package via those links, Gradle will also be able to pull it if mavenCentral() is added as a repository and you add your library as a dependency.

Search indexing is a separate, even longer process, so it can take about two hours for your artifact to show up on search.maven.org.

If this was your first release, you should at this point go back and comment on your original Jira issue, to let them know that your repository setup and publication is working.

Automating Sonatype actions

That was quite the adventure! To make things smoother for subsequent releases, you can automate the entire release flow with the publishing plugin that you already have configured in the project.

This, in theory, would mean that you dont have to revisit the Sonatype UI ever again. In practice, youll do that more often than youd like when the plugin (really, the Sonatype API it calls) breaks or misbehaves, so it's worth being familiar with.

For this, you'll need to set the sonatypeStagingProfileId variable in your project. This value is an ID that Sonatype assigns to you, which the plugin uses to make sure all the artifacts end up in the right place during the upload. You can find this by going to Staging profiles, selecting your profile, and looking at the ID in the URL.

The Sonatype staging profiles screen and the staging profile ID shown in the URL

Either set this value in your local.properties file, or set the corresponding environment variable.

With this configuration done, the plugin provides a new Gradle task that you can use to close and then release your staging repository with one simple call:

gradlew closeAndReleaseSonatypeStagingRepository

At this point, you can upload and publish your library by just invoking these two Gradle tasks in sequence - pretty convenient! As a final step, lets hook this into a CI pipeline.

Continuous integration

Since the library is hosted on GitHub anyway, we use GitHub Actions for running the publication Gradle tasks automatically. Whatever CI solution youre using, setting up publication with it will consist of two main steps:

  1. Getting your secret variables in place.
  2. Invoking the two Gradle tasks.

Most of your secret variables - for the list of these, look at the publishing script again - can simply go into Repository secrets (Add each of these by going to Settings -> Secrets within your GitHub repository):

GitHub secrets screen

For the location of the signing key (for the SIGNING_SECRET_KEY_RING_FILE secret), an easy to use value is /secret.gpg, which places the file in the root of the file system on the runner machine. This makes it easy to reference it in the project, independent of the working directory.

Your private GPG key is harder to inject into the build than the other values. It needs to be present as a file for the signing task, but you should not commit it into a public repository.

You could technically commit the private key into a public repository, since its protected by its passphrase. At that point, your key is only as secure as the strength of your passphrase (see more discussion here and here). Its much more secure to keep the key entirely private though.

The workaround for this is to add its contents as a secret variable, and then write those contents into a temporary file during your build.

If youre not the only collaborator in your repository, this method will expose your key to the other collaborators (they could get to it by running workflows that expose it). In that case, you might want to stick to local publishing only.

Since the key is a binary file, you need to first convert its contents into text form - base 64 encoding comes to the rescue. Convert your secret key file into base 64 with the following command (if youre on Windows, you can use a Git or Ubuntu bash for this):

base64 5BEF072A.gpg > 5BEF072A.txt

Place the contents of this file into yet another protected variable, and name it GPG_KEY_CONTENTS.

Now, lets create the GitHub Actions workflow that will put all of this together. The configuration for this will go in the .github/workflows/publish.yml file of the repository. This publish workflow will run every time a new release is created in the repository (you can also change the triggers to run when a tag is created, for example). Again, see the inline comments for some explanation of what each step does.

If youre new to GitHub Actions, check out their introductory documentation first.

name: Publishon:  release:    # We'll run this workflow when a new GitHub release is created    types: [released]jobs:  publish:    name: Release build and publish    runs-on: ubuntu-latest    steps:      - name: Check out code        uses: actions/checkout@v2      - name: Set up JDK 1.8        uses: actions/setup-java@v1        with:          java-version: 1.8        # Base64 decodes and pipes the GPG key content into the secret file      - name: Prepare environment        env:          GPG_KEY_CONTENTS: ${{ secrets.GPG_KEY_CONTENTS }}          SIGNING_SECRET_KEY_RING_FILE: ${{ secrets.SIGNING_SECRET_KEY_RING_FILE }}        run: |          git fetch --unshallow          sudo bash -c "echo '$GPG_KEY_CONTENTS' | base64 -d > '$SIGNING_SECRET_KEY_RING_FILE'"        # Builds the release artifacts of the library      - name: Release build        run: ./gradlew :stream-chat-android-client:assembleRelease        # Generates other artifacts (javadocJar is optional)      - name: Source jar and dokka        run: ./gradlew androidSourcesJar javadocJar        # Runs upload, and then closes & releases the repository      - name: Publish to MavenCentral        run: ./gradlew publishReleasePublicationToSonatypeRepository --max-workers 1 closeAndReleaseSonatypeStagingRepository        env:          OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}          OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}          SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}          SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}          SIGNING_SECRET_KEY_RING_FILE: ${{ secrets.SIGNING_SECRET_KEY_RING_FILE }}          SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }}

MavenCentral uploads tend to be Flaky, to put it nicely. Here are some recommendations to have fewer issues with them:

  • Perform all your Gradle upload tasks in a single job, on a single machine.
  • The --max-workers 1 option used above guarantees that the upload task runs on a single thread, even if parallel builds are otherwise configured.
  • Its also a good idea to put the upload task (all of them, if youre uploading multiple modules worth of artifacts) in a different Gradle invocation than the assemble & signing tasks, as you see it done above, so that they run closely together.

If a long time passes between your upload calls, or if they originate from different CI machines, you might see problems such as multiple staging repositories being created for you on Sonatype with your files scattered all over them.

Even if you do everything described above right, you might still get random upload errors, or timeouts either during the upload or while the plugin is trying to close and release the repository. At this point, being able to look at the staging repository on the Sonatype UI and manually closing/dropping/releasing repositories will come in handy to fix things up.

If your staging repository seems okay (has correct contents), and it just failed to close, you can close and release it yourself. If it looks messed up somehow, or you ended up with multiple staging repositories, its best to drop them all and then run the upload workflow again.

Reminder: our up-to-date publishing workflow is always available in our GitHub repository.

Conclusion

Well, that was quite a journey. We hope that this detailed guide helped you get up and running with MavenCentral publication. You can follow us on Twitter @getstream_io for more great technical content - please tweet at us if you've successfully set up publishing following these steps! You can also reach the author @zsmb13 if you have any questions.

If you're looking for a Kotlin-first, powerful Chat SDK to use in your Android app, check out our Android documentation and our Android Chat tutorial.

If you're interested in library development, you'll find these articles helpful as well:


Original Link: https://dev.to/zsmb13/publishing-android-libraries-to-mavencentral-in-2021-44oh

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To