为什么 Jenkinsfile 中的每个循环在第一次迭代时停止
Why an each loop in a Jenkinsfile stops at first iteration
这是我的 Jenkinsfile
的内容:
node {
// prints only the first element 'a'
[ 'a', 'b', 'c' ].each {
echo it
}
}
在 Jenkins 中执行作业时(使用 Pipeline plugin),仅打印列表中的第一项。
谁能给我解释一下这种奇怪的行为?这是一个错误吗?还是只是我不理解 Groovy 语法?
编辑:for (i in items)
按预期工作:
node {
// prints 'a', 'b' and 'c'
for (i in [ 'a', 'b', 'c' ]) {
echo i
}
}
感谢@batmat on #jenkins IRC channel回答这个问题!
这实际上是一个已知错误:JENKINS-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
}
然后像
那样调用函数
createScript()
load 'steps.groovy'
此处接受的答案表明这是一个已知错误,并使用了对我不起作用的解决方法,因此我将提供我最近发现的更新。
尽管 JENKINS-26481 (fairly recent, as of this writing) many people may be stuck with an older version of Jenkins where the fix is not available. For-loop iteration over a literal list might work sometimes but related issues like JENKINS-46749 and JENKINS-46747 的分辨率似乎继续困扰着许多用户。此外,根据 Jenkinsfile 中的确切上下文,可能 echo
会工作而 sh
会失败,并且事情可能会无提示地失败,或者它们可能会因序列化失败而导致构建崩溃。
如果你不喜欢惊喜(跳过循环和静默失败)并且你希望你的 Jenkinsfiles 在多个版本的 Jenkins 中最便携,主要的想法似乎是你应该总是使用经典的for 循环中的计数器并忽略其他 groovy 功能。
This gist 是我见过的最好的参考资料,它阐明了许多您认为应该工作相同但行为却大相径庭的案例。这是建立健全性检查和调试设置的良好起点,无论您正在查看哪种迭代,也无论您是否尝试使用 @NonCPS
,直接在 [=14= 中进行迭代],或调用一个单独的函数。
同样,我不认为这项工作本身值得称赞,但我在下面嵌入了迭代测试用例的要点以供后代使用:
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
这里是 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"
}
}
}
这是我的 Jenkinsfile
的内容:
node {
// prints only the first element 'a'
[ 'a', 'b', 'c' ].each {
echo it
}
}
在 Jenkins 中执行作业时(使用 Pipeline plugin),仅打印列表中的第一项。
谁能给我解释一下这种奇怪的行为?这是一个错误吗?还是只是我不理解 Groovy 语法?
编辑:for (i in items)
按预期工作:
node {
// prints 'a', 'b' and 'c'
for (i in [ 'a', 'b', 'c' ]) {
echo i
}
}
感谢@batmat on #jenkins IRC channel回答这个问题!
这实际上是一个已知错误:JENKINS-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
}
然后像
那样调用函数createScript()
load 'steps.groovy'
此处接受的答案表明这是一个已知错误,并使用了对我不起作用的解决方法,因此我将提供我最近发现的更新。
尽管 JENKINS-26481 (fairly recent, as of this writing) many people may be stuck with an older version of Jenkins where the fix is not available. For-loop iteration over a literal list might work sometimes but related issues like JENKINS-46749 and JENKINS-46747 的分辨率似乎继续困扰着许多用户。此外,根据 Jenkinsfile 中的确切上下文,可能 echo
会工作而 sh
会失败,并且事情可能会无提示地失败,或者它们可能会因序列化失败而导致构建崩溃。
如果你不喜欢惊喜(跳过循环和静默失败)并且你希望你的 Jenkinsfiles 在多个版本的 Jenkins 中最便携,主要的想法似乎是你应该总是使用经典的for 循环中的计数器并忽略其他 groovy 功能。
This gist 是我见过的最好的参考资料,它阐明了许多您认为应该工作相同但行为却大相径庭的案例。这是建立健全性检查和调试设置的良好起点,无论您正在查看哪种迭代,也无论您是否尝试使用 @NonCPS
,直接在 [=14= 中进行迭代],或调用一个单独的函数。
同样,我不认为这项工作本身值得称赞,但我在下面嵌入了迭代测试用例的要点以供后代使用:
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
这里是 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"
}
}
}