如何在Jenkins/Hudson中获取自上次构建以来已更改的文件列表

35

我已经搭建了Jenkins,但我想知道当前构建与上一个构建之间添加/更改的文件有哪些。如果源代码树的某些部分发生更改,我想运行一些长时间运行的测试。

经过在互联网上的搜索,我没有找到Hudson/Jenkins具有此功能的提及,尽管建议使用SVN提交后钩子来实现它。也许这很简单,每个人都(除了我)知道如何做到这一点!

这种可能吗?

11个回答

18

我是按照以下方式完成的。我不确定是否是正确的方式,但它似乎正在工作。您需要安装Jenkins Groovy插件并执行以下脚本。

import hudson.model.*;
import hudson.util.*;
import hudson.scm.*;
import hudson.plugins.accurev.*

def thr = Thread.currentThread();
def build = thr?.executable;

def changeSet= build.getChangeSet();

changeSet.getItems();

ChangeSet.getItems()方法可以获取到变更内容。由于我使用的是accurev,我使用了如下代码:List<AccurevTransaction> accurevTransList = changeSet.getItems();

在这里,如果文件在当前构建窗口内被多次提交,那么修改后的列表将包含重复的文件名。


使用了您的代码得出了这个解决方案:https://dev59.com/AWnWa4cB1Zd3GeqP3LS8#12939663,非常感谢! - ChrLipp
2
如果在thr上使用了安全导航(?)运算符,那么build可能为null,然后在下一行的build.getChangeSet()处会抛出空指针异常,这种情况是有可能发生的。 - solstice333
我遇到了“groovy.lang.MissingPropertyException: No such property: executable for class: java.lang.Thread”这个错误信息……我肯定是漏掉了什么吧? - Bob

5
echo $SVN_REVISION
svn_last_successful_build_revision=`curl $JOB_URL'lastSuccessfulBuild/api/json' | python -c 'import json,sys;obj=json.loads(sys.stdin.read());print obj["'"changeSet"'"]["'"revisions"'"][0]["'"revision"'"]'`
diff=`svn di -r$SVN_REVISION:$svn_last_successful_build_revision --summarize`

2
请问您能否解释一下如何实现这个功能以及在哪里操作吗?您的解决方案似乎是我所需要的,但我不知道如何实施它。 - Mike
@user1725378 只在 构建 shell 中运行,仅适用于 Subversion。 - jollychang

5

如果您正在轮询更改并使用 SVN 更新,则 CI 服务器将向您显示更改列表。但是,您似乎希望根据修改的文件更改构建行为。我认为没有任何一种基于 Jenkins 的开箱即用的方法可以做到这一点。

提交后钩子是一个合理的想法。您可以使作业参数化,并让您的钩子脚本根据提交的更改设置参数值启动构建。我不确定这对您来说可能有多难。

但是,您可能需要考虑将其拆分为两个单独的作业-一个在每次提交时运行,另一个用于您不总是需要的长时间运行的测试。个人而言,我更喜欢保持作业行为在执行之间保持一致。否则,可追溯性会受到影响。


1
你似乎想根据修改的文件来更改构建的行为。这可以通过Maven增量构建(插件)来实现。 - Paul Draper
在 Jenkins\jobs\projectName\builds\75\ 目录下,我没有看到最后一个变更日志文件,但是在 Jenkins 仪表板上,我看到了最后更改选项和更改记录,那么要如何找到这个最后更改的文件呢? - Ashish Karpe

5
你可以使用Jenkins 远程访问API来获取当前构建的机器可读描述,包括其完整的变更集。这里需要注意的是,如果你配置了“静默期”,Jenkins可能会将多个提交到同一代码库的提交合并为一个构建,因此仅依赖单个修订号有点天真。
我喜欢保持我的Subversion提交后钩子相对简单,并将任务交给CI服务器。为此,我使用wget触发构建,类似于以下内容...
/usr/bin/wget --output-document "-" --timeout=2 \
    https://ci.example.com/jenkins/job/JOBID/build?token=MYTOKEN

然后,在Jenkins上配置作业以执行一个Python脚本,利用BUILD_URL环境变量构建API的URL。URL最终看起来像这样:
https://ci.example.com/jenkins/job/JOBID/BUILDID/api/json/

这里是一些示例Python代码,可以在shell脚本中运行。为了保持可读性,我省略了任何错误处理或HTTP身份验证等内容。
import os
import json
import urllib2


# Make the URL 
build_url = os.environ['BUILD_URL']
api = build_url + 'api/json/'

# Call the Jenkins server and figured out what changed
f = urllib2.urlopen(api)
build = json.loads(f.read())
change_set = build['changeSet']
items = change_set['items']
touched = []
for item in items:
    touched += item['affectedPaths']

