在不使整个任务失败的情况下,将Jenkins管道阶段设置为失败状态

83

这是我正在使用的代码

node {
    stage 'build'
    echo 'build'

    stage 'tests'
    echo 'tests'

    stage 'end-to-end-tests'
    def e2e = build job:'end-to-end-tests', propagate: false
    result = e2e.result
    if (result.equals("SUCCESS")) {
        stage 'deploy'
        build 'deploy'
    } else {
        ?????? I want to just fail this stage
    }
}

有没有办法让我在不使整个任务失败的情况下标记“端到端测试”阶段为失败?始终传递 false 只会将该阶段标记为 true,这不是我想要的,但传递 true 会将整个任务标记为失败,这也不是我想要的。

9个回答

70

现在,即使使用声明式流水线也可以实现这一点:

pipeline {
    agent any
    stages {
        stage('1') {
            steps {
                sh 'exit 0'
            }
        }
        stage('2') {
            steps {
                catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
                    sh "exit 1"
                }
            }
        }
        stage('3') {
            steps {
                sh 'exit 0'
            }
        }
    }
}
在上面的示例中,所有阶段都将执行,管道将会成功,但第二个阶段将显示为失败: Pipeline Example 正如你所想象的那样,你可以自由选择buildResult和stageResult,以防你想让它不稳定或其他任何情况。你甚至可以让构建失败并继续执行管道。
只需确保你的Jenkins是最新版本,因为这是一个相当新的功能。

3
我收到了“Invalid parameter "buildResult",您是否想要使用“null”?”和“Invalid parameter "stageResult",您是否想要使用“null”?”的错误提示信息。 - JShorthouse
3
@JShorthouse:管道(Pipeline):基本步骤需要2.18或更高版本。你使用的是哪个版本? - Erik B
2
我找到了另一种现在可行的方法,但这可能是问题所在-我认为“相当新”的意思是它会在我几个月前安装的Jenkins上工作,但从那个版本的发布日期来看,您指的是最近一周内。 - JShorthouse
不幸的是,这标志着我的失败阶段,而管道被视为成功。 - pedroapero
这个魔法命令 sh 'exit 0' 在 Windows 安装上也能工作吗? - grenix
显示剩余2条评论

27

现在 Stage 支持块,所以将 stage 包装在 try-catch 中。在 Stage 内部使用 try-catch 可以使它成功。

之前提到的新功能将会更加强大。与此同时:

try {
   stage('end-to-end-tests') {
     node {      
       def e2e = build job:'end-to-end-tests', propagate: false
       result = e2e.result
       if (result.equals("SUCCESS")) {
       } else {
          sh "exit 1" // this fails the stage
       }
     }
   }
} catch (e) {
   result = "FAIL" // make sure other exceptions are recorded as failure too
}

stage('deploy') {
   if (result.equals("SUCCESS")) {
      build 'deploy'
   } else {
      echo "Cannot deploy without successful build" // it is important to have a deploy stage even here for the current visualization
   }
}

这对我不起作用。在“尝试”中,我收到以下错误:“WorkflowScript:需要一个阶段”。 - pedroapero

14

最近我尝试使用vaza的答案(显示 Jenkins 管道阶段失败,而不使整个任务失败)作为编写一个函数的模板,该函数在类似于任务名称的自己的阶段中执行任务。令人惊讶的是它起作用了,但也许一些 Groovy 专家可以看一下它 :)

这是如果其中一个任务被中止的外观: 输入图像描述

def BuildJob(projectName) {
    try {
       stage(projectName) {
         node {      
           def e2e = build job:projectName, propagate: false
           result = e2e.result
           if (result.equals("SUCCESS")) {
           } else {
              error 'FAIL' //sh "exit 1" // this fails the stage
           }
         }
       }
    } catch (e) {
        currentBuild.result = 'UNSTABLE'
        result = "FAIL" // make sure other exceptions are recorded as failure too
    }
}

node {
    BuildJob('job1')
    BuildJob('job2')
}

14

听起来像是JENKINS-26522。目前您能做的最好的就是设置一个总体结果:

if (result.equals("SUCCESS")) {
    stage 'deploy'
    build 'deploy'
} else {
    currentBuild.result = e2e.result
    // but continue
}

