Jenkins Workflow Pluginでリポジトリ内のスクリプトを読み込む時の注意点

まとめ

  • ブランチをパラメーターにするとスクリプトを読めない
    • 中身ではなく変数名のブランチを探しに行く
    • 多分バグ
  • ファイルから読み込むるスクリプトを書けば解決
    • 公式のflow.groovyを参考に
    • 変数を使おうとすると面倒
      • java.io.Serializableを実装する必要あり

gitリポジトリ内のスクリプトを指定できない

(以下に用意されているdocker上のJenkinsで確認しました)
https://github.com/jenkinsci/workflow-plugin/blob/master/demo/README.md

Workflow Pluginでは、リポジトリ内のgroovyスクリプトを読み込んで実行する機能があります。
この機能を使うことで、リポジトリの内容とそれに対応するビルド手順を同時にバージョン管理出来るため、
ビルド手順の変更がとてもやりやすくなります。

ですが残念ながら、パラメータで指定したブランチをチェックアウトして読み込むと、以下のエラーになります。

> git config remote.origin.url /var/lib/jenkins/workflow-plugin-pipeline-demo # timeout=10
Fetching upstream changes from /var/lib/jenkins/workflow-plugin-pipeline-demo
> git --version # timeout=10
> git -c core.askpass=true fetch --tags --progress /var/lib/jenkins/workflow-plugin-pipeline-demo
+refs/heads/*:refs/remotes/origin/*
> git rev-parse origin/$BRANCH_NAME^{commit} # timeout=10
> git rev-parse $BRANCH_NAME^{commit} # timeout=10

パラメータの内容ではなく、パラメータの名前そのものを探しに行っており、おそらくバグと思われます。
通常のJobでGit Pluginを使うと問題なくパラメータ指定が出来るため、Workflowのバグと思われます。

このバグは、Jenkinsでは事前に設定した特定ブランチしかビルドしない場合は問題ありません。
ですが、様々なブランチで実行する可能性がある場合、JenkinsのJob設定にスクリプトを書かなければならず、
ビルド手順自体の管理が大変になります。

このような場合、スクリプトをロードして実行するスクリプトをJobに設定することで、
指定したブランチからスクリプトを読み込んで実行できます。

スクリプトをロードして実行するスクリプト

(公式のdockerにもスクリプトがありますが、より簡単化したもので説明します)

以下のようなスクリプトをリポジトリのルートに起きます

// build.groovy
def build(){
  node("slave") {
    git url: RIPOSITORY_URL', branch: $BRANCH_NAME"
    sh “rake build"
  }
}
return this

Jobには以下のスクリプトを書きます

def flow
node("master") {
    git url: RIPOSITORY_URL', branch: “$BRANCH_NAME"
    flow = load 'flow.groovy'
}
flow.build()

loadは指定したパスのgroovyスクリプトを読み込み、returnしたオブジェクトを返します。
そのため、これでリポジトリ内のファイルをロードして実行することが出来ます。
また、buildメソッドをnodeの外側で実行することで、ビルド中はmasterのキューを開けておくことが出来ます。

ロード時に変数を保存したい場合

上記の方式の場合、masterにcloneしてその後にslave上に再度cloneしてビルドを始めています。
slaveの選択を凝らない場合は問題ないですが、リリースビルド専用マシンがあったり、
特定のミドルウェアを使うビルドの場合など、slaveの選択をパラメータによって切り替えたい場合があります。

パラメータだけで決定できる場合は問題ありませんが、リポジトリ内のファイルを読み込む必要がある場合、
上記のスクリプトの場合でいうところの、node(“master”)内で処理しなければなりません。
当然Job側にそれを書くとメンテが大変になるため、flow.groovy内に書く必要があります。

このような場合、groovyスクリプトの戻り値で独自クラスを返すことで解決できます。

load関数の戻り値で独自クラスを返す

Jenkinsから以下のようなgroovyスクリプトを読み込むことで、slaveの選択も柔軟にできるようになります。

def buildSlave(slaveName){
  slave(slaveName){
    sh "echo ’slave'"
  }
}

class FlowStruct implements java.io.Serializable{
  def slaveName
  def flow
  public void build(){
    flow.buildSlave(slaveName)
  }
}

f = new FlowStruct()
f.slaveName = readFile(settings/config_${SETTING}").trim()
f.flow = this

return f;

load関数では通常の文はすべて実行されるため、FlowStruct()以降が実行されてから関数が終了します。
また、戻り値は固定ではないため、上記のように独自のクラスを作成して返すことができます。
この際、戻り値はjava.io.Serializableを実装している必要がありますのでご注意ください。

これで、複雑な設定をしていたとしても、すべてリポジトリ内のスクリプトで処理することが出来ます。