web-dev-qa-db-fra.com

Impossibilité d'itérer sur une carte à l'aide de Groovy dans le pipeline Jenkins

Nous essayons d'itérer sur un Map, mais sans succès. Nous avons réduit notre problème à cet exemple minimal:

def map = [
           'monday': 'mon',
           'tuesday': 'tue',
           ]

Si nous essayons d'itérer avec:

map.each{ k, v -> println "${k}:${v}" }

Seule la première entrée est sortie: monday:mon


Les alternatives que nous connaissons ne sont même pas en mesure d'entrer dans la boucle:

for (e in map)
{
    println "key = ${e.key}, value = ${e.value}"
}

ou

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

Échouent, les deux affichant uniquement l'exception Java.io.NotSerializableException: Java.util.LinkedHashMap$Entry. (ce qui pourrait être lié à une exception survenue lors de la levée de la "vraie" exception, nous empêchant de savoir ce qui s'est passé).

Nous utilisons le dernier jenkins stable (2.19.1) avec tous les plugins à jour à ce jour (2016/10/20).

Existe-t-il une solution pour parcourir les éléments d'un Map dans un script Groovy de pipeline Jenkins?

17
Ad N

Cela fait un certain temps que je n'ai pas joué avec ça, mais la meilleure façon de parcourir les cartes (et autres conteneurs) était avec les boucles "classiques" ou les "pour dans". Voir Bug: mauvaise gestion des méthodes binaires acceptant la fermeture

À votre problème spécifique, la plupart (toutes?) Des commandes DSL de pipeline ajouteront un point de séquence, avec cela je veux dire qu'il est possible de sauvegarder l'état du pipeline et de le reprendre plus tard. Pensez à attendre l'entrée utilisateur par exemple, vous souhaitez conserver cet état même lors d'un redémarrage. Le résultat est que chaque instance live doit être sérialisée - mais l'itérateur de carte standard n'est malheureusement pas sérialisable. fil d'origine

La meilleure solution que je peux trouver consiste à définir une fonction pour convertir une carte en une liste d'entrées de carte sérialisables. La fonction n'utilise aucune étape de pipeline, donc rien ne doit y être sérialisable.

@NonCPS
def mapToList(depmap) {
    def dlist = []
    for (def entry2 in depmap) {
        dlist.add(new Java.util.AbstractMap.SimpleImmutableEntry(entry2.key, entry2.value))
    }
    dlist
}

Cela doit évidemment être appelé pour chaque carte que vous souhaitez itérer, mais le côté positif, c'est que le corps de la boucle reste le même.

for (def e in mapToList(map))
{
    println "key = ${e.key}, value = ${e.value}"
}

Vous devrez approuver le constructeur SimpleImmutableEntry la première fois, ou très probablement vous pourriez contourner cela en plaçant la fonction mapToList dans la bibliothèque de workflow.

23
Norbert Lange

Ou bien plus simple

for (def key in map.keySet()) {
  println "key = ${key}, value = ${map[key]}"
}
0
Lawrence Sim