web-dev-qa-db-ja.com

Jenkinsfileの奇妙な変数スコープ動作

以下のJenkinsパイプラインスクリプトを実行すると:

def some_var = "some value"

def pr() {
    def another_var = "another " + some_var
    echo "${another_var}"
}

pipeline {
    agent any

    stages {
        stage ("Run") {
            steps {
                pr()
            }
        }
    }
}

私はこのエラーを受け取ります:

groovy.lang.MissingPropertyException: No such property: some_var for class: groovy.lang.Binding

defsome_varから削除されている場合、正常に機能します。誰かがこの動作を引き起こすスコープ規則について説明できますか?

10
haridsv

TL; DR

  • 定義された変数withdefは、メインスクリプト本体で他のメソッドからアクセスできません。
  • 定義された変数withoutdefは、異なるスクリプトからでも任意の方法で直接アクセスできます。それは悪い習慣です。
  • 定義された変数withdefand_@Field_ 注釈は、同じスクリプトで定義されたメソッドから直接アクセスできます。

説明

Groovyがそのスクリプトをコンパイルすると、実際にはroughlyのようなクラスにすべてを移動します

_class Script1 {
    def pr() {
        def another_var = "another " + some_var
        echo "${another_var}"
    }
    def run() {
        def some_var = "some value"
        pipeline {
            agent any
            stages {
                stage ("Run") {
                    steps {
                        pr()
                    }
                }
            }
        }
    }
}
_

_some_var_は、別のメソッドのローカル変数であるため、明らかにpr()の範囲外です。

変数を定義するときwithoutdefは、実際にその変数をスクリプトの Binding に入れます(so -calledbinding variables)。したがって、groovyはpr()メソッドを最初に実行するときに、_some_var_という名前のローカル変数を見つけようとし、存在しない場合は、Binding(そのために存在するため) defなしで定義した)。

Jenkinsはすべてのスクリプトで同じバインディングを共有するため、複数のスクリプト(loadステップ)をロードすると、これらのすべてのスクリプトでバインディング変数にアクセスできるため、バインディング変数は悪い習慣と見なされます。より良い代替手段は _@Field_ アノテーションを使用することです。これにより、1つのスクリプト内のすべてのメソッドで変数にアクセスできるようになり、他のスクリプトに公開されることはありません。

_import groovy.transform.Field

@Field 
def some_var = "some value"

def pr() {
    def another_var = "another " + some_var
    echo "${another_var}"
}
//your pipeline
_

Groovyがこのスクリプトをクラスにコンパイルすると、次のようになります。

_class Script1 {
    def some_var = "some value"

    def pr() {
        def another_var = "another " + some_var
        echo "${another_var}"
    }
    def run() {
        //your pipeline
    }
}
_
33