如何在Jenkins声明式流水线语法中继续执行失败的阶段

30

我想在Jenkins声明性流水线语法中定义多个阶段,可以继续执行即使其中任何一个失败。我找不到现有的重复问题,因为它们都假设或允许脚本语法。

pipeline {
    agent any
    stages {
        stage('stage 1') {
            steps {
                echo "I need to run every time"
            }
        }
        stage('stage 2') {
            steps {
                echo "I need to run every time, even if stage 1 fails"
            }
        }
        stage('stage 3') {
            steps {
                echo "Bonus points if the solution is robust enough to allow me to continue *or* be halted based on previous stage status"
            }
        }
    }
}

澄清一下,我不是在寻找如何使用脚本语法完成这个任务。我试图了解是否支持并规范化此类流程控制的声明性语法。为此,我将尝试明确定义我正在寻找的内容:

必需的

  • 无try/catch。我不想降到脚本模式下,也不想在另一个共享库或脚本块中“包装”我的声明性管道。
  • 没有后续步骤的花招。我想要真正的多个阶段,而不是一个包含所有其他逻辑的 post always 步骤的阶段。

可选的

  • 失败的阶段应该被识别为已失败;我不希望因“跳过”或“继续”而将失败的阶段显示为绿色。
  • 有任何失败阶段的构建应标记为红色(或黄色,或任何不是绿色的颜色)。

相关但不充分的


2
我认为你的一些要求在概念上是相互排斥的。1)管道(线路)有两个开口。如果它断裂了,除非进行一些管道工作,例如 try/catch,否则不会通过断点。2)考虑到 Maven 和其声明性 POM,如果一个阶段(阶段)失败了,整个构建就会失败,除非消除原因并重试,否则没有机会克服这个问题。[待续] - Gerold Broser
  1. 如果我们看一下 Pipeline Stage View Plugin 的例子,其中包括 BuildDeployTestPromote 阶段,如果一个阶段失败了,继续执行就没有意义了。
- Gerold Broser
1
虽然我完全理解那种观点,但我个人不同意。对我来说,管道的流程控制是构建过程中绝对必不可少的一部分……否则它就太受限制而无法使用。甚至脚本语法中存在 try/catch 也似乎是对此的赞同;我的问题是如何将这种流程控制元素形式化到声明式中。也许答案是“目前还没有”,我不知道。 - dolphy
我认为在这方面"(not) yet"是关键字,因为Pipeline Syntax reference说道:"声明式管道是Jenkins Pipeline的一个相对较新的补充<sup>[1]</sup>,它在Pipeline子系统的基础上提供了更简化和有见解的语法。[...]“Pipeline插件”的2.5版本引入了对声明式管道语法的支持。” - Gerold Broser
4个回答

30

现在可以实现:

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 正如您可能已经猜到的那样,您可以自由选择buildResultstageResult,以便在需要时使其不稳定或其他任何情况。 您甚至可以使构建失败并继续执行管道。
只要确保您的Jenkins是最新的版本,因为这是一个相当新的功能。
编辑:这是这个答案最初撰写的问题。 这也是一些其他问题的正确答案,这就是我在那里发布这个答案的原因。 这是多个类似问题的正确解决方案。 我已经针对它们特定的问题量身定制了我的其他答案,以明确这一点。 我只是复制了答案以节省时间。 这并不意味着它不是一个好的正确答案。

4

我可能漏掉了一些东西,但是声明性、有主见的流水线的想法是为了提供大多数简单用例的覆盖。一旦你需要有主见的没有覆盖到的东西,你就必须求助于脚本化流水线,这只涉及“声明性流水线”的“要求”:现在不会发生。

至于你的其他“要求”,它们几乎没有任何意义,因为整个想法是将低级丑陋的东西封装成共享库,为用户提供像下面这样的结构:

    mylib.failable_stages({
      stages {
        stage('stage 1') {
          steps {
            echo "I need to run every time"
          }
        }
        stage('stage 2') {
          steps {
            echo "I need to run every time, even if stage 1 fails"
          }
        }
        stage('stage 3') {
          steps {
            echo "Bonus points if the solution is robust enough to allow me to continue *or* be halted based on previous stage status"
          }
        }
      }
    })

当然,您需要找到或实现这样的mylib类,failable_stages将得到一个闭包,并将其包装在各种管道/样板代码片段中。
希望这有所帮助。