4
使用构建流程插件和Git:
final changeSet = build.getChangeSet()
final changeSetIterator = changeSet.iterator()
while (changeSetIterator.hasNext()) {
  final gitChangeSet = changeSetIterator.next()
  for (final path : gitChangeSet.getPaths()) {
    println path.getPath()
  }
}

有没有一种方法可以获取文件的绝对路径?这会给出相对于存储库的路径,因此我无法确定文件所在的存储库。 - The Unknown Dev
我脑海中没有想起来。你需要查看源代码 https://github.com/jenkinsci/git-plugin/blob/master/src/main/java/hudson/plugins/git/GitChangeSet.java 或者自己尝试实验。 - Noel Yap
构建流插件现已弃用,在我的 Jenkins 2.89 中,这个解决方案不起作用。 - heroin

3

使用Jenkins流水线(支持API插件2.2或更高版本),这个解决方案对我有用:

def changeLogSets = currentBuild.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]
    def files = new ArrayList(entry.affectedFiles)
    for (int k = 0; k < files.size(); k++) {
      def file = files[k]
      println file.path
    }
  }
}

请查看如何访问流水线作业中的更改日志

2

通过Groovy:

<!-- CHANGE SET -->
<% changeSet = build.changeSet
if (changeSet != null) {
hadChanges = false %>
<h2>Changes</h2>
<ul>
<% changeSet.each { cs ->
hadChanges = true
aUser = cs.author %>
<li>Commit <b>${cs.revision}</b> by <b><%= aUser != null ? aUser.displayName :      it.author.displayName %>:</b> (${cs.msg})
<ul>
<% cs.affectedFiles.each { %>
<li class="change-${it.editType.name}"><b>${it.editType.name}</b>: ${it.path}                              </li> <%  } %> </ul>   </li> <%  }

 if (!hadChanges) { %>  
  <li>No Changes !!</li>
 <%  } %>   </ul> <% } %>

新分支的changeSet始终为空。Jenkins团队已经意识到这一点,并且由于Git的复杂性,他们不打算修复它,因为没有普遍正确的方法来确定正确的父级进行比较:issues.jenkins.io/browse/JENKINS-26354 - PFudd

2
#!/bin/bash

set -e

job_name="whatever"
JOB_URL="http://myserver:8080/job/${job_name}/"
FILTER_PATH="path/to/folder/to/monitor"

python_func="import json, sys
obj = json.loads(sys.stdin.read())
ch_list = obj['changeSet']['items']
_list = [ j['affectedPaths'] for j in ch_list ]
for outer in _list:
  for inner in outer:
    print inner
"

_affected_files=`curl --silent ${JOB_URL}${BUILD_NUMBER}'/api/json' | python -c "$python_func"`

if [ -z "`echo \"$_affected_files\" | grep \"${FILTER_PATH}\"`" ]; then
  echo "[INFO] no changes detected in ${FILTER_PATH}"
  exit 0
else
  echo "[INFO] changed files detected: "
  for a_file in `echo "$_affected_files" | grep "${FILTER_PATH}"`; do
    echo "    $a_file"
  done;
fi;

这与以前略有不同——我需要一个用于特定文件夹上Git的脚本……因此,我基于jollychang编写了一个检查脚本。

它可以直接添加到作业的执行shell脚本中。如果没有检测到文件,它将exit 0,即SUCCESS……这样你就可以始终在代码库提交时触发,但只有在感兴趣的文件夹中的文件更改时才构建。

但是……如果您想要按需构建(即单击“立即构建”)并使用自上次构建以来的更改,您需要将_affected_files更改为:

_affected_files=`curl --silent $JOB_URL'lastSuccessfulBuild/api/json' | python -c "$python_func"`

1

我试图将这个添加到评论中,但是评论中的代码是不行

只是想要美化来自heroin的答案中的代码:

def changedFiles = []
def changeLogSets = currentBuild.changeSets
for (entries in changeLogSets) {
    for (entry in entries) {
        for (file in entry.affectedFiles) {
            echo "Found changed file: ${file.path}"
            changedFiles += "${file.path}"
        }
    }
}

请记住,在某些情况下,git插件会返回空的changeSet,例如:
  • 在新创建的分支上首次运行
  • 当自上次构建以来远程没有新的提交时,“立即构建”按钮进行构建
有关更多详细信息,请参考https://issues.jenkins-ci.org/browse/JENKINS-26354

0
注意:您必须使用Jenkins自己的SVN客户端来获取变更列表。通过shell构建步骤执行此操作将不会列出构建中的更改。

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