在 Jenkins 中从管道中止当前构建

Posted

技术标签:

【中文标题】在 Jenkins 中从管道中止当前构建【英文标题】:Abort current build from pipeline in Jenkins 【发布时间】:2017-07-28 19:12:48 【问题描述】:

我有一个包含多个阶段的 Jenkins 管道,例如:

node("nodename") 
  stage("Checkout") 
    git ....
  
  stage("Check Preconditions") 
    ...
    if(!continueBuild) 
      // What do I put here? currentBuild.xxx ?
    
  
  stage("Do a lot of work") 
    ....
  

如果不满足某些先决条件并且没有实际工作要做,我希望能够取消(不失败)构建。我怎样才能做到这一点?我知道currentBuild 变量可用,但我找不到它的文档。

【问题讨论】:

【参考方案1】:

您可以将构建标记为 ABORTED,然后使用error 步骤使构建停止:

if (!continueBuild) 
    currentBuild.result = 'ABORTED'
    error('Stopping early…')

在阶段视图中,这将显示构建在此阶段停止,但整个构建将被标记为中止,而不是失败(请参阅构建 #9 的灰色图标):

【讨论】:

太棒了。有没有办法提前成功退出? 抱歉,已经找到。只需返回 node-level 而不是 stage-level 即可使管道提前成功退出。 if (!continueBuild)...我们如何设置“ContinueBuild”值? @NishantKansal 这只是原始发帖人提到的变量名。语法为def continueBuild = false(或true),但由您决定何时要中止构建,例如通过调用方法def continueBuild = makeSomeDecision() 仅供参考,hudson/model/Result.java【参考方案2】:

经过一些测试,我想出了以下解决方案:

def autoCancelled = false

try 
  stage('checkout') 
    ...
    if (your condition) 
      autoCancelled = true
      error('Aborting the build to prevent a loop.')
    
  
 catch (e) 
  if (autoCancelled) 
    currentBuild.result = 'ABORTED'
    echo('Skipping mail notification')
    // return here instead of throwing error to keep the build "green"
    return
  
  // normal error handling
  throw e

这将导致以下阶段视图:

失败阶段

如果你不喜欢失败的阶段,你必须使用返回。但请注意,您必须跳过每个阶段或包装器。

def autoCancelled = false

try 
  stage('checkout') 
    ...
    if (your condition) 
      autoCancelled = true
      return
    
  
  if (autoCancelled) 
    error('Aborting the build to prevent a loop.')
    // return would be also possible but you have to be sure to quit all stages and wrapper properly
    // return
  
 catch (e) 
  if (autoCancelled) 
    currentBuild.result = 'ABORTED'
    echo('Skipping mail notification')
    // return here instead of throwing error to keep the build "green"
    return
  
  // normal error handling
  throw e

结果:

自定义错误作为指标

您也可以使用自定义消息代替局部变量:

final autoCancelledError = 'autoCancelled'

try 
  stage('checkout') 
    ...
    if (your condition) 
      echo('Aborting the build to prevent a loop.')
      error(autoCancelledError)
    
  
 catch (e) 
  if (e.message == autoCancelledError) 
    currentBuild.result = 'ABORTED'
    echo('Skipping mail notification')
    // return here instead of throwing error to keep the build "green"
    return
  
  // normal error handling
  throw e

【讨论】:

我需要更清楚一点:如果(您的条件)...我有一个与先前和当前提交 id 匹配的 shell 脚本来决定构建是应该继续还是停止。是不是我必须将该 shell 脚本的退出状态传递给 if(您的条件)?如果是,那么如何?请帮忙。【参考方案3】:

按照来自 Jenkins 的 documentation,您应该能够生成错误以停止构建并像这样设置构建结果:

currentBuild.result = 'ABORTED'

希望对您有所帮助。

【讨论】:

我刚试过这个,之后有一个回声,它并没有阻止管道的其余部分运行。 该命令只设置构建结果。要停止管道,您必须生成信号错误:error('error message')throw new Exception() 仅当您想查看堆栈跟踪时才使用throw new Exception()【参考方案4】:

我以声明方式处理如下:

基于 catchError 块,它将执行 post 块。 如果发布结果属于失败类别,则会执行错误块以停止即将到来的阶段,如生产、PreProd 等。

pipeline 

  agent any

  stages 
    stage('Build') 
      steps 
        catchError 
          sh '/bin/bash path/To/Filename.sh'
        
      
      post 
        success 
          echo 'Build stage successful'
        
        failure 
          echo 'Compile stage failed'
          error('Build is aborted due to failure of build stage')

        
      
    
    stage('Production') 
      steps 
        sh '/bin/bash path/To/Filename.sh'
      
    
  

【讨论】:

我不知道您的设置,但是授予 sudo bash 任何权限都不是很安全。更好的是,只对您需要的脚本授予 sudo 权限,并在之前仔细检查它。【参考方案5】:

受到所有答案的启发,我将所有内容整合到一个脚本化管道中。请记住,这不是声明式管道。

要让这个例子正常工作,你需要:

QuickFIX 形成这个答案Jenkins CI Pipeline Scripts not permitted to use method groovy.lang.GroovyObject discord 通知插件 - https://plugins.jenkins.io/discord-notifier/ 代码填写的Discord频道webhook url

我的想法是如果管道是“重播”而不是由“运行按钮”启动(在 Jenskins BlueOcean 的分支选项卡中),则中止管道:

def isBuildAReplay() 
  // https://***.com/questions/51555910/how-to-know-inside-jenkinsfile-script-that-current-build-is-an-replay/52302879#52302879
  def replyClassName = "org.jenkinsci.plugins.workflow.cps.replay.ReplayCause"
  currentBuild.rawBuild.getCauses().any cause -> cause.toString().contains(replyClassName) 


node  
        try 
                stage('check replay') 
                    if (isBuildAReplay()) 
                        currentBuild.result = 'ABORTED'
                        error 'Biuld REPLAYED going to EXIT (please use RUN button)'
                     else 
                        echo 'NOT replay'
                    
                
                stage('simple stage') 
                    echo 'hello from simple stage'
                
                stage('error stage') 
                    //error 'hello from simple error'
                
                stage('unstable stage') 
                    unstable 'hello from simple unstable'
                
                stage('Notify sucess') 
                    //Handle SUCCESS|UNSTABLE
                    discordSend(description: "$currentBuild.currentResult: Job $env.JOB_NAME \nBuild: $env.BUILD_NUMBER \nMore info at: \n$env.BUILD_URL", footer: 'No-Code', unstable: true, link: env.BUILD_URL, result: "$currentBuild.currentResult", title: "$JOB_NAME << CLICK", webhookURL: 'https://discordapp.com/api/webhooks/')

                

         catch (e) 
                echo 'This will run only if failed'

                if(currentBuild.result == 'ABORTED')
                    //Handle ABORTED
                    discordSend(description: "$currentBuild.currentResult: Job $env.JOB_NAME \nBuild: $env.BUILD_NUMBER \nMore info at: \n$env.BUILD_URL\n\nERROR.toString():\n"+e.toString()+"\nERROR.printStackTrace():\n"+e.printStackTrace()+" ", footer: 'No-Code', unstable: true, link: env.BUILD_URL, result: "ABORTED", title: "$JOB_NAME << CLICK", webhookURL: 'https://discordapp.com/api/webhooks/')
                    throw e
                else
                    //Handle FAILURE
                    discordSend(description: "$currentBuild.currentResult: Job $env.JOB_NAME \nBuild: $env.BUILD_NUMBER \nMore info at: \n$env.BUILD_URL\n\nERROR.toString():\n"+e.toString()+"\nERROR.printStackTrace():\n"+e.printStackTrace()+" ", footer: 'No-Code', link: env.BUILD_URL, result: "FAILURE", title: "$JOB_NAME << CLICK", webhookURL: 'https://discordapp.com/api/webhooks/')
                    throw e
                
         finally 
                echo 'I will always say Hello again!'

        

主要技巧是达到中止状态的行顺序:

currentBuild.result = 'ABORTED'
error 'Biuld REPLAYED going to EXIT (please use RUN button)'

先设置状态,然后抛出异常。

在 catch 块中都可以工作:

currentBuild.result
currentBuild.currentResult

【讨论】:

【参考方案6】:

我们使用的是:

try 
 input 'Do you want to abort?'
 catch (Exception err) 
 currentBuild.result = 'ABORTED';
 return;

最后的“return”确保不再执行任何代码。

【讨论】:

【参考方案7】:

您可以转到 Jenkins 的脚本控制台并运行以下命令来中止挂起/任何 Jenkins 作业的构建/运行:

Jenkins .instance.getItemByFullName("JobName")
        .getBuildByNumber(JobNumber)
        .finish(hudson.model.Result.ABORTED, new java.io.IOException("Aborting build"));

【讨论】:

以上是关于在 Jenkins 中从管道中止当前构建的主要内容,如果未能解决你的问题,请参考以下文章

詹金斯 - 如果开始新的构建,则中止运行构建

管道中的通道关闭异常

jenkins构建时间变量

从批处理脚本中引发 Jenkins 作业中止

Jenkins Pipeline作业获取另一个jenkins作业的构建状态。

Jenkins 配置邮件通知