Skip to Content
Course content

186: Publishing Libraries to Maven Central

Click on the "Edit" button in the top corner of the screen to edit your slide content.

By now, you've probably written a few internal libraries that you copy-paste between projects. It works, but it's a nightmare to maintain. Once your code is stable and useful, the right move is to publish it to Maven Central. That way, anyone—including your future self—can just add a single line to their build.sbt and get to work.

For this lesson, we're going to publish a small utility I've been wanting: a CaseInsensitiveMap. It's a simple wrapper around a standard Scala Map that treats keys as case-insensitive. It's small enough to keep this focused, but concrete enough to show how the publishing pipeline actually behaves.

Defining the CaseInsensitiveMap utility

First, I'll set up the core logic. I want this to feel like a native Scala collection, so I'll implement it as a class that wraps a Map[String, V].

package com.mentormind.collections

class CaseInsensitiveMap[V] {
  private val inner = scala.collection.mutable.Map.empty[String, V]

  def put(key: String, value: V): Unit = {
    inner.put(key.toLowerCase, value)
  }

  def get(key: String): Option[V] = {
    inner.get(key.toLowerCase)
  }

  def contains(key: String): Boolean = {
    inner.contains(key.toLowerCase)
  }
}

I'm keeping it minimal here. In a production library, I'd probably make it implement scala.collection.Map, but for the sake of demonstrating the publishing process, this is plenty.

Configuring sbt for Sonatype and Maven Central

You can't just "upload" to Maven Central. You actually go through Sonatype's OSSRH (Open Source Software Repository Hosting). You'll need an account there, but the heavy lifting happens in your project/plugins.sbt. I'm using sbt-pgp because Maven Central requires every single artifact to be digitally signed. If you don't sign your jars, they'll be rejected immediately.

// project/plugins.sbt
addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.1")
```

Now, in the main build.sbt, I need to define the coordinates. This is the "address" of your library. If you pick a generic organization name, Sonatype will reject your request during the account setup phase, so I'm using a specific domain I own.

organization := "com.mentormind"
name := "case-insensitive-map"
version := "0.1.0"
scalaVersion := "2.13.12"

publishTo := Some("sonatype::ossrh:release")

The GPG signing stumble

Here is where I always trip up the first time. I ran sbt publish, and it failed with a massive stack trace complaining that it couldn't find a GPG key. I had GPG installed on my machine, but I hadn't actually created a key pair or told sbt where to find it.

I tried to bypass it by setting pgpSigningKey := None, but that's a trap. While that lets the build finish locally, the Sonatype servers will reject the upload the moment it hits their API. To fix this, I had to generate a key using gpg --full-generate-key and then export the public key to a keyserver so Sonatype can verify it.

Once the key was live, I added the passphrase to my ~/.sbt/1.0/plugins/pgp.conf (or passed it as a system property) so I didn't have to type it every single time I published. It's a tedious step, but it's the price of admission for the global ecosystem.

Pushing to the staging repository

Maven Central uses a "Staging Repository." Your code doesn't go live immediately. Instead, it goes into a temporary bucket where Sonatype's automated tools check for the GPG signature, the POM file, and the Javadoc.

I run sbt publish, and sbt handles the upload of the JAR, the POM, and the signature files. After that, I head over to the Sonatype Nexus UI, find my staging repository, and click "Close" and then "Release." Only then does the library actually propagate to the Maven Central mirrors. It usually takes a few hours before you can actually see it when searching via sbt.




📋 Practical Task

Exercise: Local Simulation of CaseInsensitiveMap Publishing

Since creating a Sonatype account takes a few days for approval, your task is to simulate the publishing pipeline locally. This ensures your build.sbt and project structure are correct before you attempt a real upload.

  1. Create a new Scala project and implement the CaseInsensitiveMap class as shown in the lesson.
  2. Configure your build.sbt with the organization "com.yourname.practice" and version "0.1.0-SNAPSHOT".
  3. Instead of publishing to Sonatype, run the command sbt publishM2. This publishes the library to your local ~/.m2/repository folder.
  4. Create a separate Scala project in a different directory. In its build.sbt, add the library you just published:
    libraryDependencies += "com.yourname.practice" % "case-insensitive-map" % "0.1.0-SNAPSHOT"
  5. Write a small main method in this second project that instantiates CaseInsensitiveMap, puts a value under the key "Scala", and successfully retrieves it using the key "scala".
Rating
0 0

There are no comments for now.

to be the first to leave a comment.