如何设置 Jenkins 阶段或管道并行分支状态(不稳定、失败等)以用于 Stage view 和 Blue Ocean UI?
How do you set Jenkins stage or pipeline parallel branch status (unstable, failure etc) for use in Stage view and Blue Ocean UI?
概览
我目前正在配置一个由多个平台构建组成的管道。在管道的开头,用户可以 select 构建或跳过哪个平台。
根据每个平台的 'build' 阶段是否通过,下游阶段的步骤可以检查该平台构建的状态并确定是否 运行。如果一个或多个平台失败,这允许管道尝试并完成其他平台(如果用户确认这样做)。
进度
就目前而言,我的管道实现了这一点,允许用户在管道开始时使用 include/exclude 平台,并授权管道在平台失败时继续构建(但将管道标记为失败) .这允许归档构建文件/发布 gtests 等,这可以在下游完成 stages/steps。这是我的詹金斯文件:
// Specify whether or not to build platform by default
def buildDefinitions = [ 'windows' : true , 'macos' : true , 'ubuntu' : true ]
// Keep track of builds that fail
def failedBuilds = [:]
stage('Build Customisation') {
try {
// Wait limited amount of time for user input
timeout(time: 30, unit: 'SECONDS') {
// Update the build definitions based on user input
buildDefinitions = input(
message: 'Toggle which builds to run (Abort will use default)',
// Use custom global function to generate boolean input parameters based on a map
// Sets default value to value in input map
parameters: generateInputBoolParams( buildDefinitions )
)
}
// Continue pipeline if user input not provided within time limit
} catch ( error ) {
echo 'Using default pipeline configuration...'
}
// Check that at least one build platform is selected
if ( !mapContainsTrue( buildDefinitions ) ) {
error 'No builds selected, aborting pipeline'
}
}
stage('Conditional Build') {
parallel (
'Windows' : {
// Prevent a build failure from terminating the pipeline after this stage
try {
// Check if windows build is set to run
if ( buildDefinitions['windows'] ) {
node('windows') {
checkout(scm)
bat 'build.bat default-windows'
}
} else {
echo 'Build was disabled by user'
}
// Catch an error in the build
} catch ( error ) {
// Make note that the build failed
failedBuilds['windows'] = true
// Set the pipeline status as failure
currentBuild.result = 'FAILURE'
}
},
'MacOS' : {
try {
if ( buildDefinitions['macos'] ) {
node('macos') {
checkout(scm)
sh './build.sh default-macos'
}
} else {
echo 'Build was disabled by user'
}
} catch ( error ) {
failedBuilds['macos'] = true
currentBuild.result = 'FAILURE'
}
},
'Ubuntu' : {
try {
if ( buildDefinitions['ubuntu'] ) {
node('ubuntu') {
checkout(scm)
sh './build.sh default-ubuntu'
}
} else {
echo 'Build was disabled by user'
}
error 'test error'
} catch ( error ) {
failedBuilds['ubuntu'] = true
currentBuild.result = 'FAILURE'
}
}
)
// Check if any builds have been marked as failed
if ( mapContainsTrue( failedBuilds ) ) {
// Remove failed builds from the original map of enabled builds
def updatedBuildDefinitions = subtractMap( buildDefinitions, failedBuilds )
// Check that there are builds left to run
if ( mapContainsTrue( updatedBuildDefinitions ) ) {
// Update the original build map
buildDefinitions = updatedBuildDefinitions
// Lists the failed builds and asks whether to continue or abort the pipeline
timeout(time: 30, unit: 'SECONDS') {
input(
message: 'Builds failed ' + getKeyset( failedBuilds ) + ', do you want to continue the pipeline and skip failed builds?'
)
}
} else {
// Throw an error to terminate the pipeline if no builds are left to run
error 'No builds left to run'
}
}
}
stage('Conditional Downstream') {
parallel (
'Windows' : {
if ( buildDefinitions['windows'] ) {
echo 'You chose to run the windows build!'
} else {
echo 'The windows build was skipped'
}
},
'MacOS' : {
if ( buildDefinitions['macos'] ) {
echo 'You chose to run the macos build!'
} else {
echo 'The macos build was skipped'
}
},
'Ubuntu' : {
if ( buildDefinitions['ubuntu'] ) {
echo 'You chose to run the ubuntu build!'
} else {
echo 'The ubuntu build was skipped'
}
}
)
}
我的全局函数:
// subtractMap.groovy
def call ( map1, map2 ) {
return map1 - map2
}
// mapContainsTrue.groovy
boolean call ( array ) {
for ( entry in array ) {
if ( entry.value == true ) {
isBuildConfigValid = true
return true
} else {
return false
}
}
}
// getKeyset.groovy
def call ( map ) {
return map.keySet() as String[]
}
// generateInputBoolParams.groovy
def call ( array ) {
def parameterList = []
for ( item in array ) {
parameterList.add( booleanParam(defaultValue: item.value, name: item.key) )
}
return parameterList
}
问题
虽然一般功能有效,但 UI 响应没有,除了将管道标记为失败。我希望能够将并行分支标记为失败,以便在 Blue Ocean UI 中很容易看到哪个平台构建失败。
Blue Ocean UI with failed parallel branch in try/catch block
将阶段标记为失败也很有用,因此当不在 Blue Ocean UI 中时,阶段视图会显示失败的阶段(除非只有在管道在该阶段终止时才会发生这种情况)虽然一旦 Blue Ocean 结束 Beta,这就不再是问题了。
Stage View failed stage (Above) and as is (Below)
问题
如何将并行分支标记为失败,以便它在 Blue Ocean UI 中显示为带有红叉?也许使用像 currentBuild.result
这样的环境变量
是否可以对整个舞台进行类似的操作,以便它显示在舞台视图中? (不太重要)
UI 响应可以通过将并行步骤和阶段步骤都包装在一个 try 块中来实现,将并行分支中的 try/catch 块的错误抛出到阶段块.不像设置 属性 那样干净,但对 Blue Ocean 和 Stage View 确实有正确的 UI 响应。
try {
stage('example') {
try {
parallel (
'A' : {
try {
// Example...
}
catch (error) {
// Mark branch as failed somewhere
throw error
}
},
'B' : {
try {
// Example...
}
catch (error) {
// Mark branch as failed somewhere
throw error
}
}
)
}
catch (error) {
throw (error)
}
finally {
// Parallel branch A failed, do you want to continue? etc...
}
}
}
catch (error) {
println (error)
}
这在 Blue Ocean 中对我有用:
stage("Global Stage") {
def parallelStages = [failfast: false]
parallelStages["Branch 1"] = {
//Code for branch 1
}
parallelStages["Branch 2"] = {
//Code for branch 2
}
catchError(buildResult: 'SUCCESS', message: 'Branch Failed', stageResult: 'UNSTABLE')
{ parallel parallelStages }
}
这在 Blue Ocean 中正确地将失败的并行分支标记为 UNSTABLE,不会触及其他阶段结果并将整体构建标记为 SUCCESS
概览
我目前正在配置一个由多个平台构建组成的管道。在管道的开头,用户可以 select 构建或跳过哪个平台。
根据每个平台的 'build' 阶段是否通过,下游阶段的步骤可以检查该平台构建的状态并确定是否 运行。如果一个或多个平台失败,这允许管道尝试并完成其他平台(如果用户确认这样做)。
进度
就目前而言,我的管道实现了这一点,允许用户在管道开始时使用 include/exclude 平台,并授权管道在平台失败时继续构建(但将管道标记为失败) .这允许归档构建文件/发布 gtests 等,这可以在下游完成 stages/steps。这是我的詹金斯文件:
// Specify whether or not to build platform by default
def buildDefinitions = [ 'windows' : true , 'macos' : true , 'ubuntu' : true ]
// Keep track of builds that fail
def failedBuilds = [:]
stage('Build Customisation') {
try {
// Wait limited amount of time for user input
timeout(time: 30, unit: 'SECONDS') {
// Update the build definitions based on user input
buildDefinitions = input(
message: 'Toggle which builds to run (Abort will use default)',
// Use custom global function to generate boolean input parameters based on a map
// Sets default value to value in input map
parameters: generateInputBoolParams( buildDefinitions )
)
}
// Continue pipeline if user input not provided within time limit
} catch ( error ) {
echo 'Using default pipeline configuration...'
}
// Check that at least one build platform is selected
if ( !mapContainsTrue( buildDefinitions ) ) {
error 'No builds selected, aborting pipeline'
}
}
stage('Conditional Build') {
parallel (
'Windows' : {
// Prevent a build failure from terminating the pipeline after this stage
try {
// Check if windows build is set to run
if ( buildDefinitions['windows'] ) {
node('windows') {
checkout(scm)
bat 'build.bat default-windows'
}
} else {
echo 'Build was disabled by user'
}
// Catch an error in the build
} catch ( error ) {
// Make note that the build failed
failedBuilds['windows'] = true
// Set the pipeline status as failure
currentBuild.result = 'FAILURE'
}
},
'MacOS' : {
try {
if ( buildDefinitions['macos'] ) {
node('macos') {
checkout(scm)
sh './build.sh default-macos'
}
} else {
echo 'Build was disabled by user'
}
} catch ( error ) {
failedBuilds['macos'] = true
currentBuild.result = 'FAILURE'
}
},
'Ubuntu' : {
try {
if ( buildDefinitions['ubuntu'] ) {
node('ubuntu') {
checkout(scm)
sh './build.sh default-ubuntu'
}
} else {
echo 'Build was disabled by user'
}
error 'test error'
} catch ( error ) {
failedBuilds['ubuntu'] = true
currentBuild.result = 'FAILURE'
}
}
)
// Check if any builds have been marked as failed
if ( mapContainsTrue( failedBuilds ) ) {
// Remove failed builds from the original map of enabled builds
def updatedBuildDefinitions = subtractMap( buildDefinitions, failedBuilds )
// Check that there are builds left to run
if ( mapContainsTrue( updatedBuildDefinitions ) ) {
// Update the original build map
buildDefinitions = updatedBuildDefinitions
// Lists the failed builds and asks whether to continue or abort the pipeline
timeout(time: 30, unit: 'SECONDS') {
input(
message: 'Builds failed ' + getKeyset( failedBuilds ) + ', do you want to continue the pipeline and skip failed builds?'
)
}
} else {
// Throw an error to terminate the pipeline if no builds are left to run
error 'No builds left to run'
}
}
}
stage('Conditional Downstream') {
parallel (
'Windows' : {
if ( buildDefinitions['windows'] ) {
echo 'You chose to run the windows build!'
} else {
echo 'The windows build was skipped'
}
},
'MacOS' : {
if ( buildDefinitions['macos'] ) {
echo 'You chose to run the macos build!'
} else {
echo 'The macos build was skipped'
}
},
'Ubuntu' : {
if ( buildDefinitions['ubuntu'] ) {
echo 'You chose to run the ubuntu build!'
} else {
echo 'The ubuntu build was skipped'
}
}
)
}
我的全局函数:
// subtractMap.groovy
def call ( map1, map2 ) {
return map1 - map2
}
// mapContainsTrue.groovy
boolean call ( array ) {
for ( entry in array ) {
if ( entry.value == true ) {
isBuildConfigValid = true
return true
} else {
return false
}
}
}
// getKeyset.groovy
def call ( map ) {
return map.keySet() as String[]
}
// generateInputBoolParams.groovy
def call ( array ) {
def parameterList = []
for ( item in array ) {
parameterList.add( booleanParam(defaultValue: item.value, name: item.key) )
}
return parameterList
}
问题
虽然一般功能有效,但 UI 响应没有,除了将管道标记为失败。我希望能够将并行分支标记为失败,以便在 Blue Ocean UI 中很容易看到哪个平台构建失败。
Blue Ocean UI with failed parallel branch in try/catch block
将阶段标记为失败也很有用,因此当不在 Blue Ocean UI 中时,阶段视图会显示失败的阶段(除非只有在管道在该阶段终止时才会发生这种情况)虽然一旦 Blue Ocean 结束 Beta,这就不再是问题了。
Stage View failed stage (Above) and as is (Below)
问题
如何将并行分支标记为失败,以便它在 Blue Ocean UI 中显示为带有红叉?也许使用像
currentBuild.result
这样的环境变量
是否可以对整个舞台进行类似的操作,以便它显示在舞台视图中? (不太重要)
UI 响应可以通过将并行步骤和阶段步骤都包装在一个 try 块中来实现,将并行分支中的 try/catch 块的错误抛出到阶段块.不像设置 属性 那样干净,但对 Blue Ocean 和 Stage View 确实有正确的 UI 响应。
try {
stage('example') {
try {
parallel (
'A' : {
try {
// Example...
}
catch (error) {
// Mark branch as failed somewhere
throw error
}
},
'B' : {
try {
// Example...
}
catch (error) {
// Mark branch as failed somewhere
throw error
}
}
)
}
catch (error) {
throw (error)
}
finally {
// Parallel branch A failed, do you want to continue? etc...
}
}
}
catch (error) {
println (error)
}
这在 Blue Ocean 中对我有用:
stage("Global Stage") {
def parallelStages = [failfast: false]
parallelStages["Branch 1"] = {
//Code for branch 1
}
parallelStages["Branch 2"] = {
//Code for branch 2
}
catchError(buildResult: 'SUCCESS', message: 'Branch Failed', stageResult: 'UNSTABLE')
{ parallel parallelStages }
}
这在 Blue Ocean 中正确地将失败的并行分支标记为 UNSTABLE,不会触及其他阶段结果并将整体构建标记为 SUCCESS