【问题标题】:How to get environment variable in Jenkins Groovy script console?如何在 Jenkins Groovy 脚本控制台中获取环境变量?
【发布时间】:2017-03-06 01:15:17
【问题描述】:

在“全局属性”中的 Jenkins 配置 (http://JenkinsURL/configure) 中,我定义了一些“环境变量”。

如何在 Groovy 脚本控制台 (http://JenkinsURL/script) 中访问它们?

我试图找到合适的解决方案(例如:Access to build environment variables from a groovy script in a Jenkins build step (Windows) 中提到的解决方案) 但似乎它们都不适合我。

我试过例如:

System.getenv("myVar")

manager.build.getEnvironment(listener).get('myVar') //no manager error

import jenkins.model.Jenkins
Jenkins.instance.getProperty('myVar') //No signature of method: hudson.model.Hudson.getProperty() is applicable for argument types: (java.lang.String)

import jenkins.model.Jenkins
Jenkins.instance.ParameterValue("DEV_local")

【问题讨论】:

    标签: jenkins groovy


    【解决方案1】:

    这并不像您想象的那么简单,就像 Jenkins 中的所有内容一样。它似乎没有公开一个简单的 API 来获得当前执行上下文的最终有效环境,至少不会向脚本控制台公开。

    最终配方

    这是您可以直接使用的打包版本,或者您可以稍作调整以捆绑到管道全局库中的 vars/ 类中。

    import jenkins.model.Jenkins
    import hudson.model.Node
    import hudson.slaves.EnvironmentVariablesNodeProperty
    import hudson.EnvVars
    
    EnvVars getCombinedNodeEnvironment(Node node) {
    
      /*
       * Start with env-vars defined by the shell the JVM
       * was started from and env-vars set as system properties.
       */
      def combined = new EnvVars(node.toComputer().getEnvironment())
    
      /*
       * Apply environment variables from jenkins global settings
       * ("Manage Jenkins" -> "Configure System" -> "Global Properties"
       *   -> "Environment Variables")
       */
      combined.overrideExpandingAll(Jenkins.instance.
           getGlobalNodeProperties().
           get(EnvironmentVariablesNodeProperty).
           getEnvVars() ?: new EnvVars())
    
      /*
       * Apply environment variables from node specific settings
       * ("Manage Jenkins" -> "Manage Nodes and Clouds"
       *     -> {nodename} -> "Configure" -> "Node Properties"
       *     -> "Environment Variables") 
       */
      combined.overrideExpandingAll((node.
           getNodeProperty(EnvironmentVariablesNodeProperty)?.
           getEnvVars()) ?: new EnvVars())
    
      /*
       * Important: This environment map will NOT contain job-level,
       * or run-level properties, nor anything set via build steps etc.
       */
      return combined
    }
    
    EnvVars getCombinedNodeEnvironment(String nodename) {
      if (nodename == 'master' || !nodename)
        return getCombinedNodeEnvironment(Jenkins.instance)
      else
        return getCombinedNodeEnvironment(Jenkins.instance.getNode(nodename))
    }
    

    用法:

    getCombinedNodeEnvironment('somenode').expand('$JENKINS_HOME/$USER/$SOME_NODE_VARIABLE')
    
    getCombinedNodeEnvironment('').SOME_ENV_VAR_ON_MASTER
    

    相关类:

    现有答案的问题

    arasio 的回答是一个好的开始,但假设 envvars 属性将位于全局属性的索引 0 处是不正确的。该方法还会忽略在特定节点上本地设置的环境变量。

    至少应该是这样的

    jenkins.instance.Jenkins.instance.
       getGlobalNodeProperties().
       get(hudson.slaves.EnvironmentVariablesNodeProperty).
       getEnvVars() 
    

    即在DescribableList 结果中按类查找属性,而不是假设索引。

    但是,只能从全局 jenkins 配置中的“环境变量”列表中获取环境变量 - 它不会显示系统环境变量,也不会显示特定于节点的环境变量。

    继续阅读。

    尽可能保持简单

    如果您使用的是 Groovy 管道,大多数时候您可以使用 env "variable"(请参阅管道帮助中的“全局变量参考”),它将统一环境作为属性公开。如上所述,这不能直接从脚本控制台工作,但在其余时间它是做事的适当方式。

    您还可以在 Pipeline 脚本中使用 env.getEnvironment() 来获得统一的 EnvVars 实例,用于替换字符串中的 env-vars 的占位符,例如env.getEnvironment().expand('${FOO} $BAR')。 (为此,您需要脚本安全权限,但最好将其放在全局库的 vars/ 的帮助程序中)。

    大多数时候这已经足够了。

    我只深入研究了环境结构的细节,因为我需要扩展包含环境变量的字符串因为它们将在不同的节点上扩展。这不是一个常见的用例。

    解释其工作原理和设置示例

    这是最后的秘诀,但我们是如何做到的,不同的环境变量集从何而来,为什么?

    对于以下代码示例,假设这个共同的前奏,​​主要是为了节省每个示例中的重复。

    /* Jenkins uses '' for the master node */
    nodenames = ['', 'some-other-node-name']
    
    /* Imports used in various examples */
    import jenkins.model.Jenkins
    import hudson.slaves.EnvironmentVariablesNodeProperty
    import hudson.EnvVars
    
    nodes = nodenames.collect { nodename ->
     (!nodename || nodename == 'master') ?
         Jenkins.instance : Jenkins.instance.getNode(nodename)
    
    import static groovy.json.JsonOutput.toJson
    import static groovy.json.JsonOutput.prettyPrint
    
    def eachNode(Closure c) {
      nodes.collectEntries { node -> [node.nodeName, c(node, node.nodeName) ] }
    
    
    def fmtEnv(desc,m) {
      print "\n\n${desc}\n----\n" + m.collect { k, v -> "${k?:'master'}:\n\t${trimKeys(v)}" }.join('\n')
    }
    
    def trimKeys(l) {
      if (l == null)
        return l
      if (l in Map)
        l = l.keySet()
      l = l - ['_', 'OLDPWD', 'PWD', 'SSH_CLIENT', 'JAVA_HOME', 'LANG', 'LOGNAME', 'MAIL', 'MANPATH', 'S_COLORS', 'SHLVL', 'XDG_RUNTIME_DIR', 'XDG_SESSION_ID']
      l.sort()
    }
    

    nodes 现在包含 jenkins.model.Jenkins 主节点和 hudson.model.Node 工作节点。

    eachNode 生成节点名称到环境变量键的缩写列表的映射,只是为了使示例更简洁和更易于阅读。不要在你的代码中使用它。

    为了帮助阐明这些示例的结果,我在“管理 Jenkins”->“管理节点和云”-> [节点名]-> 配置-> 环境变量下的 node1 的节点设置中配置了NODE1_SPECIFIC_ENVVAR

    在同一个地方的master节点入口,我已经配置了MASTER_SPECIFIC_ENVVAR

    在“管理 Jenkins”->“配置系统”->“全局属性”->“环境变量”中,我添加了“ALL_NODES_ENVVAR”。

    我没有费心为节点和主节点在 JVM 级别设置自定义环境变量。

    对环境的不同看法

    现在,让我们以不同的方式探索环境。

    JVM 级环境变量(主)

    在 master 上,System.getenv() 仅显示 JVM 启动时设置的环境变量或作为系统属性:

    fmtEnv('System.getenv()', ['': System.getenv()])
    
    /* 
    master:
        [HOME, JENKINS_HOME, PATH, SHELL, USER]
    */
    

    所以没有配置每个节点,全局在詹金斯本身,或每个作业。

    节点的基础环境

    Jenkins 在其 API 中公开了在每个节点上设置的基本环境变量。我认为这与System.getEnv() 在目标节点 JVM 上执行时返回的结果相同:

    fmtEnv('toComputer.getEnvironment()', eachNode() {
      node, name -> node.toComputer().getEnvironment()
    })
    
    /*
    master:
        [HOME, JENKINS_HOME, PATH, SHELL, USER]
    ci-node-qa-fsn1-01:
        [HOME, PATH, SHELL, SSH_CONNECTION, USER]
    */
    

    请注意 Jenkins 中没有设置全局或特定于节点的 env-vars。

    全局配置的环境变量

    fmtEnv('master getGlobalNodeProperties', ['': 
      Jenkins.instance.
         getGlobalNodeProperties().
         get(EnvironmentVariablesNodeProperty).
         getEnvVars()
    ])
    
    /*
    master getGlobalNodeProperties
    ----
    master:
        [ALL_NODES_ENVVAR]
    */
    

    所以这里我们只看到全局配置的环境属性,而不是特定于节点的属性、系统属性或主机环境变量。

    节点特定的环境变量覆盖

    fmtEnv('node getNodeProperty', eachNode() {
      node, name -> node.getNodeProperty(EnvironmentVariablesNodeProperty)?.getEnvVars()
    })
    
    /*
    master:
        [MASTER_SPECIFIC_ENVVAR]
    ci-node-qa-fsn1-01:
        [NODE1_SPECIFIC_ENVVAR]
    */
    

    这里我们看到“管理节点”中每个节点下配置的属性,但没有主机 env-vars、来自系统属性的 vars、标准 jenkins 作业 vars 或 jenkins 全局配置中配置的 vars。

    重要提示:如果节点上没有配置自定义环境变量,getNodeProperty(EnvironmentVariablesNodeProperty) 将返回 null,因此您必须处理它。

    把它放在一起

    上面展示了如何获取EnvVars 实例,用于在脚本控制台上有意义的环境变量的主要来源。

    运行作业时还有其他来源,我在这里不考虑,例如作业属性(EnvInject 插件)、添加到所有作业的自动 env-vars、withEnvironment 步骤、由 SCM 插件注入的变量(s ) 等。但它们对脚本控制台任务没有意义。

    那么我们如何获得一个统一的环境呢?

    首先,为每个相关的环境部分收集EnvVars

    def node_base_env = node.toComputer().getEnvironment()
    
    def global_env_properties = Jenkins.instance.
         getGlobalNodeProperties().
         get(EnvironmentVariablesNodeProperty).
         getEnvVars()
    
    def node_env_properties = node.getNodeProperty(EnvironmentVariablesNodeProperty)?.getEnvVars() ?: new EnvVars()
    
    def merged = new EnvVars(node_base_env)
    merged.overrideExpandingAll(global_env_properties)
    merged.overrideExpandingAll(node_env_properties)
    merged
    
    /*
    master:
        [ALL_NODES_ENVVAR, HOME, JENKINS_HOME, MASTER_SPECIFIC_ENVVAR, PATH, SHELL, USER]
    ci-node-qa-fsn1-01:
        [ALL_NODES_ENVVAR, HOME, NODE1_SPECIFIC_ENVVAR, PATH, SHELL, SSH_CONNECTION, USER]
     */
    

    我很确定这会产生正确的结果。扩展处理、优先级覆盖顺序或扩展顺序我没有详细测试过。

    (注意:我删除了另一个使用 EnvironmentExpander 的示例)。

    【讨论】:

    • 这是令人难以置信的信息和写得很好。感谢您为此付出的努力。 :)
    【解决方案2】:

    由于“安全”问题,这些答案都不适合我。 相反,只需使用环境变量的名称,例如可以使用的环境变量 PATH:

    final path = PATH
    

    你也可以使用

    final path = env.PATH
    

    很惊讶它是如此简单,但它是......

    【讨论】:

    • 在构建后步骤中使用env 返回groovy.lang.MissingPropertyException: No such property: env for class: Script1
    【解决方案3】:

    您可以使用System 获取环境变量。

    def env = System.getenv()
    println(env['JENKINS_HOME'])
    

    (另请参阅http://@myJenkHostname@/env-vars.html(其中@myJenkHostname@ 是您的 Jenkins 主机名)以获取内置环境变量的列表。)

    【讨论】:

    • 这不会暴露在 Jenkins 本身中定义的 env-vars,例如在全局配置中。
    【解决方案4】:

    你可以像这样获取全局属性:

    import jenkins.model.Jenkins
    def envVars = Jenkins.instance.getGlobalNodeProperties()[0].getEnvVars() 
    println envVars['myVar']
    

    我参考了下面的链接,关于如何以编程方式设置 全局属性。 https://groups.google.com/forum/#!topic/jenkinsci-users/KgCGuDmED1Q

    【讨论】:

    • 我想知道是否也可以阅读“掩码密码 - 全局名称/密码对”(在 JenkinsURL/configure 中定义)。
    • java.lang.NullPointerException:无法在空对象上调用方法 getEnvVars()
    • @OrangeDog 使用Jenkins.get().getGlobalNodeProperties()[0].getEnvVars() 而不是Jenkins.instance.getGlobalNodeProperties()[0].getEnvVars()
    • 我添加了一个新的答案,大大扩展了这个主题。您在此处提供的代码很脆弱(它假定环境属性已定义,并且位于索引 0),并且它仅找到在全局 jenkins 属性配置中定义的 env-vars 子集。此代码不会找到设置为节点系统属性或操作系统 env-vars 的 env-vars,也不会找到 jenkins 的节点属性配置中设置的 env-vars。请参阅我在下面的答案中提供的详细信息。
    猜你喜欢
    • 2019-10-21
    • 2020-05-03
    • 1970-01-01
    • 2018-04-16
    • 1970-01-01
    • 1970-01-01
    • 2020-09-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多