web-dev-qa-db-fra.com

Comportement d'étendue variable étrange dans Jenkinsfile

Lorsque j'exécute le script de pipeline Jenkins ci-dessous:

def some_var = "some value"

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

pipeline {
    agent any

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

Je reçois cette erreur:

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

Si def est supprimé de some_var, ça fonctionne bien. Quelqu'un pourrait-il expliquer les règles de portée qui provoquent ce comportement?

10
haridsv

TL; DR

  • les variables définies avec def dans le corps du script principal ne sont pas accessibles à partir d'autres méthodes.
  • les variables définies sans def sont accessibles directement par n'importe quelle méthode même à partir de différents scripts. C'est une mauvaise pratique.
  • variables définies avec def et @Field l'annotation est accessible directement à partir des méthodes définies dans le même script.

Explication

Lorsque groovy compile ce script, il déplace tout dans une classe qui à peu près ressemble à ceci

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()
                    }
                }
            }
        }
    }
}

Vous pouvez voir que some_var Est clairement hors de portée pour pr() parce que c'est une variable locale dans une méthode différente.

Lorsque vous définissez une variable sans def vous placez cette variable dans un Binding du script (donc -called variables de liaison). Donc, quand groovy exécute la méthode pr() tout d'abord, il essaie de trouver une variable locale avec un nom some_var Et s'il n'existe pas, il essaie ensuite de trouver cette variable dans une liaison (qui existe parce que vous l'avez défini sans def).

Les variables de liaison sont considérées comme une mauvaise pratique car si vous chargez plusieurs scripts (load étape), les variables de liaison seront accessibles dans tous ces scripts car Jenkins partage la même liaison pour tous les scripts. Une bien meilleure alternative consiste à utiliser @Field annotation. De cette façon, vous pouvez rendre une variable accessible dans toutes les méthodes à l'intérieur d'un script sans l'exposer à d'autres scripts.

import groovy.transform.Field

@Field 
def some_var = "some value"

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

Quand groovy compile ce script dans une classe, il ressemblera à ceci

class Script1 {
    def some_var = "some value"

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