Почему каждый цикл в Jenkinsfile останавливается на первой итерации
Вот содержание моего Jenkinsfile
:
node {
// prints only the first element 'a'
[ 'a', 'b', 'c' ].each {
echo it
}
}
При выполнении задания в Jenkins (с плагином Pipeline) печатается только первый элемент в списке.
Может кто-нибудь объяснить мне это странное поведение? Это ошибка? или это просто я не разбираюсь в синтаксисе Groovy?
Изменить: for (i in items)
работает как положено:
node {
// prints 'a', 'b' and 'c'
for (i in [ 'a', 'b', 'c' ]) {
echo i
}
}
3 ответа
Принятый ответ здесь гласит, что это известная ошибка и использует обходной путь, который не работает для меня, поэтому я предложу обновление с тем, что я нашел в последнее время.
Несмотря на разрешение JENKINS-26481 (довольно недавнее, на момент написания этой статьи), многие могут застрять в более старой версии Jenkins, где исправление недоступно. Итерация цикла for по буквальному списку может иногда работать, но связанные проблемы, такие как JENKINS-46749 и JENKINS-46747, по- видимому, продолжают сбивать с толку многих пользователей. Кроме того, в зависимости от точного контекста в вашем Jenkinsfile, возможно, echo
будет работать тогда как sh
происходит сбой, и что-то может произойти сбой молча, или они могут привести к сбою сборки с ошибками сериализации
Если вам не нравятся сюрпризы (пропущенные циклы и тихие сбои) и если вы хотите, чтобы ваши Jenkinsfiles были наиболее переносимыми в нескольких версиях Jenkins, основная идея заключается в том, что вы всегда должны использовать классические счетчики в циклах for и игнорировать другие 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 на IRC-канале #jenkins за ответ на этот вопрос!
На самом деле это известная ошибка: JENKINS-26481.
Обходной путь для этой проблемы состоит в том, чтобы развернуть все команды в простой текстовый файл как отличный сценарий. Затем используйте шаг загрузки, чтобы загрузить файл и выполнить.
Например:
@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'
Вот пример примера цикла с 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"
}
}
}