$shibayu36->blog;

エンジニアをしています。プログラミングや読書のことなどについて書いています。

自作ScalaライブラリをMaven Centralにリリースする手順メモ

最近Scalaの勉強も兼ねてjoda-time-fakeというScalaライブラリをMaven Central Repositoryにリリースしてみた。今回はその手順を忘れないようにメモとして残しておく。

今回の手順でできたこと

  • com.github.shibayu36 organizationにjoda-time-fakeとしてライブラリをリリースする
  • Scalaの2.11と2.12でクロスコンパイルして両方共リリースする
  • 最終的にsbt-releaseでリリースを自動化できるようにする

参考資料

自作のScalaライブラリをMaven Central Repositoryにリリースする - Qiitaが一番参考になった。少しハマりどころはあったものの、これを見て一つずつやっていったらリリースまで出来たので感謝。今回の僕の記事はクロスコンパイルして複数バージョン上げるという点が違うだけで、この記事とほぼ同様なものになっている。

またsbtの公式ドキュメントとしてsbt Reference Manual — Using Sonatypeも参考になった。sonatypeの公式ドキュメントとしてOSSRH Guideも参考にしたけど、こちらは正直何をしていったら良いか分かりづらかった。

build.sbtや構成などは以下の二つのレポジトリを参考にした。

環境

  • macOS High Sierra 10.13.3
  • scala 2.12.4 & 2.11.12
  • sbt 1.1.1
  • sbt-sonatype 2.3
  • sbt-pgp 1.1.1
  • sbt-release 1.0.8

作業の流れ

  • Sonatype JIRA にアカウントを登録する
  • Sonatype JIRAにNew Project Issueを作成する
  • sbt-pgp を使ってGPGの公開鍵を keyserver に登録する
  • build.sbtでコンパイルやリリースの設定をする
  • sbt-sonatypeでリリースをする
  • 初回リリースしたらProject Issueにコメント
  • リリース確認
  • 【オプション】sbt-releaseによるリリース自動化

Sonatype JIRA にアカウントを登録する

https://qiita.com/kiris/items/b043a7582c22110d7097#sonatype-jira-%E3%81%AB%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%82%92%E7%99%BB%E9%8C%B2%E3%81%99%E3%82%8B のとおり、https://issues.sonatype.org/secure/Signup!default.jspa からアカウントを作る。

後のリリース作業のために、$HOME/.sbt/1.0/sonatype.sbtにその認証情報を記述。

credentials += Credentials("Sonatype Nexus Repository Manager",
  "oss.sonatype.org",
  "<Sonatype JIRAのユーザー名>",
  "<Sonatype JIRAのパスワード>")

Sonatype JIRAにNew Project Issueを作成する

ユーザー登録したら、アップロードするorganizationを作ってもらうために、Sonatype JIRAにNew Project Issueを作成する。https://qiita.com/kiris/items/b043a7582c22110d7097#sonatype-jira%E3%81%ABnew-project-issue%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B のようにやったらできるはず。今回のは https://issues.sonatype.org/browse/OSSRH-38391 なので、参考にどうぞ。

僕はNew Issueしたらすぐにcom.github.shibayu36を用意してもらえた。

sbt-pgp を使ってGPGの公開鍵を keyserver に登録する

リリースするためには公開鍵を何処かに登録しておかないといけない。sbt-pgpを使うと簡単に登録できる。

~/.sbt/1.0/plugins/gpg.sbtを追加

addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.1")

コマンドを実行

$ sbt
> set useGpg := true
> set pgpReadOnly := false
> pgp-cmd gen-key
> pgp-cmd list-keys

この時ちょっとはまったことがあって、pgp-cmd gen-keyした時にエラーが起こって落ちた。

> pgp-cmd gen-key
Failed to run pgp-cmd: GeneratePgpKey().   Please report this issue at http://github.com/sbt/sbt-pgp/issues

これは https://github.com/sbt/sbt-pgp/issues/55 を参考にして、show */*:pgpSecretRingshow */*:pgpPublicRingコマンドを打って出てきたファイルをrmしたら直った。中身は空だったのでたぶん一度set pgpReadOnly := falseせずにpgp-cmd gen-keyをしてしまっていたので、その時に空ファイルができてしまっていたのだと思う。

build.sbtでコンパイルやリリースの設定をする

続いてbuild.sbtでコンパイルやリリースの設定をする。https://github.com/shibayu36/joda-time-fake-scala/blob/0.0.1/build.sbt を参考に。基本的には

