0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Jenkins Pipelineにて特定のディレクトリに変更があった場合にのみビルドする

Last updated at Posted at 2022-04-11

概要

レポジトリ内にドキュメントがあるなどのケースにおいて、ドキュメントの更新時にJenkinsのJobが実行されるとビルドマシンのコストを無駄に消費してしまう。
そのためJenkins Pipelineにて特定のディレクトリに変更があった場合にのみビルドする方法が求められるが、具体的にその方法がまとまっているページが見つけられなかったためここにまとめる。

PathRestrictionについて

JenkinsPipelineのcheckoutにはPathRestrictionというクラスがあり、そこでincludedRegionsexcludedRegionsを指定することで特定のファイルに差分があった(なかった)時のみビルドすることができる。
これはPipeline SyntaxのページでSample Step:checkout -> SCM: git -> 追加処理: Polling ignores commits in certain pathsを選択することでも確認できる。

しかしこの文法はMultibranch Pipelineにおいて使えない

Multibranch Pipelineというものが具体的に何なのかは未調査。
少なくとも私の環境では以下の条件があるため使用不可能だと思われる。

  1. Jenkinsfileがあるレポジトリとビルド対象のレポジトリが別
    • ジョブをトリガーするかどうかの判定はJenkinsfileのあるレポジトリにしか適応できない可能性がある。
      これはジョブがトリガーされなければcheckout自体がされないためだと推測している。
  2. マスターマシンとスレーブマシンが別
    • 以前libraryについて検証した際に問題になったがJenkinsfileはマスターマシンに展開されるため、スレーブマシンからは参照できなかった。
      • もちろんスレーブ側で明示的にcheckoutすることで参照すること自体は可能。
    • このことが影響している可能性があるが、1の問題の方が大きいと推測する。

具体的な方法

  1. 前回のビルド時のコミットハッシュと今回のコミットハッシュを取得する
    • checkoutの引数に各パラメータがあるためこれを利用する。
      map_vars = checkout( ... )
      print map_vars
      
    • 以下のようにすることで環境変数にパラメータを含めることができる。
      env.GIT_COMMIT = map_vars.get('GIT_COMMIT')
      env.GIT_PREVIOUS_COMMIT = map_vars.get('GIT_PREVIOUS_COMMIT')
      //前回の成功ビルドの場合は以下のものを使う
      //env.GIT_PREVIOUS_COMMIT = map_vars.get('GIT_PREVIOUS_SUCCESSFUL_COMMIT')
      
  2. 変更のあったファイルの一覧を取得する
    • git diff-tree --no-commit-id --name-only -r {ハッシュ1} {ハッシュ2}とすることでファイルの一覧を取得できる。
      (batでのみ確認したが、shなどでも可能であるはず)
      env.CHANGE_FILES = bat(returnStdout: true, script: """
      @git diff-tree --no-commit-id --name-only -r ${env.GIT_PREVIOUS_COMMIT} ${env.GIT_COMMIT}
      """)
      
  3. 対象のディレクトリに変更があった場合はビルドする。
    • 今回実装したのは微妙に意味が違うが、対象のディレクトリに変更がなければABORTすること。
    • 以下のfunctionを呼び出すことで対象のディレクトリに変更がなければABORTできるようになる。
      CheckChanges = {
        includes = [
          "hoge/",
          "sub", // submoduleの場合はパス区切りを入れないようにする
        ]
        for (file in env.CHANGE_FILES.split("\n")) // 1行(ファイル)毎に確認する
        {
          print "### ${file} ###"
          for (x in includes) {
            print "- ${x}"
            if (file.startsWith(x)) return // 対象のディレクトリに変更があったのでそのままreturn
          }
        }
      
        // returnされずに到達した == 対象のファイルに変更がなかった
        currentBuild.result = 'ABORTED'
        error('SkipBuild')
      }
      
    • 補足
      • ABORTEDを選択したのはビルド失敗のFAILUREと区別するため。
      • errorの単独呼び出しだとFAILUREになってしまうのでABORTEDの設定は必須
      • NOT_BUILTの方が適切なように思えるが、手元で試したところFAILUREになってしまったため断念。
  4. 3を呼び出す処理を挟むことで特定のディレクトリに変更があった場合にのみビルドするということが可能になる。
    stage('check changes') { steps { CheckChanges() } }

今回使用したスクリプト全体

CheckChanges = {
  includes = [
    "hoge/",
    "sub", // submoduleの場合はパス区切りを入れないようにする
  ]
  for (file in env.CHANGE_FILES.split("\n"))
  {
    print "### ${file} ###"
    for (x in includes) {
      print "- ${x}"
      if (file.startsWith(x)) return
    }
  }

  currentBuild.result = 'ABORTED'
  error('SkipBuild')
}

pipeline {
  stages {
    stage('Clone') { steps { script {
        map_vars = checkout( ... )
        env.GIT_COMMIT = map_vars.get('GIT_COMMIT')
        //env.GIT_PREVIOUS_COMMIT = map_vars.get('GIT_PREVIOUS_COMMIT')
        env.GIT_PREVIOUS_COMMIT = map_vars.get('GIT_PREVIOUS_SUCCESSFUL_COMMIT')
        env.CHANGE_FILES = bat(returnStdout: true, script: """
        @git diff-tree --no-commit-id --name-only -r ${env.GIT_PREVIOUS_COMMIT} ${env.GIT_COMMIT}
        """)
    } } }
    stage('check changes') { steps { CheckChanges() } }
  }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?