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

Вот содержимое моего Jenkinsfile :

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

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

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

Изменить: for (i in items) работает как положено:

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

person Eric Citaire    schedule 02.06.2016    source источник
comment
Пробовали ли вы println "${it}"?   -  person Gerold Broser    schedule 02.06.2016
comment
@GeroldBroser: такое же поведение с println и интерполяцией строк.   -  person Eric Citaire    schedule 02.06.2016


Ответы (4)


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

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

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

Это краткое описание — лучший справочник, который я когда-либо видел. работают одинаково, но ведут себя на удивление по-разному. Это хорошая отправная точка для проверки работоспособности и отладки вашей установки, независимо от того, какую итерацию вы рассматриваете, и независимо от того, пытаетесь ли вы использовать @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
person mvr    schedule 31.10.2017

Спасибо @batmat на #jenkins IRC-канал за ответ на этот вопрос!

На самом деле это известная ошибка: JENKINS-26481.

person Eric Citaire    schedule 02.06.2016
comment
Ох, все еще в процессе 6 месяцев спустя! ФФС - person pnovotnak; 15.11.2016

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

Например:

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

Затем вызовите функцию, например

createScript()
load 'steps.groovy'
person Shengyue    schedule 16.02.2017

Вот пример примера цикла с curl без NonCPS :

#!/usr/bin/env groovy

node('master') {
    stagesWithTry([
        'https://google.com/',
        'https://github.com',
        'https://releases.hashicorp.com/',
        'https://kubernetes-charts.storage.googleapis.com',
        'https://gcsweb.istio.io/gcs/istio-release/releases'
    ])
    stage ('ALlinOneStage'){
        stepsWithTry([
            'https://google.com/',
            'https://github.com',
            'https://releases.hashicorp.com/',
            'https://kubernetes-charts.storage.googleapis.com',
            'https://gcsweb.istio.io/gcs/istio-release/releases'
        ])
    }
}
//loop in one stage
def stepsWithTry(list){
    for (int i = 0; i < list.size(); i++) {
        try {
        sh "curl --connect-timeout 15 -v -L ${list[i]}"
        } catch (Exception e) {
            echo "Stage failed, but we continue"
        }
    }
}
//loop in multiple stage
def stagesWithTry(list){
    for (int i = 0; i < list.size(); i++) {
        try {
            stage(list[i]){
          sh "curl --connect-timeout 15 -v -L ${list[i]}"
            }
        } catch (Exception e) {
            echo "Stage failed, but we continue"
        }
    }
}

person airdata    schedule 08.02.2020