web-dev-qa-db-de.com

Warum eine Schleife in einer Jenkins-Datei bei der ersten Iteration stoppt

Hier ist der Inhalt meines Jenkinsfile:

node {
    // prints only the first element 'a'
    [ 'a', 'b', 'c' ].each {
        echo it
    }
}

Bei der Ausführung des Jobs in Jenkins (mit dem Pipeline-Plugin ) wird nur das erste Element in der Liste gedruckt.

Kann mir jemand dieses merkwürdige Verhalten erklären? Ist es ein Bug? Oder verstehe ich die Groovy-Syntax einfach nicht?

Bearbeiten : Die Funktion for (i in items) funktioniert wie erwartet:

node {
    // prints 'a', 'b' and 'c'
    for (i in [ 'a', 'b', 'c' ]) {
        echo i
    }
}
14
Eric Citaire

Die hier akzeptierte Antwort besagt, dass es sich um einen bekannten Fehler handelt, und verwendet eine Problemumgehung, die bei mir nicht funktioniert hat. Daher werde ich ein Update mit dem anbieten, was ich in letzter Zeit gefunden habe.

Trotz der Auflösung von JENKINS-26481 (zum Zeitpunkt des Schreibens relativ neu) stecken viele Leute möglicherweise in einer älteren Version von Jenkins fest, in der das Update nicht verfügbar ist. Die For-Loop-Iteration über eine Literal-Liste kann manchmal funktionieren, aber verwandte Probleme wie JENKINS-46749 und JENKINS-46747 scheinen weiterhin viele Benutzer zu überraschen. Abhängig vom genauen Kontext in Ihrer Jenkins-Datei funktioniert möglicherweise echo, wohingegen sh fehlschlägt und die Dinge möglicherweise unbemerkt fehlschlagen oder der Build bei Serialisierungsfehlern abstürzt.

Wenn Sie Überraschungen (übersprungene Schleifen und stille Fehler) nicht mögen und Ihre Jenkins-Dateien für mehrere Versionen von Jenkins am portabelsten sein sollen, sollten Sie anscheinend immer klassische Zähler verwenden in deinen For-Loops und ignoriere andere groovige Features.

This Gist ist die beste Referenz, die ich gesehen habe, und beschreibt viele Fälle, von denen Sie denken, dass sie gleich funktionieren sollten, aber ein überraschend unterschiedliches Verhalten aufweisen. Es ist ein guter Ausgangspunkt für die Einrichtung von Überprüfungen und das Debuggen Ihres Setups, unabhängig davon, welche Art von Iteration Sie verwenden und ob Sie versuchen, @NonCPS Zu verwenden. Führen Sie Ihre Iteration direkt in node{} Oder rufen Sie eine separate Funktion auf.

Auch hier nehme ich keine Anerkennung für die Arbeit selbst, aber ich binde den Kern der Iterationstestfälle unten für die Nachwelt ein:

abcs = ['a', 'b', 'c']

node('master') {
    stage('Test 1: loop of echo statements') {
        echo_all(abcs)
    }
    stage('Test 2: loop of sh commands') {
        loop_of_sh(abcs)
    }
    stage('Test 3: loop with preceding SH') {
        loop_with_preceding_sh(abcs)
    }
    stage('Test 4: traditional for loop') {
        traditional_int_for_loop(abcs)
    }
}

@NonCPS // has to be NonCPS or the build breaks on the call to .each
def echo_all(list) {
    list.each { item ->
        echo "Hello ${item}"
    }
}
// outputs all items as expected

@NonCPS
def loop_of_sh(list) {
    list.each { item ->
        sh "echo Hello ${item}"
    }
}
// outputs only the first item

@NonCPS
def loop_with_preceding_sh(list) {
    sh "echo Going to echo a list"
    list.each { item ->
        sh "echo Hello ${item}"
    }
}
// outputs only the "Going to echo a list" bit

//No NonCPS required
def traditional_int_for_loop(list) {
    sh "echo Going to echo a list"
    for (int i = 0; i < list.size(); i++) {
        sh "echo Hello ${list[i]}"
    }
}
// echoes everything as expected
18
mvr

Vielen Dank an @ batmat on # jenkins IRC channel für die Beantwortung dieser Frage!

Es ist tatsächlich ein bekannter Fehler: JENKINS-26481 .

5
Eric Citaire

Eine Problemumgehung für dieses Problem besteht darin, alle Befehle in eine flache Textdatei als starkes Skript zu erweitern. Verwenden Sie dann den Ladeschritt, um die Datei zu laden und auszuführen.

Beispielsweise:

@NonCPS
def createScript(){
    def cmd=""
    for (i in [ 'a', 'b', 'c' ]) {
        cmd = cmd+ "echo $i"
    }
    writeFile file: 'steps.groovy', text: cmd
}

Rufen Sie dann die Funktion wie folgt auf

createScript()
load 'steps.groovy'
2
Shengyue