Jenkins Pipeline で Gitlab連携がうまくいかない

Jenkins 2.0がリリースされ、Pipelineプラグインが標準機能になりました。 Pipelineプラグインはジョブの処理をスクリプトファイルに記載するので、ジョブの設定の修正がしやすくなったり、バージョン管理できたり、便利に使えそうな機能です。

そこで、社内で現在行っているJenkinsジョブをPipelineに乗り換えるべく、GitlabとJenkinsの連携を Pipeline で実現する方法を調べみました。
ですが、結論としてはうまくいきませんでした。
上手くいかなかったことではあるのですが、折角いろいろ調べたので調べたことを残しておこうと思います。


## 実現したい内容

まず、具体的に実現したいことをあげておきます。

* JenkinsにてGitlab上のMerge Requestに対応するジョブを用意
* GitlabにてMerge Requestを作成すると、そのブランチに対してJenkinsがジョブを実行する
* GitlabのMerge Request画面では、ジョブが実行中であることが表示される。その後、ジョブが終了すると成功・失敗の結果が表示される
* ジョブが終了すると、MergeRequestにJenkinsからのコメントが追加される

上記のことは、通常形式のジョブに対してJenkins の Gitlab Plugin を使って実現できています。

  https://github.com/jenkinsci/gitlab-plugin


## 連携設定の内容

次に、これをPipelineプラグインで実現するための設定方法をまとめます。
実際には最初に述べた通り、期待通りにはいかなかったのですが、Pipeline プラグインの使い方としては大筋このような方法になると思います。


(1) Jenkinsのシステム設定にて、Gitlab Pluginの設定をする

| 項目 | 値 |
| --- | --- |
| Gitlab | |
| - Gitlab host URL | http://gitlab.example.com/ |
| - API Token | Jenkins連携用のユーザのAPIトークン |

(2) Jenkins上で "Pipeline" 形式のジョブを作成し、ジョブ設定を以下のように設定する
  (今回の内容に関連する部分のみ抜粋)

| 項目 | 値 |
| --- | --- |
| ビルド・トリガ |  |
| - Build when a change is pushed to GitLab | on |
| - - Build on Merge Request Events | on |
| - - Build on Push Events | off |
| - - Rebuild open Merge Requests | On puth to source branch |
| - - Use GitLab CI features (GitLab 8.1 required!) | on |
| Pipeline | |
| - Definition | Pipeline script from SCM |
| - - SCM | git  |
| - - Repository URL | http://gitlab.example.com/root/flow1.git |
| - - Branch Specifier (blank for 'any') | origin/jenkins |
| - - Script Path | Jenkinsfile |

(3) Gitlabの対象プロジェクトにWebHookを追加する

このとき、以下にチェックをつける

  * Push events
  * Merge Request events

(4) プロジェクトに Pipeline用ブランチを作成し、そこにJenkinsfileを作成する

```bash
# Jenkinsのジョブ設定で指定したブランチを作成
$ git checkout -b jenkins
```


```groovy:Jenkinsfile
stage 'Test'
node {
    // Gitlab Pluginの gitlabSourceBranch 変数にブランチを checkout する
    checkout([$class: 'GitSCM',
      userRemoteConfigs: [[
        url: 'http://gitlab.example.com/main/sample.git'
      ]],
      branches: [[name: "origin/${gitlabSourceBranch}"]] // 変数展開できるように " で囲うこと
    ])

    // テスト実行
    sh "${tool 'Maven3'}/bin/mvn -B test"

    // テスト結果レポート作成
    step([$class: 'JUnitResultArchiver', testResults: 'target/surefire-reports/*.xml'])
}
```

(5) Pipeline用のジョブをJenkins上から手動で一度実行する

Git関連のプラグインは、必ず一度手動実行しておく必要があります。
手動実行することでJenkinsノード上でgitのcheckoutを行っているようです。


## 解説

先に記載した設定方法で以下のことは実現できました。

* GitlabのMergeRequestの作成やコミット追加で、ジョブを実行
* MergeRequest対象のブランチを対象にテストを実行