あたりを設定すると良い。

build.sbtはこんな感じ。

name := "joda-time-fake"

organization := "com.github.shibayu36"

version := "0.0.1"

scalaVersion := "2.12.4"
crossScalaVersions := Seq("2.11.12", "2.12.4")

libraryDependencies ++= Seq(
  "joda-time" % "joda-time" % "2.9.9",
  "org.scalatest" %% "scalatest" % "3.0.5" % "test"
)

// Mavenへリリースするための設定
publishMavenStyle := true
publishArtifact in Test := false
pomIncludeRepository := { _ => false }
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")
}
// リリース設定はここまではテンプレで書いておく
// 以下は自分のライブラリに合わせて書いていく
pomExtra := (
  <url>https://github.com/shibayu36/joda-time-fake-scala</url>
  <licenses>
    <license>
      <name>The Apache Software License, Version 2.0</name>
      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
      <distribution>repo</distribution>
    </license>
  </licenses>
  <scm>
    <url>https://github.com/shibayu36/joda-time-fake-scala</url>
    <connection>scm:git:https://github.com/shibayu36/joda-time-fake-scala.git</connection>
  </scm>
  <developers>
    <developer>
      <id>shibayu36</id>
      <name>Yuki Shibazaki</name>
      <url>https://github.com/shibayu36/</url>
    </developer>
  </developers>
)

pomの設定は、https://qiita.com/kiris/items/b043a7582c22110d7097#project-rootbuildsbt のように、sbtの機能で書いてもいいんだけれど、まあpomExtraで全部書いたらいいかという気分になったので、そのようにしている。

sbt-sonatypeでリリースをする

ここまで設定できたら後はリリースするだけ。以下のコマンドを叩くと複数のScalaのバージョンでコンパイルしてリリースできる。https://github.com/xerial/sbt-sonatype

$ sbt
> + clean
> + compile
> + test
> + publishSigned
> sonatypeReleaseAll

ここで注意するのは、複数Scalaのバージョンでリリースしたいからといって+ sonatypeReleaseを実行しないこと。これを行うとなんかめちゃくちゃになって、sonatypeからISEが返ってくるようになってしまい、結局手動でsonatypeを見に行って変なやつをcloseするという作業が必要になった。

ロスコンパイル環境では、sonatypeReleaseAllを使うと良いということだった。

初回リリースしたらProject Issueにコメント

初回リリースしたら、Central syncをアクティブにしてもらうために、「Sonatype JIRAにNew Project Issueを作成する」で作ったIssueにコメントする。https://issues.sonatype.org/browse/OSSRH-38391?focusedCommentId=469046&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-469046 の感じ。

リリース確認

あとはMaven Central Repositoryで検索して、自分のライブラリがアップロードされていることが確認できたらOK。反映には少し時間がかかったので気長に待つと良い。僕はこんな感じで複数Scalaバージョンでリリースできてたので大丈夫そうだった。


これでひとまず作業終了。お疲れ様でした。

【オプション】sbt-releaseによるリリース自動化

あとは次回リリースに備えて、gitのタグ打ちとかいろんなものを自動化しておきたかったので、sbt-releaseの設定をしておいた。

まずproject/plugins.sbtにプラグインの依存を追加。

addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.8")

続いてbuild.sbtの変更。https://github.com/sbt/sbt-release によると、バージョン情報はversion.sbtに記録されるということなので、build.sbtからはversion := "0.0.1"の記述を削除しておく。その後build.sbtに以下のように追記。

// Release
import ReleaseTransformations._

releaseCrossBuild := true // true if you cross-build the project for multiple Scala versions
releaseProcess := Seq[ReleaseStep](
  checkSnapshotDependencies,
  inquireVersions,
  runClean,
  runTest,
  setReleaseVersion,
  commitReleaseVersion,
  tagRelease,
  // For non cross-build projects, use releaseStepCommand("publishSigned")
  releaseStepCommandAndRemaining("+publishSigned"),
  setNextVersion,
  commitNextVersion,
  releaseStepCommand("sonatypeReleaseAll"),
  pushChanges
)

あとはsbt releaseするだけ!これでcompileやtest、gitのタグ打ち、リリースなど全部一気にやってくれる。

$ sbt
> release

最終的なbuild.sbtはhttps://github.com/shibayu36/joda-time-fake-scala/blob/v0.0.2/build.sbt