カスタムGradleプラグインをGradle Plugin Portalに公開する手順を詳しく説明します

わたしが書いた [Publishing Custom Gradle Plugin explained step by step](https://kazurayam.github.io/PublishingCustomGradlePlugin_StepByStep/)　の日本語版です。

## はじめに

この記事のサンプルコードを格納したGitHubレポジトリがあります。URLは下記の通り。

-   <https://github.com/kazurayam/PublishingCustomGradlePlugin_StepByStep>

この記事を書くのに Gradle 8.0.2, Java 17.0.7, macOS 12.6　を使いました。


### 要旨

本記事でわたしはカスタムGradleプラグインをGradle Plugin Portalで公開するまでの作業手順を説明します。サンプルコード一式を提供します。

わたしはGradle公式ドキュメントのうち下記ページを読むことから始めました。

-   [Developing Custom Gradle Plugins](https://docs.gradle.org/current/userguide/custom_plugins.html)

公式ドキュメンメントには詳細な情報が山ほど書いてあります。わたしは公式ドキュメントを読み漁り、コードを書いて試すこともしたがよくわからなかった。私見ですが、公式ドキュメントには表側のことすなわちbuild.gradleスクリプトの書き方とコマンドの叩き方が書いてあるが、説明が不十分だ。裏側のことすなわちbuild.gradleスクリプトを記述してコマンドを実行した結果としてどういうファイルが生成されるのか、生成されたファイルに何が書いてあるかをまったく説明しない。だからドキュメントから得られた情報を頭の中で組み立ててひとつのシステムとしてイメージすることがむずかしかった。

そこでわたしは自作したカスタムGradleプラグインをGradle Plugin Portalに公開する手順を８つの段階に分けた。ひとつの段階でひとつの疑問に対する答えを導き出すことに集中した。各ステップにおいて `java-gradle-plugin` と `maven-publish` と `com.gradle.plugin-publish` プラグインが `<projectDir>/build` ディレクトリの中にどんな物（ディレクトリとファイル）を生成するのかを検証した。さまざまのコマンドを実行する都度、`<projectDir>/build`ディレクトリの中に生成されたファイルのツリーを眺め、ファイルをエディタで開き内容を理解しようとした。

段階的な分析の成果としてわたしが理解したことをこの記事で説明します。Gradleの公式ドキュメントの森の中を彷徨っている誰かの助けになればいいなと願って。

## step1 はじめにGroovyコードありき

`step1`という名前のGradleプロジェクトを作り、その中にカスタムGradleプラグインとなるはずのGroovyコードを書きました。

### settings.gradle と build.gradle

[レポジトリ](https://github.com/kazurayam/PublishingCustomGradlePlugin_StepByStep)　のルートディレクトリの下に `step1` ディレクトリを作り、Gradleプロジェクトとして必須であるファイル群を作った。 `step1/settings.gradle` と `step1/build.gradle`　を作った。

#### step1/settings.gradle

    rootProject.name = 'step1'

#### step1/build.gradle

    plugins {
        id 'groovy'
    }

    group = 'com.example'
    version = '1.0'

    repositories {
        mavenCentral()
    }

    dependencies {
        // Use the awesome Spock testing and specification framework
        testImplementation 'org.spockframework:spock-core:2.3-groovy-3.0'
    }

    tasks.named('test') {
        // Use JUnit Jupiter for unit tests.
        useJUnitPlatform()
    }

### カスタムGradleプラグインのGroovyコード

Groovyコード  `step1/src/main/groovy/com/example/greeting/GreetingPlguin` を書いた。

    package com.example.greeting

    import org.gradle.api.Plugin
    import org.gradle.api.Project

    class GreetingPlugin implements Plugin<Project> {
        void apply(Project project) {
            project.task('hello') {
                doLast {
                    println "Hello from GreetingPlugin"
                }
            }
            project.task('goodbye') {
                doLast {
                    println "Goodbye from GreetingPlugin"
                }
            }
        }
    }

この　`com.example.greeting.GreetingPlugin` プラグインをGradleビルドに適用すると`hello`タスクと`goodbye`タスクが挿入される。helloタスクを実行してみよう。こうなる。

```
$ gradlew hello
Hello from GreetingPlugin
```

つまらないものですがカスタムGradleプラグインです。

### JUnit5によるテスト

テストコード `step1/src/test/groovy/com/example/greeting/GreetingPluginTest.groovy` も書いた。

    package com.example.greeting

    import org.gradle.api.Project
    import org.gradle.testfixtures.ProjectBuilder
    import spock.lang.Specification

    class GreetingPluginTest extends Specification {
        def "the plugin registers the tasks"() {
            given:
            Project project = ProjectBuilder.builder().build()

            when:
            project.plugins.apply("org.sample.Greetings")

            then:
            project.tasks.findByName("hello") != null
            project.tasks.findByName("goodbye") != null

        }
    }

本記事は全部で８つの段階から構成されますが、Groovyコードはほとんど変更しません。上記のコードによって出来るPluginをどうやってパブリッシュするのか、その手順を分析することに集中します。


### ビルドを実行すると何が起きるか

ビルドを実行してGroovyコードをコンパイルしようとした。しかし失敗した。

![1 1.GradleApiNotAccessible](https://kazurayam.github.io/PublishingCustomGradlePlugin_StepByStep/images/1_1.GradleApiNotAccessible.png)

GroovyコードがGradle APIに属するclass（たとえば `prg.gradle.api.Project`）を参照しているが、コンパイラがGradle APIにアクセスできなかったのでエラーになった。

### 何をしなければならないのか

このGroovyコードをコンパイルするためには [`java-gradle-plugin`](https://docs.gradle.org/current/userguide/java_gradle_plugin.html) プラグインをこの build.gradle に適用する必要がある。次のstep2でそれをします。

## step2 java-gradle-pluginプラグインを適用する

[`java-gradle-plugin`](https://docs.gradle.org/current/userguide/java_gradle_plugin.html) プラグインをビルドに適用します。これによってGradle APIがアクセス可能になってコンパイルできるようになる。この段階で `step2/build` ディレクトリの下にどんなファイルが生成されるかを観察する。特に`java-gradle-plugin`が `step2/build/pluginDescriptor` を生成することに注目する。

### settings.gradle と build.gradle

`step2/settings.gradle` ファイルと `step2/build.gradle` ファイルを作った。内容は下記の通り。

#### step2/settings.gradle

    rootProject.name = 'step2'

#### step2/build.gradle

リンク [step2/build.gradle](https://github.com/kazurayam/PublishingCustomGradlePlugin_StepByStep/blob/develop/step2/build.gradle) をクリックしてコードを参照してください。

特に3行目に注目。`java-gradle-plugin`プラグインをこのビルドに適用している。

        id 'java-gradle-plugin'

24行目の `plugins` クロージャにも注目。ここで `plugin id` を宣言し、カスタムGradleプラグインを実装しているclassのFully Qualified Nameを宣言している。

        // Define the plugin
        plugins {
            MyGreeting {
                id = 'org.sample.Greetings'
                implementationClass = 'com.example.greeting.GreetingPlugin'
            }
        }
    }

ここで注目してほしいのが、plugin id と実装classのパッケージ名が違っていてもかまわないということだ。例示するためわざと plugin id を `org.sample.Greetings` と宣言し、class名を `com.example.greeting.GreeingPlugin`　と宣言した。これでOKなのだ。

Gradle公式ドキュメントの下記の箇所にplugin idをどのように決めるのがオススメかが書いてある。一読のこと。
-   [creating a plugin id](https://docs.gradle.org/current/userguide/custom_plugins.html#sec:creating_a_plugin_id)

### ビルドを実行すると何が起きるか

step2のビルドを実行するとGroovyコードをコンパイルできた。JUnitテストを実行して成功した。

![2.1 success compile and test](https://kazurayam.github.io/PublishingCustomGradlePlugin_StepByStep/images/2.1_success_compile_and_test.png)

### java-gradle-plugin が何を生成するか

`step2/build` ディレクトリの中を観察してみよう。`java-gradle-plugin`プラグインがたくさんのファイルを生成しているのがわかる。

#### build ディレクトリの外観

まず `build` ディレクトリのツリーの形を見てみよう。

    $ tree ./build
    ./build
    ├── classes
    │   └── ...
    ├── generated
    │   └── ...
    ├── pluginDescriptors
    │   └── org.sample.Greetings.properties
    ├── pluginUnderTestMetadata
    │   └── ...
    ├── reports
    │   └── ...
    ├── resources
    │   └── main
    │       └── META-INF
    │           └── gradle-plugins
    │               └── org.sample.Greetings.properties
    ├── test-results
    │   └── ...
    └── tmp
        ├── ...

    47 directories, 20 files

この中で下記のファイルが重要だ：

`step2/build/pluginDescriptors/org.sample.Greetings.properties`

    implementation-class=com.example.greeting.GreetingPlugin

中身が同じファイルが別の場所にもある。

`step2/build/resources/main/META-INF/gradle-plugins/org.sample.Greetings.properties`

    implementation-class=com.example.greeting.GreetingPlugin

これは何か？ このpropertiesファイルは最終的にjarファイルの中に格納されて配布される。使い手のビルドがそのjarファイルを取り込んだとき、カスタムプラグインの実装クラスがどれであるかを教える名札のようなものだ。Gradle公式ドキュメントの下記URLを参照のこと。

-   [Behind the scene](https://docs.gradle.org/current/userguide/custom_plugins.html#behind_the_scenes)

### 次に何をするか

プラグインをコンパイルできた。次にプラグインをpublishしたい。コマンドを試してみると、当然のことながら、失敗した。

    $ cd step2
    :~/github/PublishingCustomGradlePlugin_StepByStep/step2 (develop *)
    $ gradle publish

    FAILURE: Build failed with an exception.

    * What went wrong:
    Task 'publish' not found in root project 'step2'.
    ...


## step3 maven-publish プラグインを適用する

第３段階として[`maven-publish`](https://docs.gradle.org/current/userguide/publishing_maven.html) プラグインを適用します。プロジェクトの build ディレクトリの中にMavenレポジトリを作り、そこにカスタムプラグインをpublishします。`maven-publish`プラグインが裏側で何をするのかを観察します。

### settings.gradle と build.gradle

`step3/settings.gradle` ファイルと `step3/build.gradle` ファイルを作りました。中身は次の通り。

#### step3/settings.gradle

    rootProject.name = 'step3'

#### step3/build.gradle

リンク [step3/build.gradle](https://github.com/kazurayam/PublishingCustomGradlePlugin_StepByStep/blob/develop/step3/build.gradle)　　を参照してください。

５行目で `mave-publish` プラグインを適用することを宣言しています。

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

35行目に `publishing` extention クロージャがあって、その中でローカルなMavenレポジトリを作ることを宣言しています。これによって`step3/build/repos-maven`にディレクトリが作られます。

        repositories {
            maven {
                name = "sketch"
                url = layout.buildDirectory.dir("repos-maven")
            }
        }
    }

### ビルドを実行すると何が起きるか

`gradle publish`タスクを実行した。

    $ pwd
    /Users/kazuakiurayama/github/PublishingCustomGradlePlugin_StepByStep/step3
    :~/github/PublishingCustomGradlePlugin_StepByStep/step3 (develop *+)
    $ gradle clean

    BUILD SUCCESSFUL in 850ms
    1 actionable task: 1 executed
    :~/github/PublishingCustomGradlePlugin_StepByStep/step3 (develop *+)
    $ gradle publish

    BUILD SUCCESSFUL in 881ms
    8 actionable tasks: 4 executed, 4 up-to-date

Successしています。でもコンソールメッセージを見ただけでは舞台裏で何が起きたのか皆目分かりません。

### maven-publish プラグインが何を生成したのか

#### `build` ディレクトリのツリーの様子

`build`ディレクトリの中に幾つかのディレクトリが作られ、たくさんのファイルが `maven-publish` によって生成されています。

    :~/github/PublishingCustomGradlePlugin_StepByStep/step3 (develop *+)
    $ tree build
    build
    ├── classes
    │   └── ...
    ├── generated
    │   └── ...
    ├── libs
    │   └── step3-1.0.jar
    ├── pluginDescriptors
    │   └── org.sample.Greetings.properties
    ├── publications
    │   ├── MyGreetingPluginMarkerMaven
    │   │   └── pom-default.xml
    │   └── pluginMaven
    │       └── pom-default.xml
    ├── repos-maven
    │   ├── com
    │   │   └── example
    │   │       └── step3
    │   │           ├── 1.0
    │   │           │   ├── step3-1.0.jar
    │   │           │   ├── step3-1.0.jar.md5
    │   │           │   ├── step3-1.0.jar.sha1
    │   │           │   ├── step3-1.0.jar.sha256
    │   │           │   ├── step3-1.0.jar.sha512
    │   │           │   ├── step3-1.0.pom
    │   │           │   ├── step3-1.0.pom.md5
    │   │           │   ├── step3-1.0.pom.sha1
    │   │           │   ├── step3-1.0.pom.sha256
    │   │           │   └── step3-1.0.pom.sha512
    │   │           ├── maven-metadata.xml
    │   │           ├── maven-metadata.xml.md5
    │   │           ├── maven-metadata.xml.sha1
    │   │           ├── maven-metadata.xml.sha256
    │   │           └── maven-metadata.xml.sha512
    │   └── org
    │       └── sample
    │           └── Greetings
    │               └── org.sample.Greetings.gradle.plugin
    │                   ├── 1.0
    │                   │   ├── org.sample.Greetings.gradle.plugin-1.0.pom
    │                   │   ├── org.sample.Greetings.gradle.plugin-1.0.pom.md5
    │                   │   ├── org.sample.Greetings.gradle.plugin-1.0.pom.sha1
    │                   │   ├── org.sample.Greetings.gradle.plugin-1.0.pom.sha256
    │                   │   └── org.sample.Greetings.gradle.plugin-1.0.pom.sha512
    │                   ├── maven-metadata.xml
    │                   ├── maven-metadata.xml.md5
    │                   ├── maven-metadata.xml.sha1
    │                   ├── maven-metadata.xml.sha256
    │                   └── maven-metadata.xml.sha512
    ├── resources
    │   └── main
    │       └── META-INF
    │           └── gradle-plugins
    │               └── org.sample.Greetings.properties
    └── tmp
        ├── ...

    37 directories, 38 files

いろんなものがあって、ちょっとよく分かりません。一つひとつ見ていきましょう。

#### プラグインのbinaryを格納したjarファイル

`step2/build/libs/step3-1.0.jar` ができています。jarファイルの中身を見てみましょう。

    :~/github/PublishingCustomGradlePlugin_StepByStep/step3 (develop *+)
    $ tar -xvf build/libs/step3-1.0.jar
    x META-INF/
    x META-INF/MANIFEST.MF
    x com/
    x com/example/
    x com/example/greeting/
    x com/example/greeting/GreetingPlugin$_apply_closure2$_closure4.class
    x com/example/greeting/GreetingPlugin$_apply_closure2.class
    x com/example/greeting/GreetingPlugin$_apply_closure1$_closure3.class
    x com/example/greeting/GreetingPlugin.class
    x com/example/greeting/GreetingPlugin$_apply_closure1.class
    x META-INF/gradle-plugins/
    x META-INF/gradle-plugins/org.sample.Greetings.properties

わたしが書いたカスタムGradleプラグインのclassファイル `com.example.greeting.GreetingPlugin` が格納されています。それからMETA-INFの下に plugin id `org.sample.Greeting に対応するpropertiesファイルも格納されています。jarファイルの内容はこれで良さそうです。

#### jarファイルの名前がどのように決まるのか

jarファイルの名前は `step3-1.0.jar` です。この名前がどうやって決まったのでしょうか?

実はGradleビルドプロジェクトの `rootProject.name`　プロパティの値が `step3` と指定されている。この値がjarファイルのbase nameのデフォルトになります。具体的には [`step3/settings.gradle`](https://github.com/kazurayam/PublishingCustomGradlePlugin_StepByStep/blob/develop/step3/settings.gradle) にそう書いてあるから。バージョン番号は`step3/build.gradle`ファイルの中で `project.version` プロパティが `1.0` と設定されていることによっています。

#### Mavenレポジトリの中に何ができているか

`step3/build.gradle` ファイルの中にこう書きました。

    gradlePlugin {
        // Define the plugin
        plugins {
            MyGreeting {
                id = 'org.sample.Greetings'
                implementationClass = 'com.example.greeting.GreetingPlugin'
            }
        }
    }

    publishing {
        repositories {
            maven {
                url = layout.buildDirectory.dir("repos-maven")
            }
        }
    }

この記述によって Mavenレポジトリが `step3/build/repos-maven` ディレクトリに作られ、その中に下記のようなファイル群が生成されました。

    $ tree build
    build
    ├── publications
    │   ├── MyGreetingPluginMarkerMaven
    │   │   └── pom-default.xml
    │   └── pluginMaven
    │       └── pom-default.xml
    ...
    │   └── org
    │       └── sample
    │           └── Greetings
    │               └── org.sample.Greetings.gradle.plugin
    │                   ├── 1.0
    │                   │   ├── org.sample.Greetings.gradle.plugin-1.0.pom
    ...


`build/publications/MyGreetingPluginMarkerMaven` という長い名前のディレクトリができています。これは何なんでしょうか？　このディレクトリ名を節に分けると

'MyGreeting' + 'PluginMarker' + 'Maven'

という３つの部分から構成されています。すなわち

`<publication name>` + 'PluginMarker' + `<type of repository>`

という意味と解釈することができます。でも "PluginMarker" って何だ? --- 公式ドキュメントに説明がちょっと書いてあります。

-   linke:https://docs.gradle.org/current/userguide/plugins.html#sec:plugin\_markers\[Plugin Marker Artifacts\]

> Since the plugins {} DSL block only allows for declaring plugins by their globally unique plugin id and version properties, Gradle needs a way to look up the coordinates of the plugin implementation artifact. To do so, Gradle will look for a Plugin Marker Artifact with the coordinates plugin.id:plugin.id.gradle.plugin:plugin.version. This marker needs to have a dependency on the actual plugin implementation. Publishing these markers is automated by the java-gradle-plugin.

うーん。

>Gradle needs a way to look up the coordinates of the plugin implementation artifact

って何を言っているのでしょう？　buildディレクトリの中のファイルを観察して具体的細部を発見しよう。

`step3/build/publications/MyGreetingPluginMarkerMaven/pom-default.xml`　ファイルの中身がこうなっています。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>org.sample.Greetings</groupId>
      <artifactId>org.sample.Greetings.gradle.plugin</artifactId>
      <version>1.0</version>
      <packaging>pom</packaging>
      <dependencies>
        <dependency>
          <groupId>com.example</groupId>
          <artifactId>step3</artifactId>
          <version>1.0</version>
        </dependency>
      </dependencies>
    </project>

このXMLファイルはMavenプロジェクトが定義するところの [Project Object Model (POM)](https://qiita.com/TaikiTkwkbysh/items/5553a8625ff95314a392) に他なりません。build.gradleの中で plugin id として指定した `org.sample.Greetings` というシンボルが、POMの中では `<groupId>org.sample.Greetings</groupId>` という形式に変換されているのがわかる。そして`<artifactId>org.sample.Greetings.gradle.plugin</artifactId>` と `<version>1.0</version>` というコードが生成されている。 

build.gradleファイルに書く 
```
plugins { 
    id 'org.sample.Greetings' version '1.0' 
}
``` 
という記述形式は極めて簡潔です。ユーザの使い勝手のためには簡潔な記述は好ましい。そのいっぽうで jarファイルをMavenレポジトリに格納して配布可能にすることも達成したい。そのためにはPOMを生成してjarファイルに添付することが必須要件になる。ならば開発者がPOM.xmlファイルを手書きすればいいようなものだが、POMはコードが長くて間違えやすいから、厄介な仕事だ。そこで `maven-publish` プラグインは　plugin id　とimplementationClass　を指定することだけを開発者に求めて、それを材料として、冗長なPOMファイルへ翻訳するという手間仕事を代行してくれる。その手間仕事の成果物のことを`maven-publish`プラグインは _PluginMarker_ という集合名で呼ぶことにした。上記の例に則していうなれば `MyPlugin` という publication nameを持つ　_PluginMaker_　が生成された、ということができます。


`pom-default.xml` ファイルの中身の後半部分を観察しましょう。`<dependency>` の記述があって、`org.sample.Greetings.gradle.plugin` というartifact idが別のartifactに依存していることが表明されている。依存先のartifactは  `com.example:step3:1.0` という _coordinate_ (座標?) によって表現されている。これに対応するのが `step3/build/publications/pluginMaven/pom-default.xml` です。その中身は下記の通り。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.example</groupId>
      <artifactId>step3</artifactId>
      <version>1.0</version>
    </project>

このPOMが指し示すのはバイナリclassファイルを格納したjarファイルです。

    $ tree build
    build
    ...
    ├── repos-maven
    │   ├── com
    │   │   └── example
    │   │       └── step3
    │   │           ├── 1.0
    │   │           │   ├── step3-1.0.jar
    ...

つまりこういうことだ。 `maven-publish` プラグインは一つのカスタムGradleプラグインに対応して必ず2つのモノを生成してMavenレポジトリにパブリッシュする。第一にplugin自身の POM xml ファイル。第二にpluginの実装classを格納したjarファイル。plugin idからjarへの結びつきを表現したPOMを生成する仕事を `maven-plugin` がやってくれる。

わたしが自作した `org.sample.Greetings` プラグインを誰かが自分のGradleビルに適用したとしましょう。彼はGradleビルドにこう書く。
```
plugins {
    id 'org.sample.Greeting' version "1.0"
}
```
ビルドはGaven Plugin Pluginにリクウエストを投げる。このplugin idとversionの組み合わせに対応するものがMavenレポジトリにあるとすれば、サブディレクトリ 

`org/sample/Greetings/org.sample.Greetings.gradle.plugin/1.0/org.sample.Greetings.gradle.plugin-1.0.pom`

にあるはずだ。この対応関係は `maven-publish`プラグインが決めたルールであり、設定変更可能ではない。

ユーザのGradleビルドはpluginのPOM.xmlを発見したら中身を解析する。するとclassファイルを格納したjarへの`<dependency>` を発見するだろう。だからGradleビルドは外部依存性を解決するいつもの仕事を遂行する。かくして彼のGradleビルドはわたしのカスタムGradleプラグインの実装classを発見して利用できるようになる。これにて一件落着。

わたしは最初、カスタムGradleプラグインをpublishするとき、何かjarファイルが1個生成されてそれがサイトにアップロードされるだけと想像していた。実はそんなに単純ではなかった。pluginのPOMとjarと２つのモノが生成されてMavenレポジトリにアップロードされる。その２つの依存関係がPOMに書いてある。`maven-publish`プラグインは裏方仕事をやってくれている。

### "publish XXX" タスク群

`maven-publish` プラグインを適用すると幾つかのタスクがビルドに挿入される。それらタスクは "publish XXXX" という形の名前を持っている。それらタスクを一覧するには `gradle tasks | grep publish` コマンドを実行すればいい。

    $ gradle tasks | grep publish
    publish - Publishes all publications produced by this project.
    publishAllPublicationsToSketchRepository - Publishes all Maven publications produced by this project to the sketch repository.
    publishMyGreetingPluginMarkerMavenPublicationToMavenLocal - Publishes Maven publication 'MyGreetingPluginMarkerMaven' to the local Maven repository.
    publishMyGreetingPluginMarkerMavenPublicationToSketchRepository - Publishes Maven publication 'MyGreetingPluginMarkerMaven' to Maven repository 'sketch'.
    publishPluginMavenPublicationToMavenLocal - Publishes Maven publication 'pluginMaven' to the local Maven repository.
    publishPluginMavenPublicationToSketchRepository - Publishes Maven publication 'pluginMaven' to Maven repository 'sketch'.
    publishToMavenLocal - Publishes all Maven publications produced by this project to the local Maven cache.

うわー、字が多い。パッと見に何が書いてあるのか読み取れない。テキストにスタイルを加えて出来るだけ読みやすくしてみよう。

次の３つのタスクは一種のマクロだ。

-   `publish` - Publishes all publications produced by this project.

-   `publishAllPublicationsToSketchRepository` - Publishes all Maven publications produced by this project to the `sketch` repository.

-   `publishToMavenLocal` - Publishes all Maven publications produced by this project to the *local Maven cache*.

ほかのタスクはマクロではなくてそれぞれ完結した役目を持つタスクだ。

-   `publishMyGreetingPluginMarkerMavenPublicationToMavenLocal` - Publishes Maven publication `MyGreetingPluginMarkerMaven` to the *local Maven repository*.

-   `publishMyGreetingPluginMarkerMavenPublicationToSketchRepository` - Publishes Maven publication `MyGreetingPluginMarkerMaven` to Maven repository `sketch`.

-   `publishPluginMavenPublicationToMavenLocal` - Publishes Maven publication `pluginMaven` to the *local Maven repository*.

-   `publishPluginMavenPublicationToSketchRepository` - Publishes Maven publication `pluginMaven` to Maven repository `sketch`.

#### Publications とは何か

タスク名を観察すると、その中で二つのシンボルが使われているのがわかる。すなわち `MyGreetingPluginMarkerMaven` と `pluginMaven` だ。`maven-publish`プラグインはこの名前を _publication_ （出版物?） と集合的に呼んでいる。publicationの名前は `build/publications` ディレクトリの下にあるサブディレクトリの名前と同じだ。

    $ tree build
    build
    ├── publications
    │   ├── MyGreetingPluginMarkerMaven
    │   │   └── pom-default.xml
    │   └── pluginMaven
    │       └── pom-default.xml
    ...

`MyGreetingPluginMarketMaven` というpublicationの中にカスタムGradleプラグインのメタデータを記述したPOM.xmlファイルがある。

`pluginMaven` というpublicationの中には実装クラスを格納したjarファイルのメタデータを記述したPOMファイルがある。その中をみるとjarファイルの名前が `step3-1.0.jar` と記述してある。`gradle publshPluginMavenPublicationToXXXX` コマンドを実行すると `maven-publish` プラグインはjarファイルを生成する仕事を自動的に行う。開発者はjarを生成するためのtaskをbuild.gradleの中に明示的にコーディングする必要がないし、仮に明示的にコーディングしたとしても、そうやって作られたjarファイルを`maven-publish` プラグインは無視する。だからやっても無駄だ。

#### Mavenレポジトリ "MavenLocal" と "Sketch" について

`maven-publish`プラグインを適用することで挿入されたタスクの名前をみると、２つのシンボルが使われているのが見てとれる。すなわち "MavenLocal" と "Sketch"　だ。

Gradleに慣れた人なら "local Maven repository" とか "local Maven cache"　が何を意味するかは既にご存知のはず。[Baeldung, Where is the Maven Local Repository?](https://www.baeldung.com/maven-local-repository)とかを一読して思い出してください。

"Sketch"というシンボルは私が `build.gradle` ファイルにレポジトリのnameとして指定したものだ。下記のように：

    publishing {
        repositories {
            maven {
                name = "sketch"

さて、以上の解釈を踏まえると "publishXXXX"　タスクが何をするのかその名前から推測できるようになった。

## step4 MavenレポジトリとIvyレポジトリ

Gradleは二種類のレポジトリをサポートしている。Mavenレポジトリと[Ivy](https://ant.apache.org/ivy/)レポジトリだ。step3ではMavenレポジトリにパブリッシュしてみた。step4では、Mavenレポジトリに加えて、Ivyレポジトリにもパブリッシしてみよう。

### settings.gradle と build.gradle

`step4/settings.gradle` ファイルと `step4/build.gradle` ファイルを作った。

#### step4/settings.gradle

    rootProject.name = 'step4'

#### step4/build.gradle

リンク [step4/build.gradle](https://github.com/kazurayam/PublishingCustomGradlePlugin_StepByStep/blob/develop/step4/build.gradle) を参照のこと。

6行目で `ivy-publish` プラグインを適用している。`ivy-publish`は`maven-publish`の同類と思えばいい。

    plugins {
        ...
        id 'maven-publish'
        id 'ivy-publish'
    }

36行目に `publishing` extension クロージャを記述している。そこでIvyレポジトリをどこに作るかを指定している。

    publishing {
        repositories {
            maven {
                name = "sketchMaven"
                url = layout.buildDirectory.dir("repos-maven")
            }
            ivy {
                name = "sketchIvy"
                url = layout.buildDirectory.dir("repos-ivy")
            }
        }
    }


### ビルドを実行すると何が起こるか

    :~/github/PublishingCustomGradlePlugin_StepByStep/step4 (develop *+)
    $ gradle  publish

    BUILD SUCCESSFUL in 881ms
    8 actionable tasks: 4 executed, 4 up-to-date

静かにビルドが成功しました。しかし何が起こったのか、メッセージからは何もわからない。`build`ディレクトリの中身の変化を観察しましょう。

### `build` ディレクトリのツリー

`gradle publsh` コマンドを実行すると `build` ディレクトリにたくさんのファイルが生成されました。

    :~/github/PublishingCustomGradlePlugin_StepByStep/step4 (develop *)
    $ tree build
    build
    ├── classes
    │   └── ...
    ├── generated
    │   └── ...
    ├── libs
    │   └── step4-1.0.jar
    ├── pluginDescriptors
    │   └── org.sample.Greetings.properties
    ├── pluginUnderTestMetadata
    │   └── plugin-under-test-metadata.properties
    ├── publications
    │   ├── MyGreetingPluginMarkerIvy
    │   │   └── ivy.xml
    │   ├── MyGreetingPluginMarkerMaven
    │   │   ├── org.sample.Greetings.gradle.plugin (1).iml
    │   │   └── pom-default.xml
    │   ├── pluginIvy
    │   │   └── ivy.xml
    │   └── pluginMaven
    │       ├── pom-default.xml
    │       └── step4.iml
    ├── reports
    │   └── ...
    ├── repos-ivy
    │   ├── com.example
    │   │   └── step4
    │   │       └── 1.0
    │   │           ├── ivy-1.0.xml
    │   │           ├── ivy-1.0.xml.sha1
    │   │           ├── ivy-1.0.xml.sha256
    │   │           ├── ivy-1.0.xml.sha512
    │   │           ├── step4-1.0.jar
    │   │           ├── step4-1.0.jar.sha1
    │   │           ├── step4-1.0.jar.sha256
    │   │           └── step4-1.0.jar.sha512
    │   └── org.sample.Greetings
    │       └── org.sample.Greetings.gradle.plugin
    │           └── 1.0
    │               ├── ivy-1.0.xml
    │               ├── ivy-1.0.xml.sha1
    │               ├── ivy-1.0.xml.sha256
    │               └── ivy-1.0.xml.sha512
    ├── repos-maven
    │   ├── com
    │   │   └── example
    │   │       └── step4
    │   │           ├── 1.0
    │   │           │   ├── step4-1.0.jar
    │   │           │   ├── step4-1.0.jar.md5
    │   │           │   ├── step4-1.0.jar.sha1
    │   │           │   ├── step4-1.0.jar.sha256
    │   │           │   ├── step4-1.0.jar.sha512
    │   │           │   ├── step4-1.0.pom
    │   │           │   ├── step4-1.0.pom.md5
    │   │           │   ├── step4-1.0.pom.sha1
    │   │           │   ├── step4-1.0.pom.sha256
    │   │           │   └── step4-1.0.pom.sha512
    │   │           ├── maven-metadata.xml
    │   │           ├── maven-metadata.xml.md5
    │   │           ├── maven-metadata.xml.sha1
    │   │           ├── maven-metadata.xml.sha256
    │   │           └── maven-metadata.xml.sha512
    │   └── org
    │       └── sample
    │           └── Greetings
    │               └── org.sample.Greetings.gradle.plugin
    │                   ├── 1.0
    │                   │   ├── org.sample.Greetings.gradle.plugin-1.0.pom
    │                   │   ├── org.sample.Greetings.gradle.plugin-1.0.pom.md5
    │                   │   ├── org.sample.Greetings.gradle.plugin-1.0.pom.sha1
    │                   │   ├── org.sample.Greetings.gradle.plugin-1.0.pom.sha256
    │                   │   └── org.sample.Greetings.gradle.plugin-1.0.pom.sha512
    │                   ├── maven-metadata.xml
    │                   ├── maven-metadata.xml.md5
    │                   ├── maven-metadata.xml.sha1
    │                   ├── maven-metadata.xml.sha256
    │                   └── maven-metadata.xml.sha512
    ├── resources
    │   └── main
    │       └── META-INF
    │           └── gradle-plugins
    │               └── org.sample.Greetings.properties
    ├── test-results
    │   └── ...
    ...
    80 directories, 68 files

MavenレポジトリとIvyレポジトリと二種類を生成したのでディレクトリとファイルの数が増えたが、概念的には変化なし。複雑性は増していない。

ちなみにディレクトリ名を見比べると面白いことがわかります。

-   step4/build/publications/MyGreetingPluginMarker**Ivy**

-   step4/build/publications/MyGreetingPluginMarker**Maven**

-   step4/build/publications/plugin**Ivy**

-   step4/build/publications/plugin**Maven**

コマンドの名前が対称形を成している。もしもレポジトリの新種が発明されて、その名前が **Bar** だったとしたら、きっと `bar-publish` プラグインが開発されるだろう。そして `bar-publish` プラグインは下記のディレクトリを生成するでしょう。

-   step4/build/publications/MyGreetingPluginMarker**Bar**

-   step4/build/publications/plugin**Bar**

シンプルですね。

## step5 複数のプラグインを1個のプロジェクトがpublishするケース

カスタムGradleプラグインをパブリッシュする手順を述べた記事はいくつも公開されているけれど、それら記事はたいてい1個のプロジェクトが1個のプラグインをpublishする例を述べている。そこでわたしは疑問に思った。１つのプロジェクトが２つ以上のカスタムGradleプラグインをpublishすることができるのだろうか？やってみたら成功した。

### プラグイン2個分のGroovyソース

step4のサンプルにはGroovyクラスが１つだけある。すなわち [`com.example.greeting.GreetingPlugin`](https://github.com/kazurayam/PublishingCustomGradlePlugin_StepByStep/blob/develop/step4/src/main/groovy/com/example/greeting/GreetingPlugin.groovy) が2つのメソッド `hello()` と `goodbye()` を実装している。

このstep5ではGreetingPluginを２つのクラスに切り分けよう。すなわち

-   [`com.example.hello.HelloPlugin`](https://github.com/kazurayam/PublishingCustomGradlePlugin_StepByStep/blob/develop/step5/src/main/groovy/com/example/hello/HelloPlugin.groovy)

-   [`com.example.goodbye.GoodbyePlugin`](https://github.com/kazurayam/PublishingCustomGradlePlugin_StepByStep/blob/develop/step5/src/main/groovy/com/example/goodbye/GoodbyePlugin.groovy)

だからstep5プロジェクトはカスタムGradleプラグインを２つパブリッシュすることになる。

### settings.gradle と build.gradle

`step5/settings.gradle` ファイルと `step5/build.gradle` ファイルを作った。

#### step5/settings.gradle

    rootProject.name = 'step5'

#### step5/build.gradle

リンク [step5/build.gradle](https://github.com/kazurayam/PublishingCustomGradlePlugin_StepByStep/blob/develop/step5/build.gradle) を参照のこと。

36行目に `gradlePlugin` extension クロージャを記述した。そこで２つのプラグインを宣言した。すなわち`org.sample.Hello` と `org.sample.Goodbye`　です。

    gradlePlugin {
        // Define the plugin
        plugins {
            MyHello {
                id = 'org.sample.Hello'
                implementationClass = 'com.example.hello.HelloPlugin'
            }
            MyGoodbye {
                id = 'org.sample.Goodbye'
                implementationClass = 'com.example.goodbye.GoodbyePlugin'
            }
        }
    }

### ビルドを実行すると何が起きるか

    :~/github/PublishingCustomGradlePlugin_StepByStep/step5 (develop *)
    $ gradle clean

    BUILD SUCCESSFUL in 895ms
    1 actionable task: 1 executed
    :~/github/PublishingCustomGradlePlugin_StepByStep/step5 (develop *)

    $ gradle test

    BUILD SUCCESSFUL in 7s

    $ gradle publish

    BUILD SUCCESSFUL in 2s
    10 actionable tasks: 10 executed

ビルドは静かに成功しました。

### `build` ディレクトリに生成されたファイル

    $ cd step5
    $ tree build
    build
    ├── classes
    │   └── ...
    ├── generated
    │   └── ...
    ├── libs
    │   └── step5-1.0.jar
    ├── pluginDescriptors
    │   ├── org.sample.Goodbye.properties
    │   └── org.sample.Hello.properties
    ├── pluginUnderTestMetadata
    │   └── ...
    ├── publications
    │   ├── MyGoodbyePluginMarkerMaven
    │   │   └── pom-default.xml
    │   ├── MyHelloPluginMarkerMaven
    │   │   └── pom-default.xml
    │   └── pluginMaven
    │       └── pom-default.xml
    ├── reports
    │   └── ...
    ├── repos-maven
    │   ├── com
    │   │   └── example
    │   │       └── step5
    │   │           ├── 1.0
    │   │           │   ├── step5-1.0.jar
    │   │           │   ├── step5-1.0.jar.md5
    │   │           │   ├── step5-1.0.jar.sha1
    │   │           │   ├── step5-1.0.jar.sha256
    │   │           │   ├── step5-1.0.jar.sha512
    │   │           │   ├── step5-1.0.pom
    │   │           │   ├── step5-1.0.pom.md5
    │   │           │   ├── step5-1.0.pom.sha1
    │   │           │   ├── step5-1.0.pom.sha256
    │   │           │   └── step5-1.0.pom.sha512
    │   │           ├── maven-metadata.xml
    │   │           ├── maven-metadata.xml.md5
    │   │           ├── maven-metadata.xml.sha1
    │   │           ├── maven-metadata.xml.sha256
    │   │           └── maven-metadata.xml.sha512
    │   └── org
    │       └── sample
    │           ├── Goodbye
    │           │   └── org.sample.Goodbye.gradle.plugin
    │           │       ├── 1.0
    │           │       │   ├── org.sample.Goodbye.gradle.plugin-1.0.pom
    │           │       │   ├── org.sample.Goodbye.gradle.plugin-1.0.pom.md5
    │           │       │   ├── org.sample.Goodbye.gradle.plugin-1.0.pom.sha1
    │           │       │   ├── org.sample.Goodbye.gradle.plugin-1.0.pom.sha256
    │           │       │   └── org.sample.Goodbye.gradle.plugin-1.0.pom.sha512
    │           │       ├── maven-metadata.xml
    │           │       ├── maven-metadata.xml.md5
    │           │       ├── maven-metadata.xml.sha1
    │           │       ├── maven-metadata.xml.sha256
    │           │       └── maven-metadata.xml.sha512
    │           └── Hello
    │               └── org.sample.Hello.gradle.plugin
    │                   ├── 1.0
    │                   │   ├── org.sample.Hello.gradle.plugin-1.0.pom
    │                   │   ├── org.sample.Hello.gradle.plugin-1.0.pom.md5
    │                   │   ├── org.sample.Hello.gradle.plugin-1.0.pom.sha1
    │                   │   ├── org.sample.Hello.gradle.plugin-1.0.pom.sha256
    │                   │   └── org.sample.Hello.gradle.plugin-1.0.pom.sha512
    │                   ├── maven-metadata.xml
    │                   ├── maven-metadata.xml.md5
    │                   ├── maven-metadata.xml.sha1
    │                   ├── maven-metadata.xml.sha256
    │                   └── maven-metadata.xml.sha512
    ├── resources
    │   └── main
    │       └── META-INF
    │           └── gradle-plugins
    │               ├── org.sample.Goodbye.properties
    │               └── org.sample.Hello.properties
    ├── test-results
    │   └── ...
    └── tmp
        ├── ...

    78 directories, 71 files

`maven-publsh` プラグインがたくさんのファイルを生成しました。しかしstep4までに学んだ知見を踏まえると、こうなるはずと納得できます。

## step6 Gradle Plugin Portalにパブリッシュする

自作したカスタムGradleプラグインを [Gradle Plugin Portal](https://plugins.gradle.org/) にパブリッシュして一般公開します。

### 参照すべきドキュメント

下記のGradle公式ドキュメントを一読のこと。

-   [Publishing Plugins to the Gradle Plugin Portal](https://docs.gradle.org/current/userguide/publishing_gradle_plugins.html)

まず最初に Gradle Plugin Portalに自分専用のアカウントを作ることが必要です。下記の指示に従って済ませましょう。

-   [Create an account on the Gradle Plugin Portal](https://docs.gradle.org/current/userguide/publishing_gradle_plugins.html#create_an_account_on_the_gradle_plugin_portal)

### settings.gradle と build.gradle

`step6`ディレクトリを作り、`step6/settings.gradle` ファイルと `step6/build.gradle` ファイルを作りました。

#### step6/settings.gradle

    rootProject.name = 'step6'

#### step6/build.gradle

リンク [step6/build.gradle](https://github.com/kazurayam/PublishingCustomGradlePlugin_StepByStep/blob/develop/step6/build.gradle) を参照のこと。

第１行目の `plugins { ... }` に注目してください。step5までは `java-gradle-plugin` と `maven-publis` プラグインを適用してきましたが、step6からは `com.gradle.plugin-publish` プラグインを適用します。

    plugins {
        id 'groovy'
        //id 'java-gradle-plugin'
        //id 'maven-publish'
        id 'com.gradle.plugin-publish' version '1.1.0'
    }

それから `gradlePlugin` extension クロージャを記述した。

    gradlePlugin {
        website = 'https://kazurayam.github.io/PublishingCustomGradlePlugin_StepByStep/'
        vcsUrl = 'https://github.com/kazurayam/PublishingCustomGradlePlugin_StepByStep/'
        plugins {
            MyGreeting {
                id = 'io.github.kazurayam.Greetings'
                displayName = 'Plugin for living nice to others'
                description = 'A plugin that prints hello and goodbye'
                tags.set(['testing', 'lesson'])

### `com.gradle.plugin-publish` プラグインは何をするのか、何をしないのか

いろいろ試したあげく、わたしは`com.gradle.plugin-publish`プラグインを次のように理解しました。

1.  わたしが自作したカスタムGradleプラグインを他の人と共有するためには、つまり彼らが自分のビルドの冒頭に `plugins { id "org.sample.Greetings" version "1.0" }}` と記述するだけでわたしのプラグインを利用できるようにするには、カスタムGradleプラグインを [Gradle Plugin Portal](https://plugins.gradle.org/) に上げることが必要だ。そのためには `com.gradle.plugin-publish` を使わなければならない。このプラグインを使うことが必須条件であり、他の選択肢は無い。

2.  `com.gradle.plugin-publish` プラグインは自動的に `java-gradle-plugin` プラグインと `maven-publish` プラグインを適用する。だから私はこれら２つのプラグインをbuild.gradleの冒頭の `plugins {...}` に書かなくてよい。

3.  `com.gradle.plugin-publish` プラグインを動かすために開発者が build.gradleファイルの中に記述する必要があるのは `gradlePlugin { …​ }` クロージャーだけである。

4.  `com.gradle.plugin-publish` プラグインはGradle Plugin Portalがどこにあるのか（URLが何か）をあらかじめ知っている。だからGradle Plugin PortalのURLを伝えるために `publishing { repository { maven { …​ }}}` を書くということは無用。

5.  `com.gradle.plugin-publish` プラグインはバイナリclassのjarファイルとソースのjarファイルとjavadocのjarファイルとそれに関係するメタデータ(POMファイルとか)を自動的に生成する。だから開発者はjarを生成するためにtaskを記述する必要がない。

6.  `com.gradle.plugin-publish` プラグインがjarファイルの名前を決定する。

7.  開発者がjarファイルの名前を自分の好きなように指定したいと思うかもしれないが、それはできない。

8.  カスタムGradleプラグインといえども一つのJava/Groovyプログラムであることに変わりはない。だから開発者がみづから build.gradleファイルの中に Jarタスクを記述してバイナリclassのjarファイル、ソースのjarファイル、javadocのjarファイルをカスタムに作ることはできる。さらに`maven-publish` プラグインをapplyして自作のjarを適当なMavenレポジトリに向けてパブリッシュすることはできる。

9.  しかしながら、`com.gradle.plugin-publish` プラグインはカスタムに準備されたjarファイルを無視する。だからわたしがカスタムに準備したjarファイルを Gradle Plugin Portal に向けてパブリッシュすることはできない。

### ビルドを実行すると --- エラーが発生した

まず `step6/build` ディレクトリを空っぽにした。

    :~/github/PublishingCustomGradlePlugin_StepByStep/step6 (develop *)
    $ gradle clean

    BUILD SUCCESSFUL in 895ms
    1 actionable task: 1 executed

次に `gradle publishToMavenLocal` タスクを実行した

    $ gradle publishToMavenLocal

    BUILD SUCCESSFUL in 1s
    10 actionable tasks: 6 executed, 4 up-to-date

これによって `~/.m2` ディレクトリの下にファイルが作られていることを確認できた。

    $ tree ~/.m2/repository/io/github/kazurayam/
    /Users/kazuakiurayama/.m2/repository/io/github/kazurayam/
    ├── Greetings
    │   └── io.github.kazurayam.Greetings.gradle.plugin
    │       ├── 1.0
    │       │   └── io.github.kazurayam.Greetings.gradle.plugin-1.0.pom
    │       └── maven-metadata-local.xml
    ├── step6
    │   ├── 1.0
    │   │   ├── step6-1.0-javadoc.jar
    │   │   ├── step6-1.0-sources.jar
    │   │   ├── step6-1.0.jar
    │   │   └── step6-1.0.pom
    │   └── maven-metadata-local.xml
    ...

`step6-1.0.jar` ファイルが作られている。その中身はどうなっているのだろう？確認してみた。

    $ tar -xvf ~/.m2/repository/io/github/kazurayam/step6/1.0/step6-1.0.jar
    x META-INF/
    x META-INF/MANIFEST.MF
    x com/
    x com/example/
    x com/example/greeting/
    x com/example/greeting/GreetingPlugin$_apply_closure2$_closure4.class
    x com/example/greeting/GreetingPlugin$_apply_closure2.class
    x com/example/greeting/GreetingPlugin$_apply_closure1$_closure3.class
    x com/example/greeting/GreetingPlugin.class
    x com/example/greeting/GreetingPlugin$_apply_closure1.class
    x META-INF/gradle-plugins/
    x META-INF/gradle-plugins/io.github.kazurayam.Greetings.properties

大丈夫そうだ。以上で `gradle publishPlugins` コマンドを実行して、Gradle Plugin Portalにパブリッシュする準備ができたはず。やってみた。

    $ gradle publishPlugins


    > Task :publishPlugins FAILED
    Publishing plugin io.github.kazurayam.Greetings version 1.0

    FAILURE: Build failed with an exception.

    * What went wrong:
    Execution failed for task ':publishPlugins'.
    > Failed to post to server.
      Server responded with:
      Plugin id 'io.github.kazurayam.Greetings' and group id 'com.example' must use same top level namespace, like 'com.example' or 'io.github.kazurayam'

おっと！エラーが発生した。何がまずいのだろうか？

わたしはGradleビルドの group id として `com.example` という値を設定していた。そのいっぽう、plugin id として　`io.github.kazurayam.XXXX`　という値を設定していた。ここに名前空間の不統一があった。それでもローカルに一時的に作ったMavenレポジトリに上げるときにこの不統一は問題にならなかった。ところが *Gradle Plugin Portal* は group id と plugin id の名前空間の上位を統一することを要求する。それを上記のメッセージが教えている。

つまり Gradle Plugin Portal は下記のいづれかのやり方に修正せよとわたしに要求している。

1.  group id = `io.github.kazurayam`, plugin id = `io.github.kazurayam.Greeings`

2.  group id = `com.example`, plugin id = `com.example.Greetings`

開発者がこの規則に従うことをGradle Plugin Portalが要求する。だから　`com.gradle.plugin-publish` プラグインはそのようなチェックを実施したところわたしの試みが引っかかってしまった。

よろしい。次のstep7で対応しよう。

## step7 group id と plugin id を整える

前のstep6で述べたように、カスタムGradleプラグインをGradle Plugin Portalに向けてパブリッシュしようと試みたところエラーが発生した。Gradle Plugin Portalサイトは `group id` と `plugin id` の名前空間の上位層が食い違っているとダメだよという。ならば指図に従うまでのこと。やってみよう。

### settings.gradle と build.gradle

#### step7/settings.gradle

    rootProject.name = 'step7'

#### step7/build.gradle

リンク [step7/build.gradle](https://github.com/kazurayam/PublishingCustomGradlePlugin_StepByStep/blob/develop/step7/build.gradle) を参照のこと。

`build.gradle` ファイルの中で `io.github.kazurayam` という名前空間を　group id と plugin id　の両方に設定した。すなわち

    group = 'io.github.kazurayam'
    version = '1.1'

と

        plugins {
            MyGreeting {
                id = 'io.github.kazurayam.Greetings'

というように。

### けっきょくどういう名前空間を指定するのが正解なのか

けっきょく"group id"と"plugin id"と実装クラスのpackage名をどのように指定したのか、下記にまとめた。

<table>
<caption>Namespace rule</caption>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<thead>
<tr class="header">
<th style="text-align: left;">entity</th>
<th style="text-align: left;">how I configured in the build.gradle</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;"><p>the build project’s group id</p></td>
<td style="text-align: left;"><p><code>group="io.github.kazurayam"</code></p></td>
</tr>
<tr class="even">
<td style="text-align: left;"><p>the plugin id</p></td>
<td style="text-align: left;"><p><code>gradlePlugin { plugins { MyGreeting { id = 'io.github.kazurayam.Greetings'</code></p></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><p>the implementation class</p></td>
<td style="text-align: left;"><p><code>com.example.greeting.GreetingPlugin</code></p></td>
</tr>
</tbody>
</table>

つまりこういうことだ。

1.  group id と plugin id とは名前空間の上位層を統一しなければならない。
2.  実装クラスのパッケージ名はなんでもいい。group idと一致していなくてかまわない。plugin　idと不一致でもかまわない。

### Gradle Plugin Portalはプラグインの作者が自分に固有の名前を使うことを求める

わたしはstep6までgroup idに'com.example'という値を指定していた。自分のローカルなMavenレポジトリにパブリッシュするだけなら咎められることはなかった。しかしGradle Plugin Portalにパブリッシュしようとして咎められた。そこでgroup idを`io.github.kazurayam` に変更した。このgroup idをGradle Plugin Portalは受け付けてくれた。

こう考えることもできる。Gradle Plugin Portalは複数の開発者が共有する公の場所だ。そこでは開発者が各々が管理責任を負う領域を明確にすること、他人の成果物を勝手に書き換えるような無法を行わないこと、それがマナーとなる。だから `com.example` などというのっぺらぼうな名前を名乗ることをGradle Plugin Portalは許さない。当然そうあるべきだとおもいます。

### ビルドを実行すると何が起きるか

local Maven cacheに向けてパブリッシュしてみた。

    $ gradle publishToMavenLocal

    BUILD SUCCESSFUL in 3s
    10 actionable tasks: 10 executed

そしてlocal Maven Cache の中身を観察した。

    $ tree ~/.m2/repository/io/github/kazurayam/
    /Users/kazuakiurayama/.m2/repository/io/github/kazurayam/
    ├── Greetings
    │   └── io.github.kazurayam.Greetings.gradle.plugin
    │       ├── 1.1
    │       │   └── io.github.kazurayam.Greetings.gradle.plugin-1.1.pom
    │       └── maven-metadata-local.xml
    ├── step7
    │   ├── 1.1
    │   │   ├── step7-1.1-javadoc.jar
    │   │   ├── step7-1.1-sources.jar
    │   │   ├── step7-1.1.jar
    │   │   └── step7-1.1.pom
    │   └── maven-metadata-local.xml
    ...
    12 directories, 23 files

同じ中身をGradle Plugin Portalに向けてパブリッシしてみた。

    $ gradle publishPlugin

    > Task :publishPlugins
    Publishing plugin io.github.kazurayam.Greetings version 1.1
    Publishing artifact build/libs/step7-1.1.jar
    Publishing artifact build/libs/step7-1.1-javadoc.jar
    Publishing artifact build/libs/step7-1.1-sources.jar
    Publishing artifact build/publications/pluginMaven/pom-default.xml
    Activating plugin io.github.kazurayam.Greetings version 1.1

    BUILD SUCCESSFUL in 5s
    8 actionable tasks: 2 executed, 6 up-to-date

ついに自作のカスタムGradleプラグインを Gradle Plugin Portal に向けてパブリッシュすることに成功した。

>パブリッシュの操作をした直後に <https://plugins.gradle.org/search?term=io.github.kazurayam> にアクセスしてみたが、わたしのプラグインはまだサイト上に現れなかった。なんらかの事情で時間がかかるのかもしれない。待つことしばし、十時間後にもう一度アクセスしたらサイト上に現れた。だからわたしのパブリッシュ操作は成功したと言える。

### Gradle Plugin Portalの中に何がある？

*Gradle Plugin Portal* というサイトは一つの Mavenレポジトリ を内部に持っていて、その中にはいわゆるカスタムGradleプラグインと呼ばれる種類の成果物だけが集められている。MavenレポジトリのURLも公開されていて、ブラウザでそのURLにアクセスすることができる。下記のURLがそれだ。

-   <https://plugins.gradle.org/m2/io/github/kazurayam/>

## step8 PluginMavenPublication にユニークな artifactId を指定する必要があった

前のstep7で、カスタムGradleプラグインをGradle Plugin Portalにパブリッシュすることに成功したものの、問題が残っていることに気がついた。

### 解決すべき問題 --- artifactIdの重複

step7のディレクトリの中でこういう操作をした。

    $ gradle publishToMavenLocal

    BUILD SUCCESSFUL in 1s
    10 actionable tasks: 4 executed, 6 up-to-date
    :~/github/PublishingCustomGradlePlugin_StepByStep/step7 (develop *)
    $ tree ~/.m2/repository/io/github/kazurayam
    /Users/kazuakiurayama/.m2/repository/io/github/kazurayam
    ├── Greetings
    │   └── io.github.kazurayam.Greetings.gradle.plugin
    │       ├── 1.0
    │       │   └── io.github.kazurayam.Greetings.gradle.plugin-1.0.pom
    │       ├── 1.1
    │       │   └── io.github.kazurayam.Greetings.gradle.plugin-1.1.pom
    │       └── maven-metadata-local.xml
    ├── step6
    │   ├── 1.0
    │   │   ├── step6-1.0-javadoc.jar
    │   │   ├── step6-1.0-sources.jar
    │   │   ├── step6-1.0.jar
    │   │   └── step6-1.0.pom
    │   └── maven-metadata-local.xml
    ├── step7
    │   ├── 1.0
    │   │   ├── step7-1.0-javadoc.jar
    │   │   ├── step7-1.0-sources.jar
    │   │   ├── step7-1.0.jar
    │   │   └── step7-1.0.pom
    │   ├── 1.1
    │   │   ├── step7-1.1-javadoc.jar
    │   │   ├── step7-1.1-sources.jar
    │   │   ├── step7-1.1.jar
    │   │   └── step7-1.1.pom
    │   └── maven-metadata-local.xml

    12 directories, 23 files

私は **step6**, **step7** というシンボルが気になった。こいつらは元々 [`PublisingCustomGradlePlugin_StepByStep`](https://github.com/kazurayam/PublishingCustomGradlePlugin_StepByStep)レポジトリの中のサブディレクトリの名前であった。サブディレクトリの名前としては好ましいシンボルだ。ところが同じシンボルがMavenレポジトリの中に配置されると、step6だのstep7だのは抽象的すぎてMavenレポジトリに格納されるモノを識別するための情報としてはふさわしくない。

step6だのstep7というシンボルは将来、エラーの原因になりうる。わたしは別の仕事のため別のプロジェクトを作り出すかもしれない。そのために別のレポジトリを作るかもしれない。そのレポジトリの名前が仮に *"MyGreaterGradlePlugin"* だとしよう。私は MyGreaterGradlePlugin レポジトリの中に "step6", "step7" というサブフォルダを設けるかもしれない。そして MyGreaterGradlePluginレポジトリのコードからGradle Plugin Portalにコードをpublishするだろう。すると同じartifactId (**step6**, **step7**, )をGradle Plugin Portalに送り込むことになる。ここで名前の重複が発生する。publish操作は成功するが結果として中身がぐちゃぐちゃになるか、あるいはpublish操作の段階でエラーが発生して失敗するか。どちらにせよ、artifactIdの重複を回避しなければならない。それが唯一の解決方法だ。

どうすればいい？ --- **step6**, **step7** のようなディレクトリ名に対応した名前ではなく、`GreetingsImpl` のような名前、プラグインの氏素性をちゃんと表現するシンボルを使ってpublicationsを生成したい。どうすればできる？

答えはある。次のstep8で試そう。

## step8 プラグインの氏素性を表すartifactIdを指定する

### settings.gradle と build.gradle

#### step8/settings.gradle

    // step8
    rootProject.name = 'GreetingsImpl'

はい、ここが肝心です。 `settings.gradle` ファイルにおいて `rootProject.name` プロパティに  `GreetingsImpl` という値を指定した。ディレクトリ名に由来する `step7` とかいうシンボルではなく、カスタムGradleプラグインの氏素性を表す固有名としてふさわしいシンボルを指定した。`com.gradle.plugin-publish` プラグインはカスタムGradleプラグインの実行クラスのjarファイルのbas nameとして `rootProject.name` の値を設定するから、Mavenレポジトリに現れる名前が `step7` ではなくて `GreetingsImpl` に入れ替わるはずだ。

#### step8/build.gradle

リンク [step8/build.gradle](https://github.com/kazurayam/PublishingCustomGradlePlugin_StepByStep/blob/develop/step8/build.gradle) を参照のこと。

`step8/build.gradle` ファイルは `step7/build.gradle` と全く同じだ。

### ビルドを動かすと何が起きるか

最初にlocal Maven cacheを空っぽにした。

    $ rm -rf  ~/.m2/repository/io/github/kazurayam

それからカスタムGradleプラグインをlocal Maven cacheにパブリッシュした。

    $ gradle publishToMavenLocal

    BUILD SUCCESSFUL in 3s
    10 actionable tasks: 10 executed

local Maven cacheがどうなったか？

    $ tree -xvf ~/.m2/repository/io/github/kazurayam/
    /Users/kazuakiurayama/.m2/repository/io/github/kazurayam
    ├── /Users/kazuakiurayama/.m2/repository/io/github/kazurayam/GreetingsImpl
    │   ├── /Users/kazuakiurayama/.m2/repository/io/github/kazurayam/GreetingsImpl/1.2
    │   │   ├── /Users/kazuakiurayama/.m2/repository/io/github/kazurayam/GreetingImpl/1.2/GreetingsImpl-1.2-javadoc.jar
    │   │   ├── /Users/kazuakiurayama/.m2/repository/io/github/kazurayam/GreetingImpl/1.2/GreetingsImpl-1.2-sources.jar
    │   │   ├── /Users/kazuakiurayama/.m2/repository/io/github/kazurayam/GreetingImpl/1.2/GreetingsImpl-1.2.jar
    │   │   └── /Users/kazuakiurayama/.m2/repository/io/github/kazurayam/GreetingImpl/1.2/GreetingImpl-1.2.pom
    │   └── /Users/kazuakiurayama/.m2/repository/io/github/kazurayam/GreetingImpl/maven-metadata-local.xml
    └── /Users/kazuakiurayama/.m2/repository/io/github/kazurayam/Greetings
        └── /Users/kazuakiurayama/.m2/repository/io/github/kazurayam/Greetings/io.github.kazurayam.Greetings.gradle.plugin
            ├── /Users/kazuakiurayama/.m2/repository/io/github/kazurayam/Greetings/io.github.kazurayam.Greetings.gradle.plugin/1.2
            │   └── /Users/kazuakiurayama/.m2/repository/io/github/kazurayam/Greetings/io.github.kazurayam.Greetings.gradle.plugin/1.2/io.github.kazurayam.Greetings.gradle.plugin-1.2.pom
            └── /Users/kazuakiurayama/.m2/repository/io/github/kazurayam/Greetings/io.github.kazurayam.Greetings.gradle.plugin/maven-metadata-local.xml

    6 directories, 7 files

いい感じだ。これならartifactIdが重複するのを避けることができるだろう。

これをGradle Plugin Portalにパブリッシュしよう。

    $ gradle publishPlugins

    > Task :publishPlugins
    Publishing plugin io.github.kazurayam.Greetings version 1.2
    Publishing artifact build/libs/greeting-gradle-plugin-1.2.jar
    Publishing artifact build/libs/greeting-gradle-plugin-1.2-sources.jar
    Publishing artifact build/libs/greeting-gradle-plugin-1.2-javadoc.jar
    Publishing artifact build/publications/pluginMaven/pom-default.xml
    Activating plugin io.github.kazurayam.Greetings version 1.2

    BUILD SUCCESSFUL in 12s
    8 actionable tasks: 5 executed, 3 up-to-date

やっとできた。

![published to Gradle Plugin Portal](https://kazurayam.github.io/PublishingCustomGradlePlugin_StepByStep/images/8.1_published_to_Gradle_Plugin_Portal.png)

## 結論

次の２箇所に書いてあることが本記事のユニークなところです。Gradle公式ドキュメントには書いてない。ご一読あれ。

- [step3 "Mavenレポジトリの中に何ができているか"](https://qiita.com/kazurayam/items/d2b3cc3fbea576858729#maven%E3%83%AC%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%81%AE%E4%B8%AD%E3%81%AB%E4%BD%95%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%A6%E3%81%84%E3%82%8B%E3%81%8B)
- [step6 "com.gradle.plugin-publishプラグインは何をするのか、何をしないのか"](https://qiita.com/kazurayam/items/d2b3cc3fbea576858729#comgradleplugin-publish-%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%AF%E4%BD%95%E3%82%92%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B%E4%BD%95%E3%82%92%E3%81%97%E3%81%AA%E3%81%84%E3%81%AE%E3%81%8B) 

おしまい。