Jenkinsの既存のジョブをPipeline化してみる

## はじめに

私が携わっている開発環境では、Unityでゲーム開発を行うにあたって
AssetBundleやアプリのビルドなどでJenkinsを使用しています。

現状ではプラットフォーム毎のビルドジョブや、セクション毎のアセットビルドジョブ等が複数あり
普段使用している作業者は手順を理解していますが、そうでない人からすると特定の環境でチェックするためにはどのジョブを実行すればよいのかわかり辛い状態になっていました。

そこで、既存の独立したジョブをパイプライン化し
この環境はこのビルドパイプラインを実行すればOKというような状態にできるよう対応しました。

## 想定する読者

* Jenkins使っているけどフリースタイルのジョブしか使用したことがない人
* Jenkinsのパイプラインを初めて触る人
* 既存のJenkinsジョブをパイプライン化したいと思っている人
* 将来的にどのように対応したか忘れたときの自分

## 環境

* Jenkinsバージョン：Jenkins 2.377
* Jenkisを使用するPC：macOS Monterey

## やりたいこと

ビルドマシンはMacですが
実際にアプリを実行する側のPCはWindowsなので
Windows環境向けのアプリをビルドします。

現状Windows環境向けのアプリの確認には以下の手順が必要でした。

1. 2Dアセットバンドル、3Dアセットバンドルをそれぞれビルドする
2. ビルドしたアセットバンドルをzipに圧縮してGoogelドライブにアップする
3. アプリビルドジョブのワークスペースにGoogelドライブから先ほどのzipファイルをダウンロードする
4. zipからアセットバンドルを解凍してアプリをビルドする

開発環境の都合でこのように少々面倒な手順が必要なため
これらの一連の流れをパイプラインで処理できるようにします。

※ドライブにアップしているのはドライブ上でアセットバンドルを共有していたためです
（ここはもっと良い方法はあったはず...）

また以下の要件も満たせるようにします。

* アセットビルドジョブは並行でビルドさせたい
* 各ジョブに共通する適当なビルドパラメータを渡したい

### 各ジョブ

* 2Dアセットバンドルビルドジョブ
  * ジョブ名：build-asset-2d
* 3Dアセットバンドルビルドジョブ
  * ジョブ名：build-asset-3d
* アプリビルドジョブ
  * ジョブ名：build-app
* Googelドライブにあるzipファイル(アセットバンドル)をアプリビルドジョブのワークスペースに展開するジョブ(パラメータなし)
  * ジョブ名：download-asset-bundle

## 対応

### ジョブの作成

パイプラインジョブの作成はJenkisの新規ジョブ作成時に
「パイプライン」を選択して作成します。

![ss01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2938978/cb165e19-36b9-3d3c-2c5d-6e3eadcd11bd.png)

ジョブを作成したらジョブの設定画面になります。
この画面の最下部に「パイプライン」の項目があり
ここに**スクリプトを書くことで**パイプラインジョブを実行します。
![ss05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2938978/2bdf4643-71f9-8440-f221-3d1e2f043e86.png)

使用するスクリプト言語は **「Groovy」** になります。
そんな言語使った事が無いという方
大丈夫です。私も初見でしたが何とかなります。

先ずはシンプルな形式で
2Dアセットバンドルのジョブ（build-asset-2d）が実行できるようにしてみます。

```Groovy
pipeline {
  agent any
  stages {
    stage("asset-2d") {
      steps {
        build job: "build-asset-2d"
      }
    }
  }
}
```

ざっくり説明すると

**pipeline**が全体の枠になります。

**stage**がジョブ一つ処理の枠です。複数のジョブを実行する場合、ここが増えていきます。
ここの名称は任意で問題ないです。

**stages**は**stage**を囲む枠です。

**build job**で実行するジョブ名を指定します。

※**agent**は実行ノード制御の指定ですが、今回は特に何も指定しない**agent any**にしておきます。


これで「build-asset-2d」ジョブのビルドが実行できるようになりました。

### パラメータの追加

次に上で述べた以下の要件を満たすためにこのジョブにパラメータを渡してみます。
> 各ジョブに共通する適当なビルドパラメータを渡したい

パイプライン側の設定で
各ジョブで共通して使用している以下のパラメータを定義します。

* branch：テキスト
ビルドを行うGitのブランチ名
![ss02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2938978/9ae50fb4-c6fe-9a4f-d73c-f8c5f0778d01.png)

* executor：選択
ジョブの実行者
![ss03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2938978/4ecbcd93-b3ba-dd10-8e34-5060e7c12900.png)

* message：テキスト
チャットツールにビルド開始・終了を通知する際の追加メッセージ
![ss04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2938978/798a8a5e-9a43-00c0-2b0e-f7b976cf3785.png)

