【问题标题】:How to check status of specific stage (of previous build) during Jenkins build?如何在 Jenkins 构建期间检查特定阶段(先前构建)的状态?
【发布时间】:2020-11-17 19:40:00
【问题描述】:

在 Jenkins(多分支管道)中,我希望能够找到某个阶段成功的最新构建(例如,未跳过,未失败)。我可以轻松循环之前的构建并检查构建本身是否成功,但我不知道如何检查给定阶段是否成功。

Collection<String> getChangedFilesSinceLastSuccessfulStageBuild(String... stages) {
    def files = new HashSet<String>()
    def build = currentBuild

    // look at previous build(s) until we find a successful build AND the given stage
    // (or leafiest child stage) was also successful (or we reach the beginning of time)
    while (build != null && build.result != 'SUCCESS' /* ... unknown here ... */) {
        // ... round up the changed files, etc. (easy)
    }

    // ... more easy things here

    return files;
}

这将被称为提问过程的一部分:

自上次成功构建 API 以来,是否有任何与 API 相关的文件发生更改?

它会被称为(例如):

hasApiChanges = getChangedFilesSinceLastSuccessfulStageBuild("Build/Test API")
hasUiChanges = getChangedFilesSinceLastSuccessfulStageBuild("Build/Test API")

(警告——“构建/测试 API”阶段实际上是一个“构建/测试”阶段,其中包含“API”和“UI”的并行阶段。所以我真的需要找到“API”阶段在“构建/测试”阶段。)

不确定如何按名称查找(嵌套)阶段,然后检查其结果。

或者,在那个阶段,我可以将一些持久的构建元数据写入当前构建,然后在后续构建期间检查该元数据是否存在/是否为真。 (也不知道该怎么做。)

PS:我似乎无法找到与此相关的 Javadoc(多分支管道)——希望提供指向该链接的链接。

