Gradle Plugin を公開する (Gradle Plugin Portal)

> 2023/04/05 Gradle 8.0.2 の内容に更新しました

[Gradle Plugin Portal](https://plugins.gradle.org/) へプラグインをアップロードし、公開する手順を解説します。

* Gradle Kotlin DSL (*.kts) 前提です
* Gradle 8.0.2, `com.gradle.plugin-publish` 1.2.0 で確認しています

この記事は、以下の公式ドキュメントの内容をまとめたものです。

https://docs.gradle.org/8.0.2/userguide/publishing_gradle_plugins.html

# Gradle Plugin Portal

プラグインは Gradle Plugin Portal へアップロードします。Gradle Plugin Portal でプラグインを公開すると、利用者はプラグイン ID を指定するだけでプラグインを利用できるようになります。

## Gradle Plugin Portal へのアカウント登録

https://plugins.gradle.org/user/login から Gradle Plugin Portal へアカウントを登録します。

`Sign up` から、メールアドレス・パスワードでの登録もできますが、`Github` から Github アカウントでの新規登録・ログインをおすすめします。`io.github.` 形式のプラグイン ID を使うためには Github アカウント紐付けが必要になります。プラグイン ID に独自ドメインを使用する場合はメールアドレス・パスワードでの登録でも構いません。

すでに Gradle Plugin Portal へメールアドレス・パスワードで登録してしまった場合でも、Github アカウントのメールアドレスと一致していれば、この画面から Github 認証でログインし直すだけで既存のアカウントとの紐付けが可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17931/3eea9750-9cdd-6c9b-9388-99269da94a25.png)

# Gradle Plugin を開発する

最新の Gradle の機能を使った Gradle Plugin 開発用のテンプレートリポジトリを用意しました。こちらのリポジトリの `Use this template` からリポジトリを複製して開発をスタートすると楽です。

このテンプレートは Android 向けの Gradle Plugin 開発を想定していますが、`sample` モジュール以外には Android 要素はなく、非 Android 向けのプラグイン開発でも利用できます。

以降の説明ではこのテンプレートプロジェクトを利用している前提で説明します。

https://github.com/irgaly/android-gradle-plugin-template

プラグインの開発は以下のドキュメントなどを参考としてください。

https://docs.gradle.org/current/userguide/custom_plugins.html#sec:mapping_extension_properties_to_task_properties

## Gradle Plugin の公開設定をする

プラグインをリリースできるところまで開発したら、`plugin-publish` を設定します。

`plugin/build.gradle.kts`

```kotlin
plugins {
    // ...
    id("com.gradle.plugin-publish") version "1.1.0"
    signing
}

// ...

group = "io.github.{user}.{plugin name}"  // Maven repository 上の groupId
version = "0.1.0"

gradlePlugin {
    // Gradle Plugin Portal 上に表示する情報
    website.set("https://github.com/example/example")
    vcsUrl.set("https://github.com/example/example")
    plugins {
        create("plugin") { // 設定名はローカルでしか使われないのでユニーク文字列ならなんでもよい
            id = "io.github.{user}.{plugin name}" // プラグイン ID
            displayName = "Sample Plugin"
            description = "A Sample Plugin"
            tags.set(listOf("example"))
            implementationClass = "org.sample.GreetingPlugin"
        }
    }
}

java {
    // Gradle Plugin Portal へソースコードもアップロードするために必要
    withSourcesJar()
    withJavadocJar()
}

signing {
    useInMemoryPgpKeys(
        providers.gradleProperty("signingKey").orNull,
        providers.gradleProperty("signingPassword").orNull
    )
    /*
    // 環境変数から読み込むときはこちら
    useInMemoryPgpKeys(
        providers.environmentVariable("SIGNING_PGP_KEY").orNull,
        providers.environmentVariable("SIGNING_PGP_PASSWORD").orNull
    )
     */
}
```

`group` は、Maven repository 上の groupId を指定します。`groupId:artifactId:version` の `groupId` です。[Maven Coordinates](https://maven.apache.org/pom.html#maven-coordinates) の仕様通りに設定します。プラグイン ID と合わせて `io.github.{user}.{plugin name}` のように設定するのが無難です。

プラグイン ID は https://plugins.gradle.org/docs/publish-plugin#approval のルールに従って設定します。`{domain}.{plugin name}` の形式となりますが、domain 部分は独自ドメインか `io.github.{user}` を利用できます。独自ドメインを利用する場合は Gradle Plugin Portal に登録したメールアドレスのドメイン部分と一致している必要があります。メールアドレスのドメイン部分と異なる独自ドメインを利用する場合は DNS へ TXT レコードを追加することでドメインの所有証明が必要になります。`io.github.{user}` は、Gradle Plugin Portal のアカウントを Github アカウントと紐付けすることで利用可能です。`{plugin name}` の命名は自由ですが、利用者が使用しやすいように短くシンプルな名前が好まれます。たとえばプラグイン ID は Gradle Plugin であることは明白であるため、 `io.github.{user}.foo-feature-gradle-plugin` とか `io.github.{user}.gradle.foo` は冗長です。ドメイン部分によって他の人と ID が被る心配はないため、`io.github.{user}.foo` のように思い切った命名がおすすめです。

プラグインの審査のために `description`, `tags`, `website`, `vcsUrl` の指定は必須とされています。 https://plugins.gradle.org/docs/publish-plugin#approval

メタデータは Gradle Plugin Portal 上で以下のように表示されます。

参考プラグイン: https://plugins.gradle.org/plugin/com.gradle.plugin-publish

![gradle plugin metadata](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17931/d0f13e3b-7be3-c9a1-9018-a57082510b47.png)

`signing` の `useInMemoryPgpKeys` はプラグインの署名設定です。ここでは gradle.properties または環境変数に ascii-armored key 形式で指定しています。

## 署名設定

利用者の安全のために、Gradle Plugin には署名をしておくことをおすすめします。上記設定で `useInMemoryPgpKeys` を設定しておくと、Gradle Plugin の成果物に署名情報の .asc ファイルが含まれるようになります。

PGP 鍵が必要です。PGP 鍵についてや、Gradle signing Plugin の説明は公式ドキュメントを確認してください。

https://docs.gradle.org/current/userguide/signing_plugin.html

`useInMemoryPgpKeys` により PGP 鍵を ascii-armored keys 形式で使用することができます。PGP 鍵を生成済みであれば、gpg コマンドの `--export-secret-keys` により ascii-armored keys 形式で取り出すことができます。

```shell
% gpg --export-secret-keys --armor ... 
```

Key と Password は ~/.gradle/gradle.properties などに設定しておきましょう。Gradle Property は `providers.gradleProperty(...)` で読み込むことができます。

`~/.gradle/gradle.properties`

```properties
signingKey="-----BEGIN PGP PRIVATE KEY BLOCK-----\
\
...\
-----END PGP PRIVATE KEY BLOCK-----\
"
signingPassword=...
```

# Gradle Plugin Portal へアップロードする

Gradle Plugin Portal のユーザーページの `API Keys` に記載されている API Key と API Secret を、`~/.gradle/gradle.properties` へ設定します。gradle.properties へ設定せずに、コマンドラインから API Key と API Secret を渡すことも可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17931/548c544e-b531-c4a1-cb55-290e968df3c8.png)

