2016-10-20 26 views
7

Próbujemy iterować nad Map, ale bez powodzenia. Zredukowaliśmy naszą problem z tym minimalnym przykład:Niemożliwość iteracji nad mapą za pomocą Groovy w ramach Jenkins Pipeline

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

Jeśli spróbujemy iteracyjne z:

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

tylko pierwszy wpis jest wyjście: monday:mon


Alternatywy znamy nie są nawet w stanie wejść w pętlę:

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

lub

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

zawodzą, tylko pokazując zarówno wyjątek java.io.NotSerializableException: java.util.LinkedHashMap$Entry. (co może być związane z wyjątkiem występującym podczas podnoszenia "prawdziwego" wyjątku, co uniemożliwia nam poznanie, co się stało).

Korzystamy z najnowszych stabilnych jenkinsów (2.19.1) z najnowszymi wtyczkami na dzień dzisiejszy (2016/10/20).

Czy istnieje rozwiązanie do iterowania elementów w pliku Map w skrypcie Groovy dotyczącym potoku Jenkins?

Odpowiedz

17

To był jakiś czas, odkąd się z tym bawiłem, ale najlepszym sposobem na iterację poprzez mapy (i inne pojemniki) było "klasyczne" dla pętli lub "dla". Patrz: Bug: Mishandling of binary methods accepting Closure

Do konkretnego problemu, większość (wszystkich?) Poleceń DSL potoku doda punkt sekwencji, co oznacza, że ​​możliwe jest zapisanie stanu potoku i wznowienie go w późniejszym czasie. Pomyśl o oczekiwaniu na dane wejściowe od użytkownika, na przykład chcesz zachować ten stan nawet po ponownym uruchomieniu. Powoduje to, że każda instancja na żywo musi być przekształcona do postaci szeregowej, ale standardowy iterator mapy nie jest niestety możliwy do serializacji. Original Thread

Najlepszym rozwiązaniem, jakie mogę wymyślić, jest zdefiniowanie funkcji konwersji mapy na listę serializowanych MapEntries. Ta funkcja nie używa żadnych kroków potoku, więc nic nie musi być w niej szeregowalne.

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

To musi być oczywiście nazywany na każdej mapie chcesz iteracyjne, ale do góry nogami to, że ciało pętli pozostaje taka sama.

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

Będziesz musiał zatwierdzić konstruktora SimpleImmutableEntry po raz pierwszy, lub być może można obejść, że umieszczając funkcję mapToList w bibliotece workflow.

+12

Znaleźliśmy ten post po godzinnej walce z Jenkinsem. Konieczność przejścia przez tę walkę do prostej iteracji w rodzimej strukturze danych jest oburzająca! –

+2

Jeśli chcesz zrobić coś skomplikowanego, to jest to pierwsza walka wielu. Oprócz samego Groovy'ego jest to PITA (zapisywanie cudzysłowów w zależności od kontekstu, tak abyś teraz musiał się uczyć, gdy nazwa oznacza zmienną, a gdy sznur, po tym, jak schemat "przechytrzył" twoją intencję), dodali dziwaczne błędy na górze i ograniczają prawie wszystko z języka bazowego.Następnie są zabawne zachowania wtyczek (Jeśli chcesz zapisać artefakt i jego brak, potok kontynuuje się i kończy na końcu). Żałuję, że nie poświęcałem czasu na to, by działało to trochę inaczej ... jak na przykład przy tworzeniu buildbota. –

+0

Użyłem tego rozwiązania, ale nadal otrzymuję komunikat o błędzie: "org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Skrypty nie mogą używać nowych" (to jest nowa mapa mapToList) –