これらを先ほどのスクリプトで
ジョブにパラメータを渡すように追記します。

```Groovy
pipeline {
  agent any
  stages {
    stage("asset-2d") {
      steps {
        build job: "build-asset-2d",
        parameters: [
          string(name: "branch", value: params.branch),
          string(name: "executor", value: params.executor),
          string(name: "message", value: params.message)
        ]
      }
    }
  }
}
```

新たに**parameters**の指定を追加しました。
この中で、ジョブに渡すパラメータを記述します。

パラメータの型がテキストの場合は見ての通り**string**でパラメータを渡します。

パラメータの型が選択の場合、こちらも**string**型でパイプラインジョブ側で選択したパラメータを渡す形になります。

**name**は渡す先のパラメータの名称にする必要があります。

そして、**value**の部分で**params.名前**でパイプラインジョブ側のパラメータを渡すことができます。

※注意点として**parameters**の定義前、**parameters**複数定義の際にはカンマが必要で抜けているとエラーになります。

また、以下のようにすることで
```Groovy
pipeline {
  agent any
  stages {
    stage("asset-2d") {
      steps {
        build job: "build-asset-2d",
        parameters: [
          string(name: "branch", value: "develop"),
          string(name: "executor", value: "Aさん"),
          string(name: "message", value: "2Dアセットビルド")
        ]
      }
    }
  }
}
```
固定のパラメータを渡すことも可能です。


### ジョブを並行ビルド化

次にもう一つの要件を満たすためにアセットビルドジョブを並行でビルドできるようにします。
>アセットビルドジョブは並行でビルドさせたい

並行処理を行うには、並行処理を行いたい**stage**を追加し
**parallel**という枠で囲みます。

先ほどのスクリプトに「build-asset-3d」ジョブを追加してみます。

```Groovy
pipeline {
  agent any
  stages {
    stage("parallel") {
      parallel {
        stage("asset-2d") {
          steps {
            build job: "build-asset-2d",
            parameters: [
              string(name: "branch", value: params.branch),
              string(name: "executor", value: params.executor),
              string(name: "message", value: params.message)
            ]
          }
        }
        stage("asset-3d") {
          steps {
            build job: "build-asset-3d",
            parameters: [
              string(name: "branch", value: params.branch),
              string(name: "executor", value: params.executor),
              string(name: "message", value: params.message)
            ]
          }
        }
      }
    }
  }
}
```

(インデントが増えてどんどん見辛く...(^^;) )

これで「build-asset-2d」と「build-asset-3d」のふたつのジョブを同時並行してビルドしてくれるようになりました。

### 仕上げ

最後に残りの二つのジョブ「build-app」「download-asset-bundle」を処理するように追記していきます。

実行順としては「アセット並行ビルド」→「download-asset-bundle」→「build-app」なので
その順番になるようにスクリプトに追記していきます。


```Groovy
pipeline {
  agent any
  stages {
    stage("parallel") {
      parallel {
        stage("asset-2d") {
          steps {
            build job: "build-asset-2d",
            parameters: [
              string(name: "branch", value: params.branch),
              string(name: "executor", value: params.executor),
              string(name: "message", value: params.message)
            ]
          }
        }
        stage("asset-3d") {
          steps {
            build job: "build-asset-3d",
            parameters: [
              string(name: "branch", value: params.branch),
              string(name: "executor", value: params.executor),
              string(name: "message", value: params.message)
            ]
          }
        }
      }
    }
    stage("dl-asset") {
      steps {
        build job: "download-asset-bundle"
        // 「download-asset-bundle」はパラメータが無いのでこれだけ
      }
    }
    stage("dl-asset") {
      steps {
        build job: "download-asset-bundle",
        parameters: [
          string(name: "branch", value: params.branch),
          string(name: "executor", value: params.executor),
          string(name: "message", value: params.message)
        ]
      }
    }
  }
}
```

以上で「アセット並行ビルド」→「download-asset-bundle」→「build-app」の順にジョブを実行してくれる
ビルドパイプラインが完成しました。

## さいごに

今回は既存の独立したJenkinsジョブをパイプラインにして一括で実行できるように対応した内容を記事にしました。

そもそももっと効率のよい手法があるかもなのですが
既存の環境に可能な限り影響を与えたくないケースでの手法としてはアリなのかなと思います。

パイプライン化には一応ですがデメリットもありまして
Jenkinsのビルドを「パイプラインジョブ本体」+「パイプラインが呼び出しているビルド」というように
一つ余分にビルドが走る形になるので、ビルド数を想定のギリギリで運用している場合などは弊害があるかもしれないです。