`~/.gradle/gradle.properties`

```properties
gradle.publish.key=...
gradle.publish.secret=...
```

以下のコマンドで Gradle Plugin Portal へプラグインをアップロードします。

```shell
% ./gradlew :plugin:publishPlugins
```

コマンドラインから API Key と API Secret を渡す場合は以下のようにします。

```shell
% ./gradlew :plugin:publishPluginns -Pgradle.publish.key=<key> -Pgradle.publish.secret=<secret>
```

環境変数から API Key と　API Secret を渡す場合は `GRADLE_PUBLISH_KEY` と `GRADLE_PUBLISH_SECRET` を設定します。

```shell
% export GRADLE_PUBLISH_KEY=<key>
% export GRADLE_PUBLISH_SECRET=<secret>
% ./gradlew :plugin:publishPluginns
```

アップロードが完了すると、そのプラグインの初回リリースでは Pending Approval となり、審査待ちになります。審査には2~3日程度(平日なら1~2日程度？)かかり、プラグイン ID などが適切であるかを確認されるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17931/6d8997bf-9785-a5ce-afae-ae7db60ecf36.png)

審査が完了するとメールで連絡が来ます。Reject の場合はメールの件名は「Plugin Portal Reject
」、Approve の場合は「[Gradle] Plugin approved」でした。審査を通過していれば、Pending Approval の表示が消えてプラグインが公開されます。

初回の審査を通過したプラグインをアップデートする場合には審査はありません。アップロード完了と同時に最新バージョンが公開されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17931/54d690dc-cab7-bd74-62e1-e4b0ec43c7b2.png)


## Gradle Plugin Portal へのアップロード内容を確認する(ローカル)

以下のコマンドでローカルへデプロイすることでアップロードされるファイルを確認できます。コマンドを実行後にローカルの `~/.m2/repository` へデプロイされたファイルを確認してください。

https://docs.gradle.org/current/userguide/plugins.html#sec:plugin_markers に記載のとおり、プラグインマーカーとして `{plugin ID}:{plugin ID}.gradle.plugin:{version}` も同時にデプロイされます。

```shell
% ./gradlew :plugin:publishToMavenLocal
# プラグインマーカーとプラグインのどちらもがデプロイされる
```

以下は、`group = "org.sample"` `plugin id = "org.sample.plugin"` としたときのデプロイ内容です。`org.sample:plugin:0.1.0` と `org.sample.plugin:org.sample.plugin.gradle.plugin:0.1.0` がデプロイされています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17931/f7bc298d-31a8-a446-c5cb-fe1c9db536b1.png)

## Gradle Plugin Portal へのアップロード内容を確認する(Gradle Plugin Portal)

プラグインが Approve されて公開されている場合は、https://plugins.gradle.org/m2/ から、アップロードされたファイルを確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17931/ddc80b41-6878-9b9b-dc9d-2e781fd36335.png)

# プラグインを利用する

プラグインが審査に通り、公開されれば、以下の設定でプラグインを利用できます。

`settings.gradle.kts`

```kotlin
pluginManagement {
    repositories {
        // ...
        gradlePluginPortal()
    }
｝
```

`app/build.gradle.kts`

```kotlin
plugins {
    id("org.sample.plugin") version "0.1.0" // プラグイン ID とバージョンを指定する
}
```

# プラグインの削除

https://plugins.gradle.org/docs/deleting にプラグインの削除についての説明があります。

プラグインを誤って公開してしまった場合などは、該当のバージョンをアップロードから7日間までは自分で削除できます。プラグインの Web サイトから、`Delete version` ボタンを押せば削除できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17931/363ce360-ae20-b2ec-4c9a-6caf299e08cb.png)

該当バージョンのアップロードから7日以上経過すると `Delete version` ボタンは消えて、自分では削除できなくなります。

一度公開したプラグインは削除せずにバージョンアップリリースをすべきですが、セキュリティ上の重大な問題があるなどの特殊な事情があれば https://plugins.gradle.org/docs/get-help からサポートへ連絡を取ることでプラグインの削除を要請することができます。