ですが、以下ができませんでした。

* GitlabのMergeRequestに表示されるビルド状態が Pending のままになり、ビルドの成功・失敗のステータスが反映されない

GitlabでMergeRequestにテスト結果が表示されないと、その内容の合否を判断できないので結構困ります。

多少コードを読んでみましたが、Gitlabプラグインはジョブの実行完了時のコールバック処理にてGitlabのAPIを呼び出してビルドステータスを変更するようになっています。ところが、Pipelineプラグインでは通常のジョブとは処理が異なっているらしく、このコールバックが実行されていないようです。


## Pipeline用ジョブのブランチ指定

ちなみに、前述のJenkinsのジョブの設定において、ターゲットとするgitのブランチ名を "origin/jenkins" に固定しているのには理由があります。

既にGitlabプラグインを利用されている方であれば、ここは "origin/${gitlabSourceBranch}"と指定ればよいように思われるかもしれません。ですが、Pipeline用ジョブのSCM設定は、通常のジョブのそれと異なり変数が展開されません。つまり、１つのブランチを固定で設定するしかありません。(ちなみに、ビルドパラメータで実行ごとにブランチを変えることもできません)
というのも、ここでのSCM設定は普段のジョブ実行用のソース取得元の設定ではなく、Pipelineプラグイン用の "Jenkinsfile" を取得するための設定だからです。普段のジョブ設定は、このJenkinsfile内に記載される `node {...}`ブロック内に記載される `checkout()`メソッドの部分が対応しています。

この特性があるためPipelineプラグインでは、ブランチごとに適したJenkinsfileにてビルドを行う、ということができません。必ず全ブランチが同じJenkinsfileの内容でビルドすることになります。
一応、この問題に対処する方法として、"Multibranch Pipeline"という形式のジョブを使う方法が用意されています。この形式のジョブでは、自動でブランチの一覧を取得してそれらに対するビルド用のジョブを仮想的に作成します。そして、ブランチに対するコミットがあると、そのブランチのJenkinsfileを用いてPipelineが実行されます。
ところが、この方式のジョブにはGitlabプラグインが機能せず、GitlabのMergeRequestと連携することができませんでした。


## まとめ

これまでのJenkinsプラグインは、そのままで即座にPipelineで利用できないものがあり、対応が進められているようです。以下のページでプラグインの対応状況がまとめられていて、GitlabPluginについても対応済み扱いになっています。

  https://github.com/jenkinsci/pipeline-plugin/blob/master/COMPATIBILITY.md

ところが「supported as of 1.1.26」と記載されていますが、このバージョンの時点ではPipelineのスクリプト内で `gitlabSourceBranch`などの変数が定義されておらず、対象ブランチをチェックアウトすることができませんでした。そして現在の 1.1.32 についても上記のような問題が残っています。

どうも、Pipelineプラグイン側で、何ができれば対応完了と言ってよいのかがちゃんと定められていないような感じがします。というのも、本記事で紹介したGitlabの連携方法の具体的な導入手順は、Pipelineプラグイン側の文書やGitlabプラグイン側の文書を結構探しましたが見つかりませんでした。開発されている方の中でも、そもそもPipeline上でGitlab連携を試していないような状態なのではないかと思います。


個人的な結論としては、Pipelineプラグインはまだ未整備の部分もあるようなので、使いどころを選ぶ必要があるようです。例えば、以下の要件では利用できると思います。

* 対象のジョブは、手動実行、もしくは定期実行する
* プラグインを使って他サービス・システムと連携しない

逆に、これに反するような使い方をしているものについては、完全にPipelineプラグインだけで実現するのは大変になる可能性が高いと思います。


------
追記: 2016/06/17

JenkinsのGitlab Pluginが1.2.x系になった時に、大きく仕組みが変わってPipelineへの対応も強化されたようです。それによって、Pipelineでもコミットステータスの連携ができるようになったみたいです。
https://github.com/jenkinsci/gitlab-plugin