Jenkinsfileを構造的に整理する

 Jenkins PipelineのJenkinsfileでテスト並列実行を最適化する - 千里霧中 つながりの話です。Jenkinsfileでは、groovyの構文を活用してJenkins Pipelineの実装を整理できます。

処理をメソッドとして抜き出す

 まずJenkinsfileではメソッドやクラスを任意に定義可能です。これにより重複する記述をメソッドにまとめるといったことが可能になります。
 例えば以下のJenkinsfileを例とします。

pipeline { 
  agent any 
  stages {
    stage('Test') { 
      steps {
        parallel 'run_test':{
          node('node1') {
            println 'run test code'
          }
          node('node2') {
            println 'run test code'
          }
        }
      }
    }
  }
}

 仮に「println 'run test code'」が長大な重複コードとすると、Jenkinsfileでは次のようにメソッドrun_test_code()としてまとめられます。

def run_test_code() {
  println 'run test code'
}

pipeline { 
  agent any 
  stages {
    stage('Test') { 
      steps {
        parallel 'run_test':{
          node('node1') {
            run_test_code()
          }
          node('node2') {
            run_test_code()
          }
        }
      }
    }
  }
}

Pipeline定義を動的に生成する

 Pipelineの定義はマップとして動的に生成することも可能です。ループ処理で定義の繰り返しを生成したり、条件分岐で定義を切り替えたりすることで、複雑なPipeline定義をシンプルなコードで実装できる場合があります。

 例えば以下のような重複の多いJenkinsfileを例に取ります。

//node1、node2でテスト並列実行
parallel 'node1' :{
  node('node1') {
    println 'run test code'
  }
, 'node2' :{
  node('node2') :{
    println 'run test code'
  }
}

これはfor文で以下のように重複部分をまとめられます。実行時の処理は前と同じです。

//node1、node2で並列実行
List node_list = ['node1', 'node2']
Map branches = [:]

for (int i = 0; i < node_list.size(); i++) {
  String node_name = node_list[i]
  branches[node_name] = {
    node(node_name) {
      println 'run test code'
    }
  }
}
parallel(branches)

ファイルを分ける

 Jenkinsfileは他ファイルのGroovyコードを呼び出せます。これにより、複数のJenkisfileの共通部分を外部モジュールとして切り出す、長大なJenkinsfileをファイル分割する、外部のライブラリを利用する、といったことが可能になります。

 例えば標準のJSONライブラリを使う場合は、次のような通常のimport構文で実装できます。

import groovy.json.JsonOutput
pipeline {
  agent any 
  stages {
    stage('Test') { 
      steps {
        parallel 'run test':{
          node('node1') {
            def hoge = JsonOutput.toJson([text : "dummy"])
            ...
          }
        }
      }
    }
  }
}