Странное поведение переменной области видимости в Jenkinsfile - PullRequest
0 голосов
/ 28 мая 2018

Когда я запускаю приведенный ниже сценарий конвейера 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

Если def удален из some_var, он работает нормально,Может ли кто-нибудь объяснить правила определения содержания, которые вызывают такое поведение?

1 Ответ

0 голосов
/ 28 мая 2018

TL; DR

  • переменные, определенные с def в основном теле скрипта, недоступны из других методов.
  • переменные определены без def можно получить доступ любым способом, даже из разных скриптов.Это плохая практика.
  • переменные, определенные с def и @Field , могут быть доступны напрямую из методов, определенных в той же самойscript.

Объяснение

Когда groovy компилирует этот скрипт, он фактически перемещает все в класс, который примерно выглядит примерно так

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(), потому что это локальная переменная в другом методе.

Когда вы определяете переменную без def, вы на самом делепоместите эту переменную в Binding скрипта (так называемые Binding переменные ).Поэтому, когда groovy выполняет метод pr(), сначала он пытается найти локальную переменную с именем some_var, а если она не существует, он пытается найти эту переменную в привязке (которая существует, потому что вы определили ее без def).

Переменные привязки считаются плохой практикой, поскольку при загрузке нескольких сценариев (шаг load) переменные привязки будут доступны во всех этих сценариях, поскольку Jenkins использует одну и ту же привязку для всех сценариев.Гораздо лучшая альтернатива - использовать аннотацию @Field .Таким образом, вы можете сделать переменную доступной во всех методах внутри одного скрипта, не подвергая ее другим скриптам.

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
    }
}
...