6
有没有办法反过来做?将未成功的阶段标记为红色,但将构建状态(那个小球)标记为蓝色? - Sviatlana
嗨,@Sviatlana,你能完成这个任务吗?即将不成功的阶段标记为红色? - user3768904
@user3768904 不,我做不到。我们已经摆脱了那个问题。 - Sviatlana
@Sviatlana 尝试相同的操作...虽然构建成功,但我想用红色标记该阶段为失败。 - Marcello DeSales

5
为了在下游作业失败时显示构建成功但某个阶段失败的情况,同时支持用户能够取消构建(包括所有后续阶段),我不得不使用多种解决方案的组合,特别是 whentry/catchthrowcatchError()
env.GLOBAL_BUILD_ABORTED = false        // Set if the user aborts the build

pipeline {
    agent any

    stages {
        stage('First Stage') {
            when { expression { env.GLOBAL_BUILD_ABORTED.toBoolean() == false } }

            steps {
                catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
                    myLocalBuildMethod('Stage #1, build #1')
                    myLocalBuildMethod('Stage #1, build #2')
                }
            }
        }

        stage('Second Stage') {
            when { expression { env.GLOBAL_BUILD_ABORTED.toBoolean() == false } }

            steps {
                catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
                    myLocalBuildMethod('Stage #2, build #1')
                    myLocalBuildMethod('Stage #2, build #2')
                    myLocalBuildMethod('Stage #2, build #3')
                }
            }
        }
    }
}

def myLocalBuildMethod(myString) {
    /* Dummy method to show User Aborts vs Build Failures */

    echo "My Local Build Method: " + myString

    try {
        build (
            job: "Dummy_Downstream_Job"
        )

    } catch (e) {
        /* Build Aborted by user - Stop All Test Executions */
        if (e.getMessage().contains("was cancelled") || e.getMessage().contains("ABORTED")) {

            env.GLOBAL_BUILD_ABORTED = true
        }
        /* Throw the execiption to be caught by catchError() to mark the stage failed. */
        throw (e)
    }

    // Do other stuff...
}

4
您可以在else语句中使用以下代码:
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
  error "some err msg"
}

3

您可以在阶段中添加一个显式的 Fail 任务,例如 'sh "不存在的命令"'。

if (result.equals("SUCCESS")) {
   stage 'deploy'
   build 'deploy'
} else {
   try {
       sh "not exist command"
   }catch(e) {
   }
}

3

解决步骤

  • 您必须在阶段中发出错误才能将其标记为错误
  • stage范围之外,处理异常并选择构建状态
  • 这样做可以实现一些用户(包括我自己、@user3768904、@Sviatlana)所期望的效果

失败步骤成功示例

node("node-name") {
  try {
    stage("Process") {
      error("This will fail")
    }
  } catch(Exception error) {
    currentBuild.result = 'SUCCESS'
    return
  }
  stage("Skipped") {
     // This stage will never run
  }
}

中止步骤失败示例

enter image description here

node("node-name") {
  try {
    stage("Process") {
      error("This will fail")
    }
  } catch(Exception error) {
    currentBuild.result = 'ABORTED'
    return
  }
  stage("Skipped") {
     // This stage will never run
  }
}

enter image description here


我今天在 DSL 管道中测试了这种方法,但它无法将整个步骤标记为失败,即使其中有一个失败的步骤... Jenkins 2.164.2 - Marcello DeSales

0
这可能是一种通用的模式,展示了如何使用内置函数自定义阶段结果,并将子任务的结果传播到阶段结果中以显示友好的消息。如果一个子任务不成功,整体构建标记为不稳定只是这个示例中的一种实现选择。
def run_sub_job() {
    def jobBuild = build(job: 'foo', wait: true, propagate: false)
    def result = jobBuild.getResult()
    def msg = 'sub-job: ' + result
    if ('SUCCESS' == result) {
        println(msg)
    } else if ('UNSTABLE' == result) {
        unstable(msg) // will also set buildResult to UNSTABLE
    } else { // anything else (FAILURE, ABORTED ...) is considered an error
        catchError(
            buildResult: 'UNSTABLE',
            stageResult: result // propagate sub-job result
        ) {
            error(msg)
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接