たごもりすメモ

コードとかその他の話とか。

GradleでMaven Centralにライブラリを公開する

前にこんなエントリを書いたが、このときはmavenから実行してた。今回はgradleから。参考にしたエントリはいくつかあったけど、ずばりというのがなくてつらかった。あれこれ試して組み合わせた。

はじめてのmaven central 公開 - たごもりすメモ

あとMaven Central(というかSonaType OSS)もちょっと変わってるのでそのへんも。なお環境は適当なMacに適当にgradleを入れた。以上。

前と変わらないところ

  • GPGのセットアップ
  • パッケージ名前空間を決める
  • アカウント登録

パッケージ名前空間は今回は前のと違ったので、申請は今回もやった。前と同じようにやってさくっと完了。

gradle環境の設定

"$HOME/.gradle/gradle.properties" に行う。

org.gradle.daemon=true
signing.keyId=DEADBEEF
signing.password=mypassphrase_for_gpgkey
signing.secretKeyRingFile=/Users/tagomoris/.gnupg/secring.gpg
sonatypeUsername=tagomoris
sonatypePassword=mypassword_for_sonatype

最初の1行は普通の便利設定で、他はJarの公開用。
パスワードやパスフレーズを毎回コンソールで聞かせる設定とかにする流儀もあるみたいだけど、とりあえずパス。ベタ書き。ここに書いておけばgradleの maven plugin が読んでくれる。

build.gradle の記述

がんばった。全体はここにあるので、ポイントをいくつか抜き出して書く。

apply plugin: "java"
apply plugin: "application"
apply plugin: "maven"
apply plugin: "signing"

group = "net.isucon.bench"
archivesBaseName = "isucon-bench"
version = "0.1.1"
description = "Benchmark framework for Web applications, especially for ISUCON"

java と application プラグインはビルドと実行用のもの、maven と signing がmaven centralでの公開用のもの。その下はパッケージのメタデータ宣言用。あとで使う。

maven-publish プラグインというのもあるみたいだったが、あれこれ試したところそれは自分で maven repository server を作って運用するようなときに、そこにpushするためのものっぽい。code signingと組み合わせるとうまく動かないという話が多くて、自分もやってみたところどうもダメな感じだった。なんかものすごい色々コード書けばできる的な話もあったけど、何のためにプラグイン使ってんのか分からなくなる的な展開が見えて避けた。

//set build variables based on build type (release, continuous integration, development)
def isDevBuild
def isCiBuild
def isReleaseBuild
def sonatypeRepositoryUrl
if (hasProperty("release")) {
    isReleaseBuild = true
    sonatypeRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
} else if (hasProperty("ci")) {
    isCiBuild = true
    version += "-SNAPSHOT"
    sonatypeRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
} else {
    isDevBuild = true
    version += "-SNAPSHOT"
}

repositories {
    mavenCentral()
}

普通にビルドしただけでSonaTypeに公開されたらたまらんのでリリース作業用の設定を明示的に行うように変更。CIの場合はSNAPSHOTを公開用とは別の場所に上げるようにしている。まあCIまわしてないんだけど……他で(ちゃんとやるケースで)真似したくなったときのために。

dependency, compileJava, mainClassName, run あたりは普通に java, application なビルド/実行のためのものなので割愛。javadocJar, sourcesJar, artifacts は見ればわかるので割愛。

signing {
    required { isReleaseBuild }
    sign configurations.archives
}

uploadArchives {
    repositories {
        if (isDevBuild) {
            mavenLocal()
        }
        else {
            mavenDeployer {
                if(isReleaseBuild) {
                    beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
                }

                repository(url: sonatypeRepositoryUrl) {
                    authentication(userName: sonatypeUsername, password: sonatypePassword)
                }

                pom.project {
                    name 'isucon-bench'
                    packaging 'jar'
                    description 'Benchmark framework for Web applications, especially for ISUCON'
                    url 'http://www.example.com/example-application'

                    scm {
                        url "scm:git@github.com:isucon/isucon-bench-java.git"
                        connection "scm:git@github.com:isucon/isucon-bench-java.git"
                        developerConnection "scm:git@github.com:isucon/isucon-bench-java.git"
                    }
                    licenses {
                        license {
                            name 'MIT'
                            url 'https://opensource.org/licenses/MIT'
                        }
                    }
                    developers {
                        developer {
                            id 'tagomoris'
                            name 'Satoshi Tagomori'
                            email 'tagomoris@gmail.com'
                        }
                    }
                }
            }
        }
    }
}

このへんも見ればだいたいわかるが、丸っと書いてあるページがなくて難儀した。開発時ビルドにはスキップする設定で、書名とかパッケージのメタデータとかが書いてある。

この設定を書いておいたら、あとはコマンドでは "gradle clean && gradle uploadArchives -Prelease" すればSonaTypeのリポジトリにアップロードまでやれる。単に uploadArchives するとなんか変なゴミが含まれたJarが作られてしまった気がしたのでいちおう今のところはこうしている。それは不要だとか、綺麗なJarを一発で作る方法があったら教えてほしい。

まあなんにしろ、これでうまくいく。

$ gradle clean && gradle uploadArchives -Prelease
:clean

BUILD SUCCESSFUL

Total time: 0.863 secs
:compileJava
注意:入力ファイルの操作のうち、未チェックまたは安全ではないものがあります。
注意:詳細は、-Xlint:uncheckedオプションを指定して再コンパイルしてください。
:processResources UP-TO-DATE
:classes
:jar
:startScripts
:distTar
:distZip
:javadocJar
:sourcesJar
:signArchives
:uploadArchives
Could not find metadata net.isucon.bench:isucon-bench/maven-metadata.xml in remote (https://oss.sonatype.org/service/local/staging/deploy/maven2/)

BUILD SUCCESSFUL

Total time: 1 mins 3.332 secs

なんか通知ぽいものが出ているが、これはSonaTypeのリポジトリが新しくなって以降出る(ことがある)ようになったもの。とりあえず無視してよい。

SonaType Nexus Repositoryでの手続き

これが変わったところ。先程のコマンドでSonaTypeのリポジトリには格納された(stagingになった)はずだが、そこから先の処理をワンステップやらないといけない。新しいWeb UI (Nexus Repository)が登場する。
なお以下のページに案内がある。スクリーンショットとかあって前よりだいぶ分かりやすいので、読むとよい。

Releasing the Deployment

で、具体的にはこのページを開く: https://oss.sonatype.org/index.html#stagingRepositories

認証は作成してあるSonaTypeのアカウントで通れる。(開いてなければ)左側のメニューから "Staging Repositories" をクリックすると、いろいろ見られるリストの中に自分がアップロードしたっぽい名前があることを確認する。

https://i.gyazo.com/d6a722eb1925c3009627bceed2b8f549.png

選択すると上のメニューで "Close" と "Drop" のボタンが有効になるはず。なったら "Close" をクリックして、ダイアログから "confirm" する。このときパッケージになにか問題があるとエラーになるらしい(なったことがない)。問題がなければ該当の staging repository の状態が closed になる。問題なければそのまま "Release" ボタンが有効になるので、押してリリース。

完了したらリポジトリの管理チケットに完了したよとコメントを書くと、staging -> release の手続きの自動化を有効にしてもらえて、一件落着。リリースされたら、Nexus Repository内でパッケージ名で検索したとき、該当バージョンが releases 下にあるように表示されるからわかるはず。

完了!

なお最近試したところ Maven Central の検索画面に出てくるようになるまではだいぶ時間がかかった。が、その状態で pom.xml に書いてみたところ正常にダウンロードできたので、あまり気にする必要はなさそう。
あと一度自動リリースが有効になったリポジトリで staging を手動で close してみたら、なんか変なことになったかも。stagingから出ていかなくなってしまった。焦らず落ち着いて様子を見よう。

なんにしろ、これでできた。やってしまえばなんとかなる。便利。