8

概述

我目前正在配置一个由许多平台构建组成的管道。在管道开始时,用户可以选择构建或跳过哪些平台。

根据每个平台的“构建”阶段是通过还是失败,下游阶段的步骤可以检查该平台构建的状态并确定是否运行。这允许管道在一个或多个平台失败时尝试并完成其他平台(如果用户确认这样做)。

进步

就目前而言,我的管道实现了这一点,允许用户在管道开始时包含/排除平台,并授权管道在平台失败时继续构建(但将管道标记为失败)。这允许归档构建文件/发布 gtests 等,这可以在下游阶段/步骤中完成。这是我的詹金斯文件:

// 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 中,很容易看到哪个平台构建失败。

在 try/catch 块中并行分支失败的 Blue Ocean UI

将阶段标记为失败也很有用,因此当不在 Blue Ocean UI 中时,阶段视图会显示哪个失败(除非只有当管道在该阶段终止时才会发生这种情况),尽管一旦 Blue Ocean 退出 Beta 版,这将真的不再是问题了。

阶段视图失败阶段(上)和原样(下)

问题

  • 如何将并行分支标记为失败,以便它显示在 Blue Ocean UI 中并带有红十字?也许有一个环境变量,比如currentBuild.result

  • 整个舞台是否可能发生类似的事情,以便它显示在舞台视图中?(不太重要)

4

2 回答 2

6

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)
}
于 2017-01-18T16:02:39.307 回答
1

这在 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

于 2020-07-01T20:36:11.440 回答