如何在Jenkins Groovy脚本控制台中获取环境变量?

18
在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")
4个回答

19

1
我想知道是否有可能读取“掩码密码 - 全局名称/密码对”(在http://JenkinsURL/configure中定义)的内容。 - matandked
4
java.lang.NullPointerException: Cannot invoke method getEnvVars() on null object Java空指针异常:无法在空对象上调用getEnvVars()方法 - OrangeDog
@OrangeDog 使用 Jenkins.get().getGlobalNodeProperties()[0].getEnvVars() 代替 Jenkins.instance.getGlobalNodeProperties()[0].getEnvVars() - Lunero
1
我已经添加了一个新的答案,大大扩展了这个主题。你提供的代码很脆弱(它假设环境属性已定义,并且在索引0处),并且它只能找到在全局jenkins属性配置中定义的env-vars子集。这段代码不会找到设置为节点系统属性或操作系统env-vars的env-vars,也不会找到在jenkins中的节点属性配置中设置的env-vars。请参见我在下面答案中提供的详细信息。 - Craig Ringer

14

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

最终方案

这是一个封装好的版本,您可以直接使用,或者稍微调整一下以打包到您的pipeline全局库的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 配置中“环境变量”列表中的 env 变量 - 它不会显示系统环境变量,也不会显示特定于节点的环境变量。

继续阅读。

如果可能,请保持简单

如果您使用 Groovy Pipeline,则大多数情况下只需使用 env “变量”(请参见管道帮助中的“全局变量参考”),它公开了统一的环境作为属性。如上所述,这无法直接从脚本控制台工作,但其余时间这是适当的方式。

您还可以在 Pipeline 脚本中使用 env.getEnvironment() 获取统一的 EnvVars 实例,可用于字符串中的 env-vars 占位符替换,例如 env.getEnvironment().expand('${FOO} $BAR')。(您需要脚本安全权限才能执行此操作,但最好将其放入全局库的 helper 中的 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” -> “管理节点和云” -> [nodename] -> 配置 -> 环境变量下的node1节点设置中配置了NODE1_SPECIFIC_ENVVAR

在同一位置的主节点条目中,我配置了MASTER_SPECIFIC_ENVVAR

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

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

环境的不同视图

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

JVM级别的环境变量(主节点)

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

fmtEnv('System.getenv()', ['': System.getenv()])

/* 
master:
    [HOME, JENKINS_HOME, PATH, SHELL, USER]
*/

在Jenkins中,没有针对每个节点或每个任务的配置。

节点的基本环境

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

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中没有设置全局或节点特定的环境变量。

全局配置的环境变量

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]
*/

在“管理节点”中,我们可以看到每个节点下配置的属性,但没有主机环境变量、系统属性变量、标准Jenkins作业变量或在Jenkins全局配置中配置的变量。
重要提示:如果节点上没有配置自定义环境变量,则`getNodeProperty(EnvironmentVariablesNodeProperty)`返回null,因此您必须处理它。
将其放在一起:上面显示了如何获取有意义的脚本控制台上的环境变量的主要来源的EnvVars实例。
运行作业时还有其他来源,我不考虑这些,例如作业属性(EnvInject插件)、自动添加到所有作业的env-vars、withEnvironment步骤、SCM插件注入的变量等。但是对于脚本控制台任务,它们没有意义。
那么我们如何获得统一的环境呢?
首先,收集每个相关环境的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的示例。)


3
这篇文章非常有价值且写得很好。感谢你所付出的努力。 :) - myahl

4
您可以使用System来获取环境变量。
def env = System.getenv()
println(env['JENKINS_HOME'])

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


1
这不会暴露在Jenkins本身中定义的环境变量,例如全局配置中的变量。 - Craig Ringer

1
由于“安全”问题,这些答案都对我没有用。相反,只需使用环境变量的名称,例如环境变量PATH,您可以使用:
final path = PATH

你可以这样使用:
final path = env.PATH

惊讶的是它如此简单,但确实是这样...


1
在后构建步骤中使用 env 返回了 groovy.lang.MissingPropertyException: No such property: env for class: Script1 - Noam Manos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接