在Jenkins管道中使用Groovy迭代地图是不可能的

Ad *_*d N 14 groovy dictionary jenkins jenkins-pipeline

我们正试图迭代Map,但没有任何成功.我们将问题简化为这个最小的例子:

def map = [
           'monday': 'mon',
           'tuesday': 'tue',
           ]
Run Code Online (Sandbox Code Playgroud)

如果我们尝试迭代:

map.each{ k, v -> println "${k}:${v}" }
Run Code Online (Sandbox Code Playgroud)

仅输出第一个条目: monday:mon


我们知道的替代方案甚至无法进入循环:

for (e in map)
{
    println "key = ${e.key}, value = ${e.value}"
}
Run Code Online (Sandbox Code Playgroud)

要么

for (Map.Entry<String, String> e: map.entrySet())
{
    println "key = ${e.key}, value = ${e.value}"
}
Run Code Online (Sandbox Code Playgroud)

失败,都只显示异常java.io.NotSerializableException: java.util.LinkedHashMap$Entry.(这可能与提出'真实'例外时发生的异常有关,阻止我们知道发生了什么).

我们正在使用最新的稳定jenkins(2.19.1),截至今天(2016/10/20)所有插件都是最新的.

是否有解决方案来迭代MapJenkins管道Groovy脚本中的元素?

Nor*_*nge 21

自从我玩这个以来已经有一段时间了,但迭代地图(和其他容器)的最好方法是使用"经典"for循环或"for in".请参阅Bug:接受Closure的二进制方法处理不当

对于您的特定问题,大多数(所有?)管道DSL命令将添加一个序列点,我的意思是它可以保存管道状态并在以后恢复它.可以考虑等待用户输入,例如,即使重启也要保持此状态.结果是每个实例都必须被序列化 - 但遗憾的是,标准的Map迭代器不可序列化.原始线程

我能想出的最佳解决方案是定义一个函数,将Map转换为可序列化的MapEntries列表.该函数不使用任何管道步骤,因此不必在其中进行任何序列化.

@NonCPS
def mapToList(depmap) {
    def dlist = []
    for (def entry2 in depmap) {
        dlist.add(new java.util.AbstractMap.SimpleImmutableEntry(entry2.key, entry2.value))
    }
    dlist
}
Run Code Online (Sandbox Code Playgroud)

显然需要为每个想要迭代的地图调用它,但是它的好处是,循环体保持不变.

for (def e in mapToList(map))
{
    println "key = ${e.key}, value = ${e.value}"
}
Run Code Online (Sandbox Code Playgroud)

您必须SimpleImmutableEntry第一次批准构造函数,或者很可能通过将mapToList函数放在工作流库中来解决这个问题.

  • 在与詹金斯长达一小时的战斗之后找到这篇文章.不得不经历这种对原生数据结构进行简单迭代的努力是令人发指的! (16认同)
  • 如果你想做任何复杂的事情,那么它是许多人的第一次斗争.除了单独的Groovy是一个PITA(保存引号取决于上下文,只是为了你现在需要知道一个名字表示一个变量,当一个字符串,在该方案"超出"你的意图后),他们在顶部添加了一些奇怪的错误并限制几乎所有基本语言.然后有插件的有趣行为(如果你想保存一个工件及其丢失,管道只是继续并在最后失败).我希望我花时间让它在别处工作...就像设置buildbot. (3认同)

小智 7

或者更简单

for (def key in map.keySet()) {
  println "key = ${key}, value = ${map[key]}"
}
Run Code Online (Sandbox Code Playgroud)