Jenkinsのサンプルプラグインを動かしてみる

# 要約
Jenkinsのプラグインは手軽に作れるという噂を聞いたので、チュートリアルを試しました。
https://wiki.jenkins.io/display/JENKINS/Plugin+tutorial

チュートリアルでは、ビルドコンソールにHello worldと表示するプラグインを作ります。
Pipeline用の関数も作ってくれます。

# 環境
macOS Sierra v.10.12.6

# 手順
## mavenのインストール
```
$ brew install maven
```

```
$ mvn -v
Apache Maven 3.5.2 (...)
Maven home: ...
Java version: 1.8.0_144, ...
...
```
Mavenは3以上、JDKは6.0以上が必要です。

Jenkinsのプラグイン開発では圧倒的にMavenが使われているようです。
gradleでも（+groovyでも）開発できるようですが今は指示通りにMavenで進みます。
https://qiita.com/sue445/items/e0ea16d9c38d949114eb

### mavenの設定ファイルの設定
以下の設定ファイルを作っておくと、プラグイン作成コマンドを打つときに短くて済むので便利です。

```~/.m2/settings.xml 
<settings>
  <pluginGroups>
    <pluginGroup>org.jenkins-ci.tools</pluginGroup>
  </pluginGroups>
 
  <profiles>
    <!-- Give access to Jenkins plugins -->
    <profile>
      <id>jenkins</id>
      <activation>
        <activeByDefault>true</activeByDefault> <!-- change this to false, if you don't like to have it on per default -->
      </activation>
      <repositories>
        <repository>
          <id>repo.jenkins-ci.org</id>
          <url>https://repo.jenkins-ci.org/public/</url>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>repo.jenkins-ci.org</id>
          <url>https://repo.jenkins-ci.org/public/</url>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>
  <mirrors>
    <mirror>
      <id>repo.jenkins-ci.org</id>
      <url>https://repo.jenkins-ci.org/public/</url>
      <mirrorOf>m.g.o-public</mirrorOf>
    </mirror>
  </mirrors>
</settings>
```

## サンプルプラグインの作成

`$ mvn archetype:generate -Dfilter=io.jenkins.archetypes:plugin`
テンプレートが用意されているのでそれを使います。
今回使いたいのは3つ示されたうちの3つ目、hello-world-pluginです。
上記のコマンドを打つとインタラクティブにセットアップをすすめることができます。

```
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:3.0.1:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:3.0.1:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO]
[INFO]
[INFO] --- maven-archetype-plugin:3.0.1:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: remote -> io.jenkins.archetypes:empty-plugin (Skeleton of a Jenkins plugin with a POM and an empty source tree.)
2: remote -> io.jenkins.archetypes:global-configuration-plugin (Skeleton of a Jenkins plugin with a POM and an example piece of global configuration.)
3: remote -> io.jenkins.archetypes:hello-world-plugin (Skeleton of a Jenkins plugin with a POM and an example build step.)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 3
Choose io.jenkins.archetypes:hello-world-plugin version:
1: 1.1
2: 1.2
3: 1.3
Choose a number: 3: 1
[INFO] Using property: groupId = unused
Define value for property 'artifactId': hello-jenkins-plugin-world
Define value for property 'version' 1.0-SNAPSHOT: :
[INFO] Using property: package = org.jenkinsci.plugins.sample
Confirm properties configuration:
groupId: unused
artifactId: hello-jenkins-plugin-world
version: 1.0-SNAPSHOT
package: org.jenkinsci.plugins.sample
 Y: : Y
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: hello-world-plugin:1.1
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: unused
[INFO] Parameter: artifactId, Value: hello-jenkins-plugin-world
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: org.jenkinsci.plugins.sample
[INFO] Parameter: packageInPathFormat, Value: org/jenkinsci/plugins/sample
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: org.jenkinsci.plugins.sample
[INFO] Parameter: groupId, Value: unused
[INFO] Parameter: artifactId, Value: hello-jenkins-plugin-world
[INFO] Project created from Archetype in dir: ~/project/jenkins-plugins/hello-jenkins-plugin-world
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:46 min
[INFO] Finished at: 2018-02-25T23:00:51+09:00
[INFO] Final Memory: 15M/177M
[INFO] ------------------------------------------------------------------------
```

