Deploying to sonatype is easy! Just follow these simple steps:
You’ll need to PGP sign your artifacts for the Sonatype repository. Don’t worry, there’s a plugin for that. Follow the instructions for the plugin and you’ll have PGP signed artifacts in no time.
- Note: The plugin is a jvm-only solution to generate PGP keys and sign artifacts. It can work with the GPG command line tool, but the command line is not needed.*
If the command to generate your key fails execute the following commands and remove the displayed files:
> show */*:pgpSecretRing
[info] /home/username/.sbt/.gnupg/secring.gpg
> show */*:pgpPublicRing
[info] /home/username/.sbt/.gnupg/pubring.gpg
If your PGP key has not yet been distributed to the keyserver pool, i.e., you’ve just generated it, you’ll need to publish it. You can do so using the sbt-pgp plugin:
pgp-cmd send-key keyname hkp://pool.sks-keyservers.net
(where keyname is the name or email address used when creating the key or hexadecimal identifier for the key.)
If you see no output from sbt-pgp then the key name specified was not found.
If it fails to run the SendKey
command you can try another server (for example: hkp://keyserver.ubuntu.com). A list of servers can be found at the status page of sks-keyservers.net.
To publish to a maven repository, you’ll need to configure a few settings so that the correct metadata is generated.
publishMavenStyle := true
is used to ensure POMs are generated and pushed. Next, you have to set up the repositories you wish to push too. Luckily, Sonatype’s OSSRH uses the same URLs for everyone:
publishTo := {
val nexus = "https://oss.sonatype.org/"
if (isSnapshot.value)
Some("snapshots" at nexus + "content/repositories/snapshots")
else
Some("releases" at nexus + "service/local/staging/deploy/maven2")
}
Another good idea is to not publish your test artifacts (this is the default):
publishArtifact in Test := false
Now, we want to control what’s available in the pom.xml
file. This
file describes our project in the maven repository and is used by
indexing services for search and discover. This means it’s important
that pom.xml
should have all information we wish to advertise as well
as required info!
First, let’s make sure no repositories show up in the POM file. To publish on maven-central, all required artifacts must also be hosted on maven central. However, sometimes we have optional dependencies for special features. If that’s the case, let’s remove the repositories for optional dependencies in our artifact:
pomIncludeRepository := { _ => false }
Next, the POM metadata that isn’t generated by sbt must be added. This
is done through the pomExtra
configuration option:
pomExtra := (
<url>http://jsuereth.com/scala-arm</url>
<licenses>
<license>
<name>BSD-style</name>
<url>http://www.opensource.org/licenses/bsd-license.php</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<url>[email protected]:jsuereth/scala-arm.git</url>
<connection>scm:git:[email protected]:jsuereth/scala-arm.git</connection>
</scm>
<developers>
<developer>
<id>jsuereth</id>
<name>Josh Suereth</name>
<url>http://jsuereth.com</url>
</developer>
</developers>)
Specifically, the url
, license
, scm.url
, scm.connection
and
developer
sections are required. The above is an example from the
scala-arm project.
Note that sbt will automatically inject
licenses
andurl
nodes if they are already present in your build file. Thus an alternative to the abovepomExtra
is to include the following entries:
licenses := Seq("BSD-style" -> url("http://www.opensource.org/licenses/bsd-license.php"))
homepage := Some(url("http://jsuereth.com/scala-arm"))
This might be advantageous if those keys are used also by other plugins
(e.g. ls
). You cannot use both the sbt licenses
key and the
licenses
section in pomExtra
at the same time, as this will produce
duplicate entries in the final POM file, leading to a rejection in
Sonatype’s staging process.
The credentials for your Sonatype OSSRH account need to be added
somewhere. Common convention is a ~/.sbt/0.13/sonatype.sbt
file
with the following:
credentials += Credentials("Sonatype Nexus Repository Manager",
"oss.sonatype.org",
"<your username>",
"<your password>")
- Note: The first two strings must be
"Sonatype Nexus Repository Manager"
and"oss.sonatype.org"
for Ivy to use the credentials.*
In sbt, run publishSigned
and you should see something like the
following:
> publishSigned
Please enter your PGP passphrase> ***********
[info] Packaging /home/josh/projects/typesafe/scala-arm/target/scala-2.9.1/scala-arm_2.9.1-1.2.jar ...
[info] Wrote /home/josh/projects/typesafe/scala-arm/target/scala-2.9.1/scala-arm_2.9.1-1.2.pom
[info] Packaging /home/josh/projects/typesafe/scala-arm/target/scala-2.9.1/scala-arm_2.9.1-1.2-javadoc.jar ...
[info] Packaging /home/josh/projects/typesafe/scala-arm/target/scala-2.9.1/scala-arm_2.9.1-1.2-sources.jar ...
[info] :: delivering :: com.jsuereth#scala-arm_2.9.1;1.2 :: 1.2 :: release :: Mon Jan 23 13:16:57 EST 2012
[info] Done packaging.
[info] Done packaging.
[info] Done packaging.
[info] delivering ivy file to /home/josh/projects/typesafe/scala-arm/target/scala-2.9.1/ivy-1.2.xml
[info] published scala-arm_2.9.1 to https://oss.sonatype.org/service/local/staging/deploy/maven2/com/jsuereth/scala-arm_2.9.1/1.2/scala-arm_2.9.1-1.2-sources.jar
[info] published scala-arm_2.9.1 to https://oss.sonatype.org/service/local/staging/deploy/maven2/com/jsuereth/scala-arm_2.9.1/1.2/scala-arm_2.9.1-1.2-javadoc.jar.asc
[info] published scala-arm_2.9.1 to https://oss.sonatype.org/service/local/staging/deploy/maven2/com/jsuereth/scala-arm_2.9.1/1.2/scala-arm_2.9.1-1.2-sources.jar.asc
[info] published scala-arm_2.9.1 to https://oss.sonatype.org/service/local/staging/deploy/maven2/com/jsuereth/scala-arm_2.9.1/1.2/scala-arm_2.9.1-1.2.jar
[info] published scala-arm_2.9.1 to https://oss.sonatype.org/service/local/staging/deploy/maven2/com/jsuereth/scala-arm_2.9.1/1.2/scala-arm_2.9.1-1.2.jar.asc
[info] published scala-arm_2.9.1 to https://oss.sonatype.org/service/local/staging/deploy/maven2/com/jsuereth/scala-arm_2.9.1/1.2/scala-arm_2.9.1-1.2.pom.asc
[info] published scala-arm_2.9.1 to https://oss.sonatype.org/service/local/staging/deploy/maven2/com/jsuereth/scala-arm_2.9.1/1.2/scala-arm_2.9.1-1.2.pom
[info] published scala-arm_2.9.1 to https://oss.sonatype.org/service/local/staging/deploy/maven2/com/jsuereth/scala-arm_2.9.1/1.2/scala-arm_2.9.1-1.2-javadoc.jar
[success] Total time: 9 s, completed Jan 23, 2012 1:17:03 PM
After publishing you have to follow the release workflow of Nexus. The sbt-sonatype plugin allows the release workflow procedures to be performed directly from sbt.
- Note: Staged releases allow testing across large projects of independent releases before pushing the full project.*
Note: An error message of
PGPException: checksum mismatch at 0 of 20
indicates that you got the passphrase wrong. We have found at least on OS X that there may be issues with characters outside the 7-bit ASCII range (e.g. Umlauts). If you are absolutely sure that you typed the right phrase and the error doesn’t disappear, try changing the passphrase.
To get your project hosted on Sonatype (and Maven Central), you will need to:
build.sbt
with the required elements in the generated POM.
Starting with a project that is not being published, you’ll need to install GPG, generate and publish your key. Swtiching to sbt, you’ll then need to:
This file (kept outside the VCS) contains the Sonatype credentials settings:
credentials += Credentials("Sonatype Nexus Repository Manager",
"oss.sonatype.org",
"your-sonatype-username",
"your-sonatype-password")
The sbt-pgp plugin allows you to sign
and publish your artefacts by running publishSigned
in sbt:
addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "0.8")
Finally, you’ll need to tweak the generated POM in your build.sbt
. The
tweaks include specifying the project’s authors, URL, SCM and many
others:
publishTo := {
val nexus = "https://oss.sonatype.org/"
if (isSnapshot.value)
Some("snapshots" at nexus + "content/repositories/snapshots")
else
Some("releases" at nexus + "service/local/staging/deploy/maven2")
}
publishMavenStyle := true
publishArtifact in Test := false
pomIncludeRepository := { _ => false }
pomExtra := (
<url>http://your.project.url</url>
<licenses>
<license>
<name>BSD-style</name>
<url>http://www.opensource.org/licenses/bsd-license.php</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<url>[email protected]:your-account/your-project.git</url>
<connection>scm:git:[email protected]:your-account/your-project.git</connection>
</scm>
<developers>
<developer>
<id>you</id>
<name>Your Name</name>
<url>http://your.url</url>
</developer>
</developers>
)