首页 > 解决方案 > 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

如果从def中删除some_var,它可以正常工作。有人可以解释导致这种行为的范围规则吗?

标签: jenkinsgroovyjenkins-pipelinejenkins-groovygroovyshell

解决方案


TL;博士

  • 不能从其他方法访问主脚本主体中定义 变量。def
  • 没有 定义的变量def可以通过任何方法直接访问,甚至可以从不同的脚本中访问。这是一个不好的做法。
  • 使用 def 注释定义的变量@Field 可以直接从同一脚本中定义的方法访问。

解释

当 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将该变量放入脚本的绑定中(所谓的绑定变量)。因此,当 groovypr()首先执行方法时,它会尝试查找具有名称的局部变量,some_var如果它不存在,则尝试在 Binding 中查找该变量(该变量存在是因为您没有定义它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
    }
}

推荐阅读