感谢您的回复。技术上的回答正是我用这种方式提出问题的原因。我想了解这种流程控制是否在声明性语法中得到了规范化。每当有任何接近这个问题的内容被问到时,通常的回答是“这是如何在脚本中实现的”。那是一个解决方案,但不是问题的答案。这有点像我问“如何在Python中获取目录列表”,然后被告知“只需使用subprocess.check_output(["ls"])”。这是一个解决方案,但不是问题的答案。 - dolphy
2
我对共享库的目的持有不同意见。我的理解是,它们旨在抽象可共享的代码,而不是丑陋的代码。如果将代码放入共享库中只是为了隐藏它,而没有在多个地方重用它的意图,那么这就是语言或开发人员或两者都失败了。也许这只是两种不同观点的情况,也许我的问题的答案真的是“你不能这样做”,但我真的想知道。我问这个问题是因为我知道我不是这里最聪明的人。 - dolphy
无论如何,你对问题的解释并不是我想要表达的意思,这意味着我需要重新措辞。我已经添加了一个澄清,如果有帮助,请告诉我! - dolphy
嗨,@dolphy。我现在感觉好多了。已删除“道德”段落。仍不清楚你为什么固执于声明性。此外还有另一种方法:
  1. Hackish:在单个项目化的“parallel”步骤中运行您的阶段。它们具有一个参数,如果内部步骤失败,则不会使构建失败。
  2. 真正的解决方案是通过向“stage”添加额外的布尔参数来扩展它。
- mvk_il
@dolphy 这次我表述不好。事实上,没有抽象化的代码会让你的工作更费时。这本身就很丑陋。此外,“包装器”使代码看起来嵌套,也很丑陋。因此,即使我们放弃美观的纯逻辑和结构,使用共享库可以提高可视吸引力和可读性,这是更好的选择。当然,这只是我的个人意见。 - mvk_il
@dolphy,我忘了提到我的客户通常已经有一些自由工作。因此,让他们学习Groovy并不总是得到员工的最大热情支持。因此,让他们维护短期工作是一个很大的优势。 - mvk_il

2
我认为这取决于工作之间的依赖程度。根据您的例子,我会假设:
- 阶段1与所有其他阶段无关,因为它是第一个阶段。 - 阶段2与所有其他阶段无关,因为阶段1可能立即失败,而仍需要运行阶段2。 - 阶段3取决于阶段1和阶段2的结果。
因此,相应的流水线可能是:
pipeline {
    stages {
        stage('Independent tasks') {
            parallel {
                stage('stage 1') {
                    steps {
                        sh 'exit 1' // failure
                    }
                }
                stage('stage 2') {
                    steps {
                        echo 'Happens even so stage 1 fails'
                        sh 'exit 0' // success
                    }
                }
            }
            post {  // 'stage 3'
                failure {
                    echo "... at least one failed"
                }
                success {
                    echo "Success!"
                }
            }
        }
        stage ('stage 4') {
            steps {
                echo 'Happens only if all previous succeed'
            }
        }
    }
}

阶段1阶段2总是会运行,阶段3则根据它们的成功/失败结果做出反应。


额外的想法:这个概念仅适用于您的管道“结尾”。如果您需要在中间某处使用它,并且构建必须继续,则可以将其移动到自己的作业中并使用build job插件。

注意:为了保证翻译准确,请勿修改原始html标签。

pipeline {
    stages {
    stage('Start own job for stage 1, 2, 3') {
        steps {
            build job: 'stageOneTwoThree', propagate: false, wait: true
        }
    }
    stage ('stage 4') {
        steps {
            echo 'Happens always, because "propagate: false"'
        }
    }
}

2
我使用post实现了它。我的要求是不管构建状态如何,都要发送slack通知。
@Library('instanceGroups')
import x.z.y.jenkins.libraries.SlackNotifier

def slackHelper = new x.z.y.jenkins.libraries.SlackNotifier(env)
final String projectName = "pomeranian"
final String featureBranchPattern = "f_"

pipeline {
    agent any
    options { disableConcurrentBuilds() }

    stages {
        stage('clean') {
            steps {
                script {
                    try {
                        echo 'Current Branch...' + env.BRANCH_NAME
                        sh 'rm -rf /var/lib/jenkins/.gradle/caches'
                        sh './gradlew clean'
                    } catch (e) {
                        currentBuild.result = 'FAILURE'
                        slackHelper.buildGenericJobFailureNotificationMessage()
                        throw e
                    }
                }
            }
        }

        stage('compile') {
            steps {
                script {
                    try {
                        sh "./gradlew compileJava"
                    } catch (e) {
                        currentBuild.result = 'FAILURE'
                        slackHelper.getCompilationFailureSlackNotificationMessage()
                        throw e
                    }
                }
            }
        }

        stage('test') {
            steps {
                script {
                    try {
                        sh "./gradlew test"
                    } finally {
                        junit 'build/test-results/test/*.xml'

                        slackHelper.getTestStatuses(currentBuild)
                        slackHelper.buildUnitTestSlackNotificationMessage()
                    }
                }
            }
        }

        stage('publish 2 nexus') {
            steps {
                script {
                  // some code
                }
            }
        }

        stage('git tagging') {
            steps {
                script {
                    // some more code...
            }
        }
    }


    post {
        always {
            script {
                slackHelper.finalBuildStatusMessage(currentBuild)
                slackSend(channel: '#ci-cd', attachments: slackHelper.getFinalSlackMessage())
            }
        }
    }
}

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