如何在Jenkins管道中获取自上次成功构建以来的更改?

22

有没有一份Jenkins Pipeline脚本可以将自上一个成功构建以来的所有更改放入变量中?我正在使用git和多分支管道作业。

6个回答

20

好的,我设法拼凑出了一些东西。我相信你可以更好地迭代数组,但目前这就是我所拥有的:

node('Android') {
  passedBuilds = []

  lastSuccessfulBuild(passedBuilds, currentBuild);

  def changeLog = getChangeLog(passedBuilds)
  echo "changeLog ${changeLog}"
}

def lastSuccessfulBuild(passedBuilds, build) {
  if ((build != null) && (build.result != 'SUCCESS')) {
      passedBuilds.add(build)
      lastSuccessfulBuild(passedBuilds, build.getPreviousBuild())
   }
}

@NonCPS
def getChangeLog(passedBuilds) {
    def log = ""
    for (int x = 0; x < passedBuilds.size(); x++) {
        def currentBuild = passedBuilds[x];
        def changeLogSets = currentBuild.rawBuild.changeSets
        for (int i = 0; i < changeLogSets.size(); i++) {
            def entries = changeLogSets[i].items
            for (int j = 0; j < entries.length; j++) {
                def entry = entries[j]
                log += "* ${entry.msg} by ${entry.author} \n"
            }
        }
    }
    return log;
  }

很不幸,工作的第一个构建返回0个更改。理想情况下,我会考虑在第一个构建中将每个文件都视为已更改。参考:https://issues.jenkins-ci.org/browse/JENKINS-26354 - RaGe
你会如何修改这个程序以接受一个特定的 Jenkins 作业作为参数? - Chris F
这总是给我一个空的更改日志。在lastSuccessfulBuild()函数中,为什么if()语句是build.result != SUCCESS - Chris F
@RaGe:在经历了一番挣扎之后,我通过实证发现currentBuild.rawBuild.changeSets是在调用checkout scm时初始化的。如果你的调用在这条指令之前进行,你将得不到任何东西。 - Max
在lastSuccessfulBuild内的if语句应该是:if ((build != null) && (build.getResult().toString() == 'SUCCESS'))。build.result将返回类hudson.model.Result,而不是一个字符串。 - Biju

13

根据CaptRespect的回答,我编写了以下用于声明式流水线的脚本:

def changes = "Changes:\n"
build = currentBuild
while(build != null && build.result != 'SUCCESS') {
    changes += "In ${build.id}:\n"
    for (changeLog in build.changeSets) {
        for(entry in changeLog.items) {
            for(file in entry.affectedFiles) {
                changes += "* ${file.path}\n"
            }
        }
    }
    build = build.previousBuild
}
echo changes

stage->when->expression 部分中,这非常有用,可以在某些文件发生更改时仅运行一个阶段。尽管我还没有涉及到这一部分,但我希望从中创建一个共享库,并使其能够传递一些 globbing 模式以进行检查。

编辑:顺便查看文档,如果您想深入了解的话。您应该能够将所有的 object.getSomeProperty() 调用转换为只使用 entry.someProperty


很好。如果您想要贡献,这里有一个带有一些流水线示例的 Github 存储库:https://github.com/jenkinsci/pipeline-examples - CaptRespect
这不是声明式流水线风格,而是脚本化流水线! - Lincoln
1
@Lincoln,是的。你不能以声明方式完成它。你需要将它包装在脚本块中或从共享库中使用它。 - andsens

1
为了将更改作为字符串列表返回,而不仅仅是打印它们,您可以使用此函数(基于@andsens的答案):
def getChangesSinceLastSuccessfulBuild() {
    def changes = []
    def build = currentBuild

    while (build != null && build.result != 'SUCCESS') {
        changes += (build.changeSets.collect { changeSet ->
            (changeSet.items.collect { item ->
                (item.affectedFiles.collect { affectedFile ->
                    affectedFile.path
                }).flatten()
            }).flatten()
        }).flatten()

        build = build.previousBuild
    }

    return changes.unique()
}

感谢您将这段内容制作成可复制粘贴的片段!这正是我所需要的,而且运行得非常顺利。您帮我省去了很多麻烦。:) - moertel

1
这是我使用过的内容:
def listFilesForBuild(build) {
  def files = []
  currentBuild.changeSets.each {
    it.items.each {
      it.affectedFiles.each {
        files << it.path
      }
    }
  }
  files
}

def filesSinceLastPass() {
  def files = []
  def build = currentBuild
  while(build.result != 'SUCCESS') {
    files += listFilesForBuild(build)
    build = build.getPreviousBuild()
  }
  return files.unique()
}

def files = filesSinceLastPass()

currentBuild需要声明性流水线吗?当我在我的脚本流水线中尝试时,我得到了java.lang.NullPointerException: Cannot get property 'result' on null object的错误,这可能是因为currentBuild为空。 - Shane Bishop
此外,listFilesForBuild() 中的 build 参数未被使用(你在该函数中使用了currentBuild代替它)。 - Shane Bishop

0

是的,我查看了那个,但是无法弄清楚如何从JenkinsFile中访问它。 - CaptRespect

0

对于使用 Accurev 的任何人,这里是 andsens 答案的一种适应方式。由于 Accurev 插件没有实现 getAffectedFiles,因此无法使用 andsens 的答案。可以在扩展 ChangeLogSet.Entry 类的 AccurevTransaction 的文档中找到更多信息,链接在这里

import hudson.plugins.accurev.*

def changes = "Changes: \n"
build = currentBuild
// Go through the previous builds and get changes until the
// last successful build is found.
while (build != null && build.result != 'SUCCESS') {
    changes += "Build ${build.id}:\n"

    for (changeLog in build.changeSets) {
        for (AccurevTransaction entry in changeLog.items) {
            changes += "\n    Issue: " + entry.getIssueNum()
            changes += "\n    Change Type: " + entry.getAction()
            changes += "\n    Change Message: " + entry.getMsg()
            changes += "\n    Author: " + entry.getAuthor()
            changes += "\n    Date: " + entry.getDate()
            changes += "\n    Files: "
            for (path in entry.getAffectedPaths()) {
                changes += "\n        " + path;
            }
            changes += "\n"
        }
    }
    build = build.previousBuild
}
echo changes
writeFile file: "changeLog.txt", text: changes

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