这并不像你想象中的那么简单,就像Jenkins中的所有内容一样。它似乎没有暴露一个简单的API来获取当前执行上下文的最终有效环境,至少在脚本控制台中没有。
最终方案
这是一个封装好的版本,您可以直接使用,或者稍微调整一下以打包到您的pipeline全局库的vars/
类中。
import jenkins.model.Jenkins
import hudson.model.Node
import hudson.slaves.EnvironmentVariablesNodeProperty
import hudson.EnvVars
EnvVars getCombinedNodeEnvironment(Node node) {
def combined = new EnvVars(node.toComputer().getEnvironment())
combined.overrideExpandingAll(Jenkins.instance.
getGlobalNodeProperties().
get(EnvironmentVariablesNodeProperty).
getEnvVars() ?: new EnvVars())
combined.overrideExpandingAll((node.
getNodeProperty(EnvironmentVariablesNodeProperty)?.
getEnvVars()) ?: new EnvVars())
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/
中)。
大多数情况下这已足够。
我只是深入了解环境结构的细节,因为我需要展开包含环境变量的字符串,就像它们在不同节点上展开一样。这不是常见用例。
如何工作的说明和示例设置
这是最终的配方,但我们是如何得到它的,不同的环境变量集从哪里来,以及为什么?
对于以下代码示例,请假设这个常见的序言,主要是为了避免在每个示例中重复。
nodenames = ['', 'some-other-node-name']
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()
})
请注意,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()
})
在“管理节点”中,我们可以看到每个节点下配置的属性,但没有主机环境变量、系统属性变量、标准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
的示例。)
Jenkins.get().getGlobalNodeProperties()[0].getEnvVars()
代替Jenkins.instance.getGlobalNodeProperties()[0].getEnvVars()
。 - Lunero