Почему каждый цикл в файле Jenkinsfile останавливается на первой итерации

вот содержание моего Jenkinsfile :

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

при выполнении задания в Jenkins (с плагин трубопровода), печатается только первый элемент в списке.

может кто-нибудь объяснить мне это странное поведение? Это жук? или это просто я не понимаю синтаксис Groovy?

редактировать: the for (i in items) работает :

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

3 ответов


в принятом ответе здесь говорится, что это известная ошибка и использует обходной путь, который не работал для меня, поэтому я предложу обновление с тем, что я нашел в последнее время.

несмотря на постановление Дженкинс-26481 (довольно недавно, на момент написания этой статьи) многие люди могут застрять с более старой версией Дженкинса, где исправление недоступно. Итерация for-loop над литеральным списком может иногда работать, но связанные с этим проблемы, такие как Дженкинс-46749 и Дженкинс-46747 кажется, продолжают терзать многих пользователей. Кроме того, в зависимости от точного контекста в вашем Jenkinsfile, возможно echo будет работать, тогда как sh терпит неудачу, и вещи могут потерпеть неудачу молча или они могут привести к сбою сборки с неудачами сериализации.

если вы не любите сюрпризы (пропущенные циклы и тихие сбои), и если вы хотите, чтобы ваши Jenkinsfiles были наиболее переносимыми в нескольких версиях Jenkins, основная идея заключается в том, что вы должны всегда используйте классические счетчики в своих for-loops и игнорируйте другие функции groovy.

в этом суть это лучшая ссылка, которую я видел, и излагает многие случаи, которые, как вы думаете, должны работать одинаково, но иметь удивительно разное поведение. Это хорошее начальное место для проверки здравомыслия и отладки вашей установки, независимо от того, какую итерацию вы смотрите и независимо от того, пытаетесь ли вы использовать @NonCPS, сделайте свою итерацию непосредственно внутри node{}, или вызывать отдельную функцию.

опять же, я не беру на себя ответственность за саму работу, но я встраиваю суть итерационных тестов ниже для потомков:

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

спасибо @batmat on #Дженкинс IRC-канал для ответа на этот вопрос!

Это на самом деле известная ошибка : Дженкинс-26481.


обходной путь для этой проблемы-развернуть все команды в плоский текстовый файл в виде скрипта groovy. Затем используйте load step для загрузки файла и выполнения.

например:

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

затем вызовите функцию like

createScript()
load 'steps.groovy'