Maven Central Repository への公開手順(Gradle版)


はじめに

Maven Central Repository へライブラリを登録する手順です。

意外と古い情報しか見当たらなく、少し面倒な作業なのでこちらで紹介します。

大きくは、以下の流れになります。

  1. sonatype の JIRA で issue を通してリポジトリ作成を依頼
  2. GnuPG で jar を署名できる環境を作成
  3. Gradle プラグインでリポジトリへ登録
  4. Repository Manager で Maven Central Repository へリリース

順に見ていきましょう。


sonatype で JIRAアカウント作成

Maven リポジトリへの登録権限は sonatype の JIRA でプロジェクト作成の issue を通して行う。

最初に issue 登録する必要があるので、以下から Sign up する。

https://issues.sonatype.org/secure/Signup!default.jspa

初回ログイン時には、アバター選択や言語の選択ができるので、適宜設定する。

その後、プロジェクト作成を依頼する issue を新規登録する。


New Project の issue 登録

ダッシュボードから「作成」を選択して新しい issue 登録ダイアログを開く

登録内容は以下のような内容で行う。

項目 入力
プロジェクト Community Support - Open Source Project Repository Hosting を選択
課題タイプ New Project を選択
要約(Summary) Github のプロジェクト名などを入力
説明(Description) Github の README の内容などを入力
Group Id 保有ドメインなどから Group Id を指定(com.github.xxx などGithub のURLでも可)
Project URL GitHub のプロジェクト URL
SCM url GitHub の Clone URL
Username(s) 空でOK(Group Id に対して権限を共有したい JIRA アカウント)
Already Synced to Central No

Group Id に 自身のドメインを指定した場合は、JIRA チケットのIDを DNSの TXT レコードに登録することで、ドメインの所有権を確認する流れになる。

ドメインの所有権の確認などが完了すれば、以下のような流れでリポジトリマネージャが利用できるようになる。

リポジトリマネージャ には、JIRA の登録アカウントでログインできる。


GnuPG の導入

Maven リポジトリへの登録には GnuPG などにより署名付きの jar が必要になる。

GnuPG は macOS の場合は brew で以下のように導入できる(gnupg2, gpg2 などとせずとも現在は gnupg の指定で2系がインストールされる)。

$ brew install gnupg

Windows の場合や brew を使わない場合などはGnuPGから直接インストーラを入手することもできる。

インストール後、バージョンを確認。

$ gpg --version
gpg (GnuPG) 2.2.19
libgcrypt 1.8.5
Copyright (C) 2019 Free Software Foundation, Inc.


GnuPG による鍵の生成

GnuPG は公開鍵暗号方式となるため、秘密鍵と公開鍵を生成し、公開鍵をキーサーバに登録する。

GnuPG は LANG 設定で文字化けするケースがあるので、LANG=C など指定して鍵生成を行うと良い。

$ LANG=C gpg --full-gen-key

鍵生成時には各種オプションの入力を求められる。

gpg (GnuPG) 2.2.19; Copyright (C) 2019 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
  (14) Existing key from card
Your selection?
...

以下のような選択を行う。

項目 選択
Please select what kind of key you want (1) RSA and RSA (default) [デフォルト]
RSA keys may be between 1024 and 4096 bits long (2048) [デフォルト]
Please specify how long the key should be valid 0 = key does not expire [デフォルト]
Real name 氏名
Email address メールアドレス
Passphrase 鍵のパスフレーズ

パスフレースの登録は以下のような入力画面になる(pinentry-mac を使うようにした場合はGUIダイアログが出る)。

入力が終わると以下のように鍵生成が完了する。

gpg: /Users/XXXXX/.gnupg/trustdb.gpg: trustdb created
gpg: key D352XXXXX marked as ultimately trusted
gpg: directory '/Users/XXXXX/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/Users/XXXXX/.gnupg/openpgp-revocs.d/AF7C1EXXXXX.rev'
public and secret key created and signed.

pub   rsa2048 20XX-XX-XX [SC]
      AF7C1EXXXXX
uid                      Your name <your email address>
sub   rsa2048 20XX-XX-XX [E]


--list-secret-keys で秘密鍵を確認。

$ LANG=C gpg --list-secret-keys
/Users/XXXXX/.gnupg/pubring.kbx
----------------------------------
sec   rsa2048 20XX-XX-XX [SC]
      AF7C1EXXXXX
uid           [ultimate] Your name <your email address>
ssb   rsa2048 20XX-XX-XX [E]

--list-keys で公開鍵を確認。

$ LANG=C gpg --list-keys
/Users/XXXXX/.gnupg/pubring.kbx
----------------------------------
pub   rsa2048 20XX-XX-XX [SC]
      AF7C1EXXXXX
uid           [ultimate] Your name <your email address>
sub   rsa2048 20XX-XX-XX [E]


公開鍵を鍵サーバへ登録

--send-keys で生成した公開鍵を鍵サーバへ送信する。

$ gpg --send-keys AF7C1EXXXXX
gpg: 鍵D35XXXXXをhkps://hkps.pool.sks-keyservers.netへ送信

なお、--keyserver オプションを指定する例が良くあるが、このオプションは deprecated になっている。 現在は送信サーバの指定は ~/.gnupg/dirmngr.conf で行うが、デフォルトで鍵サーバープールが使われるようなので指定は不要。

登録されたことを --search-keys で確認する。

$ gpg --search-keys AF7C1EXXXXX
gpg: data source: https://82.148.229.254:443
(1) Your name <your email address>
      2048 bit RSA key D352XXXXX, 作成: 20XX-XX-XX
Keys 1-1 of 1 for "AF7C1EXXXXX".  番号(s)、N)次、またはQ)中止を入力してください >Q


秘密鍵をエクスポート

