読者です 読者をやめる 読者になる 読者になる

Jenkins Pipeline を使ってみたメモ

次のようなことがやりたかった。

  • テストを実行するジョブと、テストが通った特定(master)のブランチをデプロイするジョブを作る
    • さらにデプロイが完了したブランチを Redmine に同期するジョブも作るけどそれは省略
    • 機能ブランチをテスト → master にマージしてテスト → デプロイ → Redmine に同期
  • デプロイ対象のサーバ自体を Jenkins スレーブにする
    • デプロイは Jenkins がソースをチェックアウトしてローカルコピーするだけ
    • テストとデプロイは別のスレーブで実行されることがある

Pipeline

  • 環境変数 BRANCH_NAME が設定されない
    • シェルで同じ情報を得られなくもないけど
  • properties ビルド世代を指定するとジョブの実行時に一部の設定が消える?
    • 後述の Multibranch Pipeline のように設定してると SCMをポーリング の設定が消える
    • 上書きされてしまうため?
  • checkout scm にコミットのIDが渡されないっぽい
    • 連続でばばばっとプッシュされると同じコミットが2回ビルドされることがある
    • ジョブが実行されたときの最新コミットでビルドされるため
    • 普通は Git のポーリングで検出されたコミットになるはずなのに
    • ので Pipeline の後段で checkout scm していると前段とは異なるコミットになることがある
    • ので前段と後段で同じコミットをチェックアウトするためにはIDを指定する必要がある
    • ので checkout scm は使えない(使いにくい)

次のように test のステージでチェックアウトされたコミットを取得して deploy のステージで使う。

def git_url = 'http://gitlab.example.com/goto/testing.git'
def git_branch
def git_commit

node('test'){
   stage 'test'
   git url: git_url, branch: "**"
   echo "this is test"

   git_branch = sh(script: 'git rev-parse --abbrev-ref HEAD', returnStdout: true).trim()
   git_commit = sh(script: 'git rev-parse HEAD', returnStdout: true).trim()

   if (git_branch == 'master') {
      node('dev'){
         stage 'deploy'
         checkout([$class: 'GitSCM', branches: [[name: git_commit]], userRemoteConfigs: [[url: git_url]]])
         echo "this is deploy"
      }
   }
}

あるいは test のステージのツリーを stash して deploy のステージで unstash でも良いかもしれないけどツリーが巨大だと辛そう。

node('test'){
   stage 'test'
   checkout scm
   echo "this is test"
   def git_branch = sh(script: 'git describe --all', returnStdout: true).trim().replaceFirst(/^\w+\/\w+\//, '')
   if (git_branch == 'master') {
      stash "ok"
      node('dev'){
        stage 'deploy'
        unstash "ok"
        echo "this is deploy"
      }
   }
}

Multibranch Pipeline

  • ブランチごとにジョブが自動的に作成される
    • Jenkinsfile が含まれるブランチだけ対象
  • Branch SourcesGitSingle repository ってのがあるけど何が違うの?
    • Single repository だとジョブは指定した名前で1つだけできる?
    • なぜか設定を保存してもう一度開くと設定がクリアされている?
    • よくわからない
  • Discard old items で保持数に 1 以上を指定するとブランチが消えてもジョブが消えないっぽい?
    • 0 を指定すれば大丈夫だけどそういうものなの?
  • ブランチが消えてジョブが削除されてもワークスペースが削除されない
    • master には下記のディレクトリができる
      • workspace/${multi}/${branch}@script/
    • node で指定した slave には下記のディレクトリができる
      • workspace/${multi}/${branch}/
      • workspace/${multi}/${branch}@tmp/
    • ブランチ作成→削除を繰り返すとものすごいゴミがたまる
  • ブランチジョブのビルド履歴は properties で指定しないとダメ
    • 親の Discard old items が継承されるわけではない

下記のような Jenkinsfile でそこそこ良い感じにできるけど、ブランチ作成→削除でゴミがたまる。

properties properties: [
  [$class: 'BuildDiscarderProperty', strategy: [$class: 'LogRotator', numToKeepStr: '10']]
]

node('test'){
   stage name: 'test', concurrency: 1
   checkout scm
   echo "this is test"
}

if (env.BRANCH_NAME == 'master') {
   node('dev'){
      stage name: 'deploy', concurrency: 1
      checkout scm
      echo "this is deploy"
   }
}

例えばジョブの実行時にカレントディレクトリの兄弟のディレクトリが古かったら削除するとか。

properties properties: [
  [$class: 'BuildDiscarderProperty', strategy: [$class: 'LogRotator', numToKeepStr: '10']]
]

node('test'){
   stage name: 'test', concurrency: 1
   checkout scm
   echo "this is test"
}

if (env.BRANCH_NAME == 'master') {
   node('dev'){
      stage name: 'deploy', concurrency: 1
      checkout scm
      echo "this is deploy"
   }
}

stage 'purge'
node('test'){
  sh "find ../ -mindepth 1 -maxdepth 1 -mmin +60 -print0 | xargs -0 rm -vfr"
}
node('master'){
  sh "find ../ -mindepth 1 -maxdepth 1 -mmin +60 -print0 | xargs -0 rm -vfr"
}

ブランチの有無、あるいはジョブの有無を確認して削除するかどうかを判断できると理想。

まとめ

そんなに多段にしているわけでもないし、Jenkins DSL でいまのところ満足しているので、当面はそのままでいいや。