如何报告declarative pipeline失败的阶段?在失败块中,我想获取failedStage.name并报告它(最终松弛)。
pipeline {
agent { label 'master'}
stages {
stage('Ok') {
steps {
echo 'do thing'
}
}
stage('NotOK') {
steps {
sh 'make fail'
}
}
}
post {
always {
echo 'ok'
}
failure {
echo 'Failed during Which Stage?'
}
}
}
答案 0 :(得分:4)
通常可以使用Blue Ocean插件API来实现。类PipelineNodeGraphVisitor
可用于遍历所有管道节点(例如阶段,并行分支和步骤)。我们只需要检查FlowNodeWrapper
的type
属性是否等于FlowNodeWrapper.NodeType.STAGE
。
另外,我们可以从存储在节点中的ErrorAction
中获取失败原因。
您通常会将以下代码放入共享库中, 因为如果直接将其插入管道代码中,它将阻止管道在沙盒环境中运行。
import io.jenkins.blueocean.rest.impl.pipeline.PipelineNodeGraphVisitor
import io.jenkins.blueocean.rest.impl.pipeline.FlowNodeWrapper
import org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper
import org.jenkinsci.plugins.workflow.actions.ErrorAction
// Get information about all stages, including the failure causes.
//
// Returns a list of maps: [[id, displayName, result, errors]]
// The 'errors' member is a list of unique exceptions.
@NonCPS
List<Map> getStageResults( RunWrapper build ) {
// Get all pipeline nodes that represent stages
def visitor = new PipelineNodeGraphVisitor( build.rawBuild )
def stages = visitor.pipelineNodes.findAll{ it.type == FlowNodeWrapper.NodeType.STAGE }
return stages.collect{ stage ->
// Get all the errors from the stage
def errorActions = stage.getPipelineActions( ErrorAction )
def errors = errorActions?.collect{ it.error }.unique()
return [
id: stage.id,
displayName: stage.displayName,
result: "${stage.status.result}",
errors: errors
]
}
}
// Get information of all failed stages
@NonCPS
List<Map> getFailedStages( RunWrapper build ) {
return getStageResults( build ).findAll{ it.result == 'FAILURE' }
}
pipeline{
agent any
stages {
stage('SuccessStage') {
steps {
echo 'Success'
}
}
stage('FailedStage') {
steps {
readFile 'dfgkjsdffj'
}
}
stage('SkippedStage') {
steps {
echo 'Skipped because of error in FailedStage'
}
}
}
post {
failure {
script {
// Print information about all failed stages
def failedStages = getFailedStages( currentBuild )
echo "Failed stages:\n" + failedStages.join('\n')
// To get a list of just the stage names:
//echo "Failed stage names: " + failedStages.displayName
}
}
}
}
如果您想获得除FAILURE
之外的其他结果,请查看我的函数getFailedStages()
。您可以简单地更改条件,例如。 g。:
it.result in ['FAILURE','UNSTABLE']
it.result != 'SUCCESS'
可能的替代实施方式:
严格来说,Blue Ocean API不是必需的。它只是简化了很多代码。您可以仅使用基本的Jenkins管道API来执行相同的操作。首先,请寻找FlowGraphWalker
以迭代管道节点。查看Blue Ocean的PipelineNodeGraphVisitor
的代码,以了解它们如何确定“阶段”节点类型。
答案 1 :(得分:2)
您可以在每个阶段使用post
指令,通过特定操作和通知对失败采取行动。
这并不完全理想,好像你想要在所有阶段你都需要重复它,我不认为你可以动态访问你的舞台名称,所以它真的是verbos和硬编码。你可能会重构它以使用库。
pipeline {
agent { label 'master'}
stages {
stage('Ok') {
steps {
echo 'do thing'
}
post {
failure {
echo 'FAILED (in stage OK - should not happen :))'
}
}
}
stage('NotOK') {
steps {
sh 'make fail'
}
post {
failure {
echo 'FAILED (in stage NotOK)'
}
}
}
}
post {
always {
echo 'COMPLETED (global)'
}
failure {
echo 'FAILED (global)'
}
}
}
答案 2 :(得分:1)
我发现,不是在每个阶段都添加post
节,而是从我的角度找到了一些在声明性管道中不应该使用的解决方案,但确实如此。
您所需要做的就是覆盖stage
:
def stage(String name, Closure cl) {
echo "Stage: ${name}"
try {
cl()
} catch (Exception e) {
// I needed to save failed stage and message for parent pipeline job
// so I saved them in environment variables, otherwise it can be saved
// in global variables
if (!env.FAILED_STAGE) {
env.FAILED_STAGE = name
env.FAILED_MESSAGE = e.getMessage()
}
}
}
pipeline {
options { timestamps() }
agent { label 'master' }
stages {
stage('First stage') {
steps {
//Any steps are working
script {
sh "echo first"
}
}
}
stage('Second stage') {
steps {
echo "second"
}
}
stage('Fail stage') {
steps {
error "failed"
}
}
stage('Final stage') {
steps {
build "Other job"
}
}
}
post {
failure {
echo "Failed stage: ${env.FAILED_STAGE}"
echo "Error message: ${env.FAILED_MESSAGE}"
}
}
}
对我来说最奇怪的是,在阶段失败之后,其他阶段将被跳过。 输出如下:
14:05:14 Stage: First stage
[Pipeline] script
[Pipeline] {
[Pipeline] sh
14:05:14 + echo first
14:05:14 first
[Pipeline] }
[Pipeline] // script
[Pipeline] echo
14:05:14 Stage: Second stage
[Pipeline] echo
14:05:14 second
[Pipeline] echo
14:05:14 Stage: Fail stage
[Pipeline] error
[Pipeline] error
[Pipeline] echo
14:05:14 Stage: Final stage
Stage "Final stage" skipped due to earlier failure(s)
[Pipeline] echo
14:05:14 Stage: Declarative: Post Actions
[Pipeline] echo
14:05:14 Failed stage: Fail stage
[Pipeline] echo
14:05:14 Error message: failed
[Pipeline] }
[Pipeline] // timestamps
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
ERROR: failed
Finished: FAILURE
编辑:请注意,您将失去舞台视野,因为从詹金斯的角度来看,不会有正常的舞台。
答案 3 :(得分:1)
PipelineVisitor是一种很好的方法。但是,如果您只想查看错误,那么利用FlowGraphTable
可能会更好。
以下内容提供了每个失败步骤的映射列表,并且还遍历了下游作业。我发现它非常有用。
您将要使用共享库来避免安全沙箱警告/批准
List<Map> getStepResults() {
def result = []
WorkflowRun build = currentBuild()
FlowGraphTable t = new FlowGraphTable(build.execution)
t.build()
for (def row in t.rows) {
if (row.node.error) {
def nodeInfo = [
'name': "${row.node.displayName}",
'url': "${env.JENKINS_URL}${row.node.url}",
'error': "${row.node.error.error}",
'downstream': [:]
]
if (row.node.getAction(LogStorageAction)) {
nodeInfo.url += 'log/'
}
for (def entry in getDownStreamJobAndBuildNumber(row.node)) {
nodeInfo.downstream["${entry.key}-${entry.value}"] = getStepResults(entry.key, entry.value)
}
result << nodeInfo
}
}
log(result)
return result
}
Map getDownStreamJobAndBuildNumber(def node) {
Map downStreamJobsAndBuilds = [:]
for (def action in node.getActions(NodeDownstreamBuildAction)) {
def result = (action.link =~ /.*\/(?!\/)(.*)\/runs\/(.*)\//).findAll()
if (result) {
downStreamJobsAndBuilds[result[0][1]] = result[0][2]
}
}
return downStreamJobsAndBuilds
}
答案 4 :(得分:1)
我根据自己的需要构建了一些更简单的东西,尽管它仍然需要每个阶段的代码片段。
我来到这里是为了寻找一种方法来避免在每个阶段的 post
部分重复出现错误,而且我喜欢上述解决方案。我可能会在某个时候将其他答案中的一些内容整合到我的图书馆中。
我有一个 env.informationalMessages
变量,它会在每次运行结束时与通知电子邮件一起发送。
在每个阶段,您都会执行不成功的 post
部分。这应该捕获 failed
、aborted
、unstable
结果。清理在任何其他后置条件之后运行。
stage ('some stage name') {
steps { ... }
post {
unsuccessful {
addStageResultToInfoMessages()
}
cleanup {
// whatever else you want to do
}
}
}
vars/addStageResultToInfoMessages.groovy
:
// Adds a message with the stage result into the informationalMessages env var.
def call() {
addToInfoMessages("${currentBuild.result} in stage '${STAGE_NAME}'.")
}
vars/addToInfoMessages.groovy
// Adds the passed-in string to the informationalMessages env var
// that gets sent with notification emails.
// env.informationalMessages is initialized to '' at the start of each run.
def call(String message) {
env.informationalMessages += "${message}\n"
}
然后在管道的末端,另一个 post
部分:
post {
unsuccessful {
addToInfoMessages(getErrorMessage())
}
cleanup {
notifyEmail()
}
}
vars/getErrorMessage.groovy
从 Jenkins API 获取原始控制台文本并查找文本“ERROR:
”。 Jenkins 中过时的 Groovy 不支持列表上的 null-safe 导航,因此您必须采用老式做法。
// gets the Error message from the run's console text
// uses the jenkins api
def call() {
// get the raw text of the build's console output
response = httpRequest ignoreSslErrors: true, url: "${BUILD_URL}consoleText"
// find lines with 'ERROR:'
err = response.getContent().findAll(/.*ERROR:.*/)
// if null-safe, split off the time code
if (err) { return err[-1].split('Z] ')[1] }
else { return 'Error not found' }
}
只是另一种方式来做到这一点。