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

kak*_*ty3 31 git groovy jenkins multibranch-pipeline

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

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

我试图使用stage concurrency=x选项和stage-lock-milestone功能,但没有设法解决我的问题.

此外,我已经阅读了这个线程停止Jenkins工作,以防新的一个启动,但我的问题没有解决方案.

你知道任何解决方案吗?

小智 26

从 Jenkins 工作流作业插件版本 2.42 中,您可以简单地执行以下操作

// as a step in a scripted pipeline    
properties([disableConcurrentBuilds(abortPrevious: true)]) 
// as a directive in a declarative pipeline
options { disableConcurrentBuilds abortPrevious: true } 
Run Code Online (Sandbox Code Playgroud)

在评论中找到解决方案 https://issues.jenkins.io/browse/JENKINS-43353


dag*_*ett 20

为您的项目启用作业并行运行 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)
  }
}
Run Code Online (Sandbox Code Playgroud)

在中断的工作中会有一个日志:

Build was aborted
interrupted by build #12
Finished: ABORTED 
Run Code Online (Sandbox Code Playgroud)

  • 对于任何像我一样得到这个答案并且无法运行代码的人 - 从闭包中删除id.基本上改变行:`build.getProject()._ getRuns().each {id,run->`into`build.getProject()._ getRuns().each {run - >` (2认同)

mid*_*idN 16

如果有人在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}")
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 也许值得检查一下内部版本号是否低于当前版本。否则,您可能会杀死更新的版本。 (2认同)

Bra*_*ato 15

借助Jenkins脚本安全性,此处的许多解决方案都变得困难,因为它们使用的是非白名单方法。

在Jenkinsfile的开始处有了这些里程碑式的步骤,这对我有用:

def buildNumber = env.BUILD_NUMBER as int
if (buildNumber > 1) milestone(buildNumber - 1)
milestone(buildNumber)
Run Code Online (Sandbox Code Playgroud)

结果将是:

  • 生成1运行并创建里程碑1
  • 运行版本1时,将生成版本2。它具有里程碑1和里程碑2。它经过里程碑1,这会导致构建#1中止。

  • 里程碑是否特定于分支? (2认同)

C4s*_*tor 7

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

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

def killOldBuilds() {
  while(currentBuild.rawBuild.getPreviousBuildInProgress() != null) {
    currentBuild.rawBuild.getPreviousBuildInProgress().doKill()
  }
}
Run Code Online (Sandbox Code Playgroud)

并在我的管道中调用它:

@Library('librayName')
def pipeline = new killOldBuilds()
[...] 
stage 'purge'
pipeline.killOldBuilds()
Run Code Online (Sandbox Code Playgroud)

编辑:根据您想要杀死oldBuild的强度,您可以使用doStop(),doTerm()或doKill()!


Ste*_*zis 6

基于@ 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()
    }
}
Run Code Online (Sandbox Code Playgroud)


Cod*_*key 5

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

还要确保您的选项中没有disableConcurrentBuilds。否则管道不会到达里程碑步骤,这将不起作用。

def buildNumber = env.BUILD_NUMBER as int
for (int i = 1; i < buildNumber; i++)
{
    milestone(i)
}
milestone(buildNumber)
Run Code Online (Sandbox Code Playgroud)

  • 这样做的潜在问题是,当您有大量构建时,创建如此多的里程碑可能会占用相当多的时间。我不知道事情在什么时候发生了变化——创造了许多对我来说很快就能实现的里程碑。最近,创建一个里程碑大约需要半秒——如果您使用的是 build #900,显然不理想。所以我创建了不使用 for 循环的解决方案。 (3认同)