web-dev-qa-db-ja.com

Jenkinsfileの各ループが最初の反復で停止する理由

これが私のJenkinsfileの内容です:

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

Jenkinsでジョブを実行する場合( Pipelineプラグイン を使用)、リストの最初のアイテムのみが印刷されます。

誰かがこの奇妙な行動を説明できますか?バグですか?それともGroovyの構文を理解していないだけですか?

Editfor (i in items)は期待どおりに動作します:

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

ここで受け入れられた答えは、それが既知のバグであり、私にとってはうまくいかなかった回避策を使用していることを示しているので、最近見つけたものでアップデートを提供します。

JENKINS-26481 (この記事の執筆時点ではかなり最近)の解決にもかかわらず、多くの人々は、修正が利用できない古いバージョンのJenkinsに固執しているかもしれません。リテラルリストに対するforループの反復は時々機能する場合がありますが、 JENKINS-46749JENKINS-46747 などの関連する問題が多くのユーザーを悩ませ続けているようです。また、Jenkinsfileの正確なコンテキストに応じて、おそらくechoが機能するのに対してshが失敗し、サイレントに失敗したり、シリアル化に失敗してビルドがクラッシュしたりする可能性があります。

サプライズ(スキップされたループと無音の失敗)が嫌いで、JenkinsfilesをJenkinsの複数のバージョン間で最もポータブルにしたい場合、主なアイデアは常に古典的なカウンターを使用することですforループで、他のグルーヴィーな機能を無視します。

この要点 は、私が見た中で最高のリファレンスであり、同じように動作するはずであるが驚くほど異なる動作を持つと思われる多くのケースを説明しています。どんな種類のイテレーションを見て、@NonCPSを使用しようとしているか、node{}内で直接イテレーションを実行するか、別の関数を呼び出すかに関係なく、健全性チェックを確立してセットアップをデバッグするのに適した出発点です。

繰り返しになりますが、私はその作業自体を信用していませんが、後世のためにGist of iterationテストケースを以下に埋め込みます。

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

@ batmat on #jenkins IRC channel この質問に答えてくれてありがとう!

それは実際には既知のバグです: JENKINS-26481

5
Eric Citaire

この問題の回避策は、すべてのコマンドを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'
2
Shengyue