【问题讨论】:

    标签: jenkins


    【解决方案1】:

    我最终实现了这个功能,但没有在步骤级别跟踪成功。我基本上回顾了最近的构建,对于任何成功的构建,如果它对“受影响的”文件进行了更改,并且 在构建之前也对受影响的文件进行了更改,那么构建将继续进行一组文件(例如 API)。

    我还在管道中添加了参数,以便我们可以根据需要覆盖此规则。

    设置变量

    final String forceApiBuildParam = 'forceApiBuild'
    final String skipApiBuildParam = 'skipApiBuild'
    final String forceUiBuildParam = 'forceUiBuild'
    final String skipUiBuildParam = 'skipUiBuild'
    final String apiPathRegex = /^(api\/|build.gradle|checkstyle.xml|settings.gradle|Jenkinsfile).*/
    final String uiPathRegex = /^(ui\/|Jenkinsfile).*/
    Boolean apiBuildEnabled = params[forceApiBuildParam] == true
        ? true
        : (params[skipApiBuildParam] == true 
            ? false 
            : hasChangesInPathsSinceLastSuccessFulBuildWithChangesInPaths(verbose, apiPathRegex))
    Boolean uiBuildEnabled = params[forceUiBuildParam] == true
        ? true
        : (params[skipUiBuildParam] == true 
            ? false 
            : hasChangesInPathsSinceLastSuccessFulBuildWithChangesInPaths(verbose, uiPathRegex))
    

    设置参数

    parameters {
        booleanParam(name: forceApiBuildParam, defaultValue: false, description: 'Whether or not to force the API to build and deploy.')
        booleanParam(name: skipApiBuildParam, defaultValue: false, description: 'Whether or not to skip the API build, even if there are changes.')
        booleanParam(name: forceUiBuildParam, defaultValue: false, description: 'Whether or not to force the UI to build and deploy.')
        booleanParam(name: skipUiBuildParam, defaultValue: false, description: 'Whether or not to skip the UI build, even if there are changes.')
    }
    

    检查更改的功能(可以简化)

    /**
     * Attempts to determine if, for a set of paths given in the form of a regex, 
     * any files have changed (which match those paths) since the last build which
     *  was successful and also had changes within those paths.
     *
     * @param verbose true to include additional logging
     * @param pathRegex the regex of the paths to consider
     * @return true if paths matching the provided regex have changed since the last
     *              successful build which also had changes to a file matching the
     *              regex; false if otherwise or if no successful build yet exist
     */
    boolean hasChangesInPathsSinceLastSuccessFulBuildWithChangesInPaths(boolean verbose, String pathRegex) {
        Collection<String> allChangedPaths = new HashSet<String>()
        RunWrapper build = (RunWrapper) (Object) currentBuild
        int buildCount = 0
    
        echo "Checking for changed paths since last successful build for paths matching: ${pathRegex}"
    
        while (build != null) {
            if (verbose) echo "Checking build ${build.displayName}..."
    
            WorkflowRun rawBuild = (WorkflowRun) build.rawBuild
            String buildHash = rawBuild.getAction(BuildData.class).lastBuiltRevision.sha1String.substring(8)
            boolean success = build.result == 'SUCCESS'
    
            if (success) {
                echo "Found successful build (${build.displayName}; #${buildHash}) -- ${buildCount} builds ago."
            }
            else if (verbose) {
                echo "Found failed build (${build.displayName}; #${buildHash}) -- ${buildCount} builds ago."
            }
    
            for (GitChangeSetList changeLog in (build.changeSets as List<GitChangeSetList>)) {
                Collection<String> changedChangeLogPaths = new HashSet<String>()
    
                for (GitChangeSet entry in changeLog) {
                    if (verbose) echo "Checking commit ${entry.commitId}..."
    
                    // if this build is successful, check if the already-stored files match `pathRegex`
                    // if so, then there have been changes since this successful build
                    if (success) {
                        for (String thisBuildPath in entry.affectedPaths) {
                            if (verbose) echo "Checking path ${thisBuildPath} for commit ${entry.commitId}..."
    
                            // check if this file (changed in this build) matches
                            if (thisBuildPath.matches(pathRegex)) {
                                if (verbose) echo "Match found!"
    
                                // now look through all changed files *before* this build for matches
                                for (String allBuildsPath in allChangedPaths) {
                                    if (verbose) echo "Checking changed path ${allBuildsPath}..."
    
                                    // check if this file (which was changed before this build) matches
                                    if (allBuildsPath.matches(pathRegex)) {
                                        echo "Since build ${build.displayName}, path \"${allBuildsPath}\" was changed and matches: ${pathRegex}"
    
                                        return true
                                    }
                                }
    
                                // if we get here, then we found a successful build matching `pathRegex`, but since then
                                // there have been no files changed which match `pathRegex` -- so no need to build again
                                echo "No files changed since build ${build.displayName} matching: ${pathRegex}"
    
                                return false
                            }
                        }
                    }
    
                    // add the affected paths to the list of changes for the changeset
                    changedChangeLogPaths.addAll(entry.affectedPaths)
                }
    
                // if we get here then the build was not successful or had no files changed whose
                // path matches `pathRegex` -- add the files to the list and continue
                allChangedPaths.addAll(changedChangeLogPaths)
            }
    
            build = build.previousBuild
            buildCount++
        }
    
        echo "Found no successful builds (checked ${buildCount} builds) with changes to paths matching: ${pathRegex}"
    
        // return true to signify so we can do the initial build
        return true;
    }
    

    这种方法有一个需要注意的地方,那就是它的构建频率可能比它需要的略多。假设有一个 API 更改破坏了构建 - 不是因为 API 代码更改中的问题,而是因为构建脚本中的问题,或 API 的部署(代码更改之外的东西)。然后,API 将在每个后续构建中重新构建,直到另一个 API 更改出现。

    • 构建 #100 -- API 更改,构建损坏
    • Build #101...#n -- 没有 API 更改;由于 #100 失败而重建 API - 现在成功
    • Build #102...#n -- 没有 API 更改;由于 #100 失败,API 被重建——尽管从那时起 #101 成功了
    • Build #n+1 -- API 更改,重建 API
    • Build #n+2 -- 没有 API 更改; API 重新构建,因为 #n+1 有 API 更改并且成功

    在上面的示例中,API 从 #101 开始重新构建,直到下一次 API 更改出现并导致构建成功。我认为这个解决方案不可能解决这个问题,除非可以为每个构建标记,无论 API/UI 是否成功构建。如果我们能做到这一点,那么 #102+ 就不会被构建,因为我们知道 API 构建在 #101 上是成功的,即使在构建过程中没有任何 API 文件受到影响。

    【讨论】:

      猜你喜欢
      • 2017-03-07
      • 1970-01-01
      • 2020-02-19
      • 1970-01-01
      • 2022-11-14
      • 1970-01-01
      • 2019-10-08
      • 2017-06-04
      • 2015-04-03
      相关资源
      最近更新 更多