後述の signingプラグインで使用するため --export-secret-keys で秘密鍵をファイルにエクスポートしておく。

$ LANG=C gpg --export-secret-keys -o ~/.gnupg/secring.gpg

また、ショート形式の鍵IDが必要になるため --keyid-format short を指定して表示する。

$ LANG=C gpg --list-secret-keys --keyid-format short
/Users/XXXXX/.gnupg/pubring.kbx
----------------------------------
sec   rsa2048/812DXXXX 20XX-XX-XX [SC]
      AF7C1EXXXXX
uid         [ultimate] Your name <your email address>
ssb   rsa2048/DB68XXXX 20XX-XX-XX [E]

ここで表示された 812DXXXX が鍵IDなので控えておく。


Gradle プラグインの導入

ビルド時の jar を署名するための signing プラグイン と、Maven リポジトリ公開用の maven-publish プラグイン を導入する。

plugins {
  // ...
  id 'signing'
  id 'maven-publish'
}

プロジェクトの gradle.properties ではなく、ユーザホームの ~/.gradle/gradle.properties に鍵の情報と sonatype へのアップロード用の設定を定義する。

signing.keyId=812DXXXX
signing.password=鍵作成時のパスフレーズ
signing.secretKeyRingFile=/Users/XXXXX/.gnupg/secring.gpg

sonatypeUsername=sonatypeアカウント
sonatypePassword=sonatypeパスワード

signing.keyId には先程確認したショート形式の鍵IDを指定する。


build.gradle の設定

プラグイン用の設定を build.gradle に定義する。

plugins {
  // ...
    id 'maven-publish'
    id 'signing'
}

// ...

group = 'Your Group Id'
version = 'X.X.X'

java {
    withJavadocJar()
    withSourcesJar()
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            artifactId = 'プロジェクト名'
            from components.java
            pom {
                name = 'プロジェクト名'
                description = 'プロジェクトの説明'
                url = 'プロジェクトURL(GithubのURL)'
                licenses {
                    license {
                        name = 'The Apache License, Version 2.0'
                        url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                    }
                }
                developers {
                    developer {
                        id = 'Your ID'
                        name = 'Your name'
                        email = 'Your email address'
                    }
                }
                scm {
                    connection = 'git@github.com:XXXXX.git'
                    developerConnection = 'git@github.com:XXXXX.git'
                    url = 'https://github.com/XXXXX'
                }
            }
        }
    }
    repositories {
        maven {
            def releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2"
            def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots"
            url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
            credentials {
                username = "${sonatypeUsername}"
                password = "${sonatypePassword}"
            }
        }
    }
}

signing {
    sign publishing.publications.mavenJava
}

javadoc {
    if (JavaVersion.current().isJava9Compatible()) {
        options.addBooleanOption('html5', true)
    }
}

上記定義が済めば、maven-publishpublish タスクによりリポジトリへの登録ができる。

Gradle Kotlin DSL の場合は build.gradle.kts で以下のように定義する。

plugins {
    // ...
    `maven-publish`
    signing
}

// ...

group = "Your Group Id"
version = "X.X.X"


val sonatypeUsername: String? by project
val sonatypePassword: String? by project

java {
    withJavadocJar()
    withSourcesJar()
}

publishing {
    publications {
        create<MavenPublication>("mavenJava") {
            artifactId = "プロジェクト名"
            from(components["java"])
            versionMapping {
                usage("java-api") {
                    fromResolutionOf("runtimeClasspath")
                }
                usage("java-runtime") {
                    fromResolutionResult()
                }
            }
            pom {
                name.set("プロジェクト名")
                description.set("プロジェクトの説明")
                url.set("プロジェクトURL(GithubのURL)")
                licenses {
                    license {
                        name.set("The Apache License, Version 2.0")
                        url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
                    }
                }
                developers {
                    developer {
                        id.set("Your ID")
                        name.set("Your name")
                        email.set("Your email address")
                    }
                }
                scm {
                    connection.set("git@github.com:XXXXX.git")
                    developerConnection.set("git@github.com:XXXXX.git")
                    url.set("https://github.com/XXXXX")
                }
            }
        }
    }
    repositories {
        maven {
            val releasesRepoUrl = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2")
            val snapshotsRepoUrl = uri("https://oss.sonatype.org/content/repositories/snapshots")
            url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
            credentials {
                username = sonatypeUsername
                password = sonatypePassword
            }
        }
    }
}

signing {
    sign(publishing.publications["mavenJava"])
}

tasks.javadoc {
    if (JavaVersion.current().isJava9Compatible) {
        (options as StandardJavadocDocletOptions).addBooleanOption("html5", true)
    }
}


リポジトリへの公開

リポジトリへは、Staging リポジトリへ公開し、問題がなければ正式リリースを行う流れになる。

publish タスクでリポジトリへ公開する。

$ ./gradlew publish

ビルドが完了すれば、リポジトリマネージャの「Staging Repositories」から照会できる。

当該のリポジトリを選択し、「Close」を押すと Staging 状態となる。

Staging 状態の Summary タブにある URLを以下のように指定することができる。

内容を確認して問題なければ「Release」ボタンでリリースできる。

repositories {
    jcenter()
    maven { url "https://oss.sonatype.org/content/repositories/XXXXX-1001" }
}

JIRA のチケットに「please comment on this ticket when you promoted your first release, thanks」とあるように初回リリースの完了時にコメントしておく。

2時間もすれば https://search.maven.org の検索結果に乗るようになる。


まとめ

Maven Central Repository へのライブラリ公開手順を紹介しました。

初回は少し面倒で敷居が高いのですが、2回目以降からは複雑な手順はありませんので、自作ライブラリをオープンソースとして公開してみては如何でしょうか?



Gradle in Action

Gradle in Action

Amazon