Jenkins - 如果启动新的,则中止运行构建

时间:2016-11-23 09:39:35

标签: git jenkins groovy multibranch-pipeline

我使用Jenkins和Multibranch Pipeline。我为每个活跃的git分支都有工作。 通过推入git存储库触发新构建。我想要的是在新分支出现在同一分支中时中止当前分支中的运行构建。

例如:我提交并推送到分支feature1。然后在詹金斯开始BUILD_1。我进行了另一次提交,并在feature1仍在运行时推送到分支BUILD_1。我希望BUILD_1被中止并启动BUILD_2

我尝试使用stage concurrency=x选项和stage-lock-milestone功能,但无法解决我的问题。

我也读过这个帖子Stopping Jenkins job in case newer one is started,但我的问题没有解决办法。

你知道解决这个问题吗?

9 个答案:

答案 0 :(得分:18)

使用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 

答案 1 :(得分:12)

如果有人在Jenkins Pipeline Multibranch中需要它,可以在Jenkinsfile中完成,如下所示:

class MyClass{
std::map <KeyType, std::unique_ptr<my_type>> my_map;
...
};

答案 2 :(得分:7)

通过在全局共享库中使用以下脚本来实现它:

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()!

答案 3 :(得分:4)

根据@ 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()
    }
}

答案 4 :(得分:3)

由于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终止。

答案 5 :(得分:3)

在Brandon Squizzato的答案中添加。如果有时跳过构建,则提到的里程碑机制将失败。在for循环中设置较旧的里程碑可以解决此问题。

还要确保您的选项中没有 disableConcurrentBuilds 。否则,管道无法到达里程碑步骤,这将无法正常工作。

def buildNumber = env.BUILD_NUMBER as int
for (int i = 1; i < buildNumber; i++)
{
    milestone(i)
}
milestone(buildNumber)

答案 6 :(得分:0)

我还从以前给定的版本中进行了一些细微调整,以编译出一个版本:

  • 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)
        }
    }
}

答案 7 :(得分:0)

是否有一种方法可以仅从一个给定分支中止构建,并允许来自不同分支的其他构建以同一作业运行。例如,假设我有一个正在运行的Branch1和Branch2。我希望能够中止除最新分支之外的所有当前正在从Branch1执行的构建,同时Branch2开始执行构建,并且我希望Branch2能够执行其构建,因为它是一个不同的分支。可能吗?

澄清;我们有许多分支,因此我们不能仅阻止并发构建,在下一次启动时立即取消上一个,等等。无论使用哪种方法,都必须专门检查分支是否已经为其运行了作业,而不是通常检查该作业。已经开始。 运行代码是在Jenkins中执行系统常规。

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)  

 }
 }
 }

答案 8 :(得分:0)

基于@daggett方法。如果您想在新推送到来时以及在获取更新之前中止正在运行的构建。
1.启用Execute concurrent builds if necessary
2.启用Prepare an environment for the run
3.在Groovy ScriptEvaluated Groovy script

中运行以下代码
import hudson.model.Result
import hudson.model.Run
import jenkins.model.CauseOfInterruption

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

    while (previousBuild != null) {
        if (previousBuild.isInProgress()) {
            def executor = previousBuild.getExecutor()
            if (executor != null) {
                println ">> Aborting older build #${previousBuild.number}"
                def cause = { "interrupted by build #${currentBuild.getId()}" as String } as CauseOfInterruption 
                executor.interrupt(Result.ABORTED, cause)
            }
        }
        previousBuild = previousBuild.getPreviousBuildInProgress()
    }
//}