web-dev-qa-db-ja.com

Jenkins-新しいビルドが開始された場合、実行中のビルドを中止する

私はJenkinsとMultibranch Pipelineを使用します。アクティブなgitブランチごとに仕事があります。新しいビルドは、gitリポジトリのプッシュによってトリガーされます。私が望んでいるのは、同じブランチに新しいビルドが現れた場合、現在のブランチで実行中のビルドを中止することです。

例:ブランチfeature1にコミットしてプッシュします。その後、BUILD_1がJenkinsで開始されました。 feature1の実行中に、別のコミットを行い、ブランチBUILD_1にプッシュします。 BUILD_1を中止し、BUILD_2を開始したい。

stage concurrency=xオプションと stage-lock-milestone 機能を使用しようとしましたが、問題を解決できませんでした。

また、私はこのスレッドを読みました 新しいものが開始された場合にJenkinsジョブを停止します 、しかし、私の問題の解決策はありません。

これに対する解決策を知っていますか?

28
kakty3

Execute concurrent builds if necessaryを使用してプロジェクトのジョブ並列実行を有効にします

最初のビルドステップとしてexecute system groovy scriptを使用します。

import hudson.model.Result
import jenkins.model.CauseOfInterruption

//iterate through current project runs
build.getProject()._getRuns().iterator().each{ run ->
  def exec = run.getExecutor()
  //if the run is not a current build and it has executor (running) then stop it
  if( run!=build && exec!=null ){
    //prepare the cause of interruption
    def cause = { "interrupted by build #${build.getId()}" as String } as CauseOfInterruption 
    exec.interrupt(Result.ABORTED, cause)
  }
}

中断されたジョブにはログがあります:

Build was aborted
interrupted by build #12
Finished: ABORTED 
19
daggett

Jenkins Pipeline Multibranchで必要な場合は、Jenkinsfileで次のように実行できます。

def abortPreviousRunningBuilds() {
  def hi = Hudson.instance
  def pname = env.JOB_NAME.split('/')[0]

  hi.getItem(pname).getItem(env.JOB_BASE_NAME).getBuilds().each{ build ->
    def exec = build.getExecutor()

    if (build.number != currentBuild.number && exec != null) {
      exec.interrupt(
        Result.ABORTED,
        new CauseOfInterruption.UserInterruption(
          "Aborted by #${currentBuild.number}"
        )
      )
      println("Aborted previous running build #${build.number}")
    } else {
      println("Build is not running or is current build, not aborting - #${build.number}")
    }
  }
}
14
midN

Jenkinsスクリプトセキュリティでは、ホワイトリストに登録されていない方法を使用しているため、ここでのソリューションの多くは困難になります。

Jenkinsfileの開始時にこれらのマイルストーンの手順を使用して、これは私のために働いています:

def buildNumber = env.BUILD_NUMBER as int
if (buildNumber > 1) milestone(buildNumber - 1)
milestone(buildNumber)

結果は次のようになります。

  • ビルド1が実行され、マイルストーン1が作成されます
  • ビルド1の実行中に、ビルド2が起動します。マイルストーン1とマイルストーン2があります。マイルストーン1を渡すため、ビルド#1が中止されます。
12

グローバル共有ライブラリに次のスクリプトを含めることで動作するようになりました:

import hudson.model.Result
import jenkins.model.CauseOfInterruption.UserInterruption

def killOldBuilds() {
  while(currentBuild.rawBuild.getPreviousBuildInProgress() != null) {
    currentBuild.rawBuild.getPreviousBuildInProgress().doKill()
  }
}

そして、私のパイプラインでそれを呼び出す:

@Library('librayName')
def pipeline = new killOldBuilds()
[...] 
stage 'purge'
pipeline.killOldBuilds()

編集:oldBuildをどれだけ強力に強制終了するかに応じて、doStop()、doTerm()、またはdoKill()を使用できます。

7
C4stor

@ C4storのアイデアに基づいて、私はこの改善されたバージョンを作成しました... @daggettのバージョンからより読みやすくなりました

import hudson.model.Result
import hudson.model.Run
import jenkins.model.CauseOfInterruption.UserInterruption

def abortPreviousBuilds() {
    Run previousBuild = currentBuild.rawBuild.getPreviousBuildInProgress()

    while (previousBuild != null) {
        if (previousBuild.isInProgress()) {
            def executor = previousBuild.getExecutor()
            if (executor != null) {
                echo ">> Aborting older build #${previousBuild.number}"
                executor.interrupt(Result.ABORTED, new UserInterruption(
                    "Aborted by newer build #${currentBuild.number}"
                ))
            }
        }

        previousBuild = previousBuild.getPreviousBuildInProgress()
    }
}
5

また、以前のバージョンからいくつかのマイナーな調整を加えたバージョンをコンパイルしました。

  • while()ループは、ビルドごとに複数の出力を生成しました
  • userInterruptionは現在、推論文字列ではなくuserIdを予期しており、どこにも推論文字列を表示しません。したがって、これは単にuserIdを提供します
def killOldBuilds(userAborting) {
    def killedBuilds = []
    while(currentBuild.rawBuild.getPreviousBuildInProgress() != null) {
        def build = currentBuild.rawBuild.getPreviousBuildInProgress()
        def exec = build.getExecutor()

        if (build.number != currentBuild.number && exec != null && !killedBuilds.contains(build.number)) {
            exec.interrupt(
                    Result.ABORTED,
                    // The line below actually requires a userId, and doesn't output this text anywhere
                    new CauseOfInterruption.UserInterruption(
                            "${userAborting}"
                    )
            )
            println("Aborted previous running build #${build.number}")
            killedBuilds.add(build.number)
        }
    }
}
0

特定のブランチからビルドを中止し、別のブランチからの他のビルドを同じジョブで実行できるようにする方法はありますか。たとえば、Branch1とBranch2をジョブで実行しているとします。最新のビルドを除く、Branch1から現在実行中のすべてのビルドを中止できると同時に、Branch2がビルドの実行を開始し、別のブランチであるためBranch2がビルドを実行できるようにしたいと考えています。出来ますか?

明確にするために;多くのブランチがあるので、同時ビルドを防止したり、次のビルドが始まるとすぐに最後をキャンセルしたりすることはできません。どのような方法を使用する場合でも、ブランチがジョブを実行しているかどうかを確認する必要があります。すでに実行されています。コードを実行すると、Jenkinsでシステムgroovyが実行されます。

import hudson.model.Result
import jenkins.model.CauseOfInterruption
import groovy.transform.Field
import jenkins.model.*

build.getProject()._getRuns().iterator().each{ run ->
def exec = run.getExecutor()

 //from each run get the branch_name and put it into the list, add it to the list
 def branchName = build.environment.get("GIT_BRANCH")
 def list = []
 list.add(branchName)

  for (i = 0; i <list.size(); i++) {
  if( run!=build && exec!=null && branchName == list[i]){

  exec.interrupt(Result.ABORTED)  

 }
 }
 }
0
mmm20