How to deploy artifact to the Maven Central Repository

It was quite a long time ago when I wrote the last blog post. Sometimes it just takes time till inspiration finds you… but do not rush forward!
Last year I promised: I will report about growing tomatoes and I keep my word.

Motivation

You have built something niche and you want to give back something to the community, right? Even if the idea (and the implementation) is revolutionary it will not be used by anyone if it is not accessible somehow. To be concrete: if we talk about a jar then this artifact must be available in the Maven Central Repository – or at least in a public repository somewhere. Since Maven Central Repository is the default repo the best is to have our world savior there.

I went through some tutorials but some of them were outdated while others worked only on Linux/Mac. Since I am more a Windows guy I had to walk sometimes in the dark. I do not say that it was difficult but this is something that no one does too frequently thus in a few years when I buy my next notebook I will just scratch my head: what have I done to make it work?

Prerequisites

I assume you have a basic understanding of

  • Windows
  • Git
  • Java
  • Maven

Steps

  1. Create your account on Github
  2. Upload your Maven project and take java-offline-geoip as an example
  3. Study the pom.xml, the important sections are
    • <version>${semver}</version>
    • <distributionManagement>
          <snapshotRepository>
              <id>ossrh</id>
              <url>https://oss.sonatype.org/content/repositories/snapshots</url>
          </snapshotRepository>
          <repository>
              <id>ossrh</id>
              <url>https://oss.sonatype.org/service/local/staging/deploy/maven2</url>
          </repository>
      </distributionManagement>
    • <scm>
          <url>https://github.com/tornaia/java-offline-geoip</url>
          <connection>scm:git:https://github.com/tornaia/java-offline-geoip.git</connection>
          <developerConnection>scm:git:git@github.com:tornaia/java-offline-geoip.git</developerConnection>
      </scm>
    • <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-source-plugin</artifactId>
          <version>${maven-source-plugin.version}</version>
          <executions>
              <execution>
                  <id>attach-sources</id>
                  <goals>
                      <goal>jar</goal>
                  </goals>
              </execution>
          </executions>
      </plugin>
    • <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-javadoc-plugin</artifactId>
          <version>${maven-javadoc-plugin.version}</version>
          <configuration>
              <additionalOptions>
                  <additionalOption>-html5</additionalOption>
              </additionalOptions>
          </configuration>
          <executions>
              <execution>
                  <id>attach-javadocs</id>
                  <goals>
                      <goal>jar</goal>
                  </goals>
              </execution>
          </executions>
      </plugin>
    • <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-gpg-plugin</artifactId>
          <version>${maven-gpg-plugin.version}</version>
          <executions>
              <execution>
                  <id>sign-artifacts</id>
                  <phase>verify</phase>
                  <goals>
                      <goal>sign</goal>
                  </goals>
              </execution>
          </executions>
      </plugin>
    • <plugin>
          <groupId>org.sonatype.plugins</groupId>
          <artifactId>nexus-staging-maven-plugin</artifactId>
          <version>${nexus-staging-maven-plugin.version}</version>
          <extensions>true</extensions>
          <configuration>
              <serverId>ossrh</serverId>
              <nexusUrl>https://oss.sonatype.org</nexusUrl>
              <autoReleaseAfterClose>true</autoReleaseAfterClose>
          </configuration>
      </plugin>
    • <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-scm-plugin</artifactId>
          <version>${maven-scm-plugin.version}</version>
          <configuration>
              <tag>${project.artifactId}-${project.version}</tag>
          </configuration>
      </plugin>
  4. Create a Sonatype account
  5. Open ticket to create your new project and take mine as an example
  6. Install GnuPG
  7. Create a new key pair with Kleopatra: add your name and email too
    • Remember for your passphrase
    • Make a backup of your key pair
    • Generate a revocation certificate – later you need to edit it manually to avoid accidental use
    • Keep these files in a very safe place
  8. Export your certificate to an OpenPGP Keyserver
    1. Configure Kleopatra by right clicking on its system tray icon
    2. Set OpenPGP Keyserver to hkp://pool.sks-keys under Directory Services
    3. Apply
    4. Open Kleopatra by a single click on the system tray icon
    5. Publish it by right clicking on the certificate and selecting Publish on server…
  9. Create/Edit ~/.m2/settings.xml
    • <settings>
        <servers>
          <server>
            <id>ossrh</id>
            <username>YOUR_SONARTYPE_ACCOUNT_NAME</username>
            <password>YOUR_SONARTYPE_ACCOUNT_PASSWORD</password>
          </server>
        </servers>
        <profiles>
          <profile>
            <id>ossrh</id>
            <activation>
              <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
              <gpg.executable>gpg</gpg.executable>
            </properties>
          </profile>
        </profiles>
      </settings>
  10. Deploy your artifact: mvn clean deploy scm:tag -Dsemver=0.0.1
    • It will ask for your certificate’s passphrase
    • After you promoted your first release you need to add a comment to your “new project” ticket then central sync will be activated
    • After you successfully release, your component will be published to Maven Central Repository, typically within 10 minutes, though updates to search.maven.org can take up to two hours
    • If the version ends with -SNAPSHOT then it will go to the snapshots repository – but I know you know Maven very well! 😉

Misc

I was able to deploy snapshots but somehow I had no rights to deploy releases and encountered the following error:
you have no permissions to stage against profile with ID "62d9bff4b07028"? Get to Nexus admin...
I was not the only one with this so I guess it is a common issue but after opening a Jira ticket it just worked.

4 thoughts on “How to deploy artifact to the Maven Central Repository”

  1. Wait, how is Maven going to find the certificate created with Kleopatra and ask the passphrase?? That’s where I’m stuck…

    Like

    1. In Kleopatra I see a table with Name, E-Mail, User-IDs, Valid From, Valid Until, Details and Key-ID columns. I have only one entry there where the name is “Andras Tornai”.

      The normal way I do the whole stuff:
      mvn clean deploy scm:tag -Dsemver=0.1.20

      The normal way equals to this somehow:
      mvn clean deploy scm:tag -Dsemver=0.1.20 -Dgpg.keyname=”Andras Tornai”

      If I override the default gpg.keyname, then the build fails:
      mvn clean deploy scm:tag -Dsemver=0.1.20 -Dgpg.keyname=”Andras Tornai whatever”

      [INFO] — maven-gpg-plugin:1.6:sign (sign-artifacts) @ java-offline-geoip —
      gpg: skipped “Andras Tornai whatever”: No secret key
      gpg: signing failed: No secret key

      [INFO] BUILD FAILURE

      These two links might help you:
      http://maven.apache.org/plugins/maven-gpg-plugin/sign-mojo.html#keyname
      https://www.gnupg.org/gph/en/manual/r1353.html

      I hope it helps… **fingers crossed

      Like

      1. Hey, thanks for your reply! I finally managed to solve the issue when I noticed that GPG was creating empty pubring.gpg and secring.gpg files somewhere. I figured those are the files where it looks for the keys. In Kleopatra you can right-click on a certificate and select “Export…” or “Export Secret Keys…”. It then suggests a name, which, in my case, is a long hexadecimal string with a “.asc” extension. The key here is to note that the save dialog has a “Save as type” selector with “*.asc *.gpg *.pgp” all in the same selection. So I stored the public key as pubring.gpg and the secret key as secring.gpg using this dialog, placed those files into the directory where I found the empty files, and it all just clicked. 🙂

        Like

  2. This is my very first time visiting your blog and I’m very interested. Many thanks for sharing and keep up 😉

    Like

Leave a comment