- hello-world-pluginのバージョンでどれを選ぶべきかはよくわからなかったので、ひとまず1.3で進めておく。
- groupIdが空欄になってしまったが公開時には削除するらしいので問題なし。artifactIdがプラグイン名(ハイフン区切り)。

## サンプルプラグインのビルド
`$ cd hello-jenkins-plugin-world`
`$ mvn install`
これでビルドが行われ、プラグインの実体である `./target/hello-jenkins-plugin-world.hpi` が生成されました！

## サンプルプラグインのデバッグ実行
以下のコマンドでJenkinsがプラグインをインストールした状態で立ち上がります。
```
$ export MAVEN_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n"
$ mvn hpi:run
```
ジョブが何も定義されていない状態なので、ブラウザ上からフリースタイルジョブとパイプラインジョブを適当な名前でひとつずつ作ります。
内容は空です。

### フリースタイルジョブでのサンプルプラグインの挙動
![スクリーンショット 2018-02-25 23.27.28.png](https://qiita-image-store.s3.amazonaws.com/0/66517/666a8943-e256-6ef2-0863-87d6bbe5be71.png)
「Say hello world」という手順が追加されています。
![スクリーンショット 2018-02-25 23.28.39.png](https://qiita-image-store.s3.amazonaws.com/0/66517/5e092731-b3ed-6426-a105-173d296693b6.png)
適当な名前を入れておきます。Applyして保存したらビルドを実行します。
コンソールを確認します。
![スクリーンショット 2018-02-25 23.29.23.png](https://qiita-image-store.s3.amazonaws.com/0/66517/e451df2f-335e-e9e8-eb33-a86b4faee3ce.png)
「Hello, ほげほげ！」と先ほど入れた名前宛にメッセージが表示されるようになりました！


![スクリーンショット 2018-02-25 23.30.34.png](https://qiita-image-store.s3.amazonaws.com/0/66517/18846e1a-b8c1-3427-ea07-68b44252121d.png)
フランス語に特徴的な文字が入ると警告されます。
![スクリーンショット 2018-02-25 23.32.01.png](https://qiita-image-store.s3.amazonaws.com/0/66517/e9e24b8f-1693-c033-fc9e-416e0371bc95.png)!

フランス語の名前なのでチェックを入れます。
![スクリーンショット 2018-02-25 23.32.22.png](https://qiita-image-store.s3.amazonaws.com/0/66517/d8fdfeb5-2c82-61fe-d133-45f92cbda793.png)
この状態でビルドをするとフランス語で挨拶されました！

### パイプラインジョブでのサンプルプラグインの挙動
パイプラインジョブからこのプラグインを使いたいと思います。
パイプラインのビルドスクリプトからこのプラグインを使うためにgreetという関数が使えるように定義されています。

```HelloWorldBuilder.java
    @Symbol("greet")
    @Extension
    public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {
    ...
}
```

ビルドスクリプトはメッセージを表示するだけのものを作り、Apply&保存してビルドします:

```
node {
   greet 'hogehoge'
}
```

実行結果のコンソール画面: 
![スクリーンショット 2018-02-25 23.38.01.png](https://qiita-image-store.s3.amazonaws.com/0/66517/3a4e33e8-2ced-1c58-3c77-f42bee540c70.png)
パイプラインでもサンプルプラグインが正しく機能していることを確認できました。

フランス語フラグをつけるならばこんな感じです:

```
node {
   greet name:"hogehoge", useFrench:true
}
```

実行結果のコンソール画面:
![スクリーンショット 2018-02-26 22.00.44.png](https://qiita-image-store.s3.amazonaws.com/0/66517/88eae642-c1d2-b1d9-2209-b72fba75b8ec.png)
パイプラインでもフランス語で挨拶できました！

# 感想
ひとまずサンプルプラグインを動かすことができました。
mavenはxmlベースでgradleに比べると記述量が多くて見にくいのが難点ですね。
他のプラグインやリファレンスを見ていけば何か作るのはそれほどハードルは高くなさそうだと感じました。