


=======================
 Deploying to Sonatype
=======================

Deploying to sonatype is easy! Just follow these simple steps:

First - PGP Signatures
----------------------

You'll need to PGP sign your artifacts for the Sonatype repository.
Don't worry, there's a `plugin for that <http://scala-sbt.org/sbt-pgp>`_.
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 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 <http://scala-sbt.org/sbt-pgp>`_ plugin:

::

    pgp-cmd send-key keyname hkp://pool.sks-keyservers.net/

(where keyname is the name, 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.

Second - Maven Publishing Settings
----------------------------------

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

Third - POM Metadata
--------------------

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 :key:`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>git@github.com:jsuereth/scala-arm.git</url>
        <connection>scm:git:git@github.com: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 <http://jsuereth.com/scala-arm>`_ project.

*Note* that sbt will automatically inject `licenses` and `url` nodes
if they are already present in your build file. Thus an alternative to
the above :key:`pomExtra` 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 :key:`licenses` key and the
`licenses` section in :key:`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 full format of a pom.xml file is `outlined
here <http://maven.apache.org/pom.html>`_.*

Fourth - Adding credentials
---------------------------

The credentials for your Sonatype OSSRH account need to be added
somewhere. Common convention is a :sublit:`|globalBase|/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.*

Finally - Publish
-----------------

In sbt, run `publishSigned` and you should see something like the following:

.. code-block:: console

    > 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 <https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide#SonatypeOSSMavenRepositoryUsageGuide-8.ReleaseIt>`_.
`sbt-sonatype plugin <https://github.com/xerial/sbt-sonatype>`_ 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.

Summary
-------

To get your project hosted on Sonatype (and Maven Central), you will
need to:

-  Have GPG key pair, with published public key,
-  An sbt file with your Sonatype credentials *that is not pushed to the VCS*,
-  Add the `sbt-pgp plugin <http://scala-sbt.org/sbt-pgp>`_ to sign the artefacts,
-  Modify `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:

~/.sbt/sonatype.sbt
^^^^^^^^^^^^^^^^^^^

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")

~/.sbt/plugins/gpg.sbt
^^^^^^^^^^^^^^^^^^^^^^

The `sbt-pgp plugin <http://scala-sbt.org/sbt-pgp>`_ allows you to
sign and publish your artefacts by running `publishSigned` in sbt:

::

    addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "0.8")

build.sbt
^^^^^^^^^

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>git@github.com:your-account/your-project.git</url>
        <connection>scm:git:git@github.com:your-account/your-project.git</connection>
      </scm>
      <developers>
        <developer>
          <id>you</id>
          <name>Your Name</name>
          <url>http://your.url</url>
        </developer>
      </developers>
    )

.. |globalBase| replace:: ~/.sbt/|version|
