使用环境变量 Jenkins Pipeline 加载文件
Load file with environment variables Jenkins Pipeline
我正在做一个简单的管道:
构建 -> 暂存 -> 生产
我需要不同的环境变量来进行暂存和生产,所以我正在尝试 来源 变量。
sh 'source $JENKINS_HOME/.envvars/stacktest-staging.sh'
但是returns没找到
[Stack Test] Running shell script
+ source /var/jenkins_home/.envvars/stacktest-staging.sh
/var/jenkins_home/workspace/Stack Test@tmp/durable-bcbe1515/script.sh: 2: /var/jenkins_home/workspace/Stack Test@tmp/durable-bcbe1515/script.sh: source: not found
路径是正确的,因为我 运行 当我通过 ssh 登录时使用相同的命令,并且它工作正常。
管道思路如下:
node {
stage name: 'Build'
// git and gradle build OK
echo 'My build stage'
stage name: 'Staging'
sh 'source $JENKINS_HOME/.envvars/stacktest-staging.sh' // PROBLEM HERE
echo '$DB_URL' // Expects http://production_url/my_db
sh 'gradle flywayMigrate' // To staging
input message: "Does Staging server look good?"
stage name: 'Production'
sh 'source $JENKINS_HOME/.envvars/stacktest-production.sh'
echo '$DB_URL' // Expects http://production_url/my_db
sh 'gradle flywayMigrate' // To production
sh './deploy.sh'
}
我该怎么办?
- 我正在考虑不使用管道(但我将无法使用我的 Jenkinsfile)。
- 或者使用 EnvInject 插件为舞台和制作做不同的工作(但我失去了我的舞台视图)
- 或者使用 withEnv(但是代码变大了,因为今天我要使用 12 个环境变量)
从文件加载环境变量的一种方法是加载 Groovy 文件。
例如:
- 假设您在“$JENKINS_HOME/.envvars”中有一个名为 'stacktest-staging.groovy'.
的 groovy 文件
在此文件中,您定义了 2 个要加载的环境变量
env.DB_URL="hello"
env.DB_URL2="hello2"
然后您可以使用
加载它
load "$JENKINS_HOME/.envvars/stacktest-staging.groovy"
然后您可以在后续的 echo/shell 步骤中使用它们。
例如,这是一个简短的管道脚本:
node {
load "$JENKINS_HOME/.envvars/stacktest-staging.groovy"
echo "${env.DB_URL}"
echo "${env.DB_URL2}"
}
从评论到
不要使用全局 'env' 而是使用 'withEnv' 构造,例如:
问题 #9:不要在 top 10 best practices jenkins pipeline plugin
中使用全局环境设置环境变量
在下面的例子中:VAR1 是一个普通的 java 字符串(没有 groovy 变量扩展),VAR2 是一个 groovy 字符串(所以变量 'someGroovyVar' 被扩展).
传递的脚本是一个普通的 java 字符串,因此 $VAR1 和 $VAR2 按字面意思传递给 shell,回显正在访问环境变量 VAR1 和 VAR2。
stage('build') {
def someGroovyVar = 'Hello world'
withEnv(['VAR1=VALUE ONE',
"VAR2=${someGroovyVar}"
]) {
def result = sh(script: 'echo $VAR1; echo $VAR2', returnStdout: true)
echo result
}
}
对于机密/密码,您可以使用 credentials binding plugin
示例:
注意:CREDENTIALS_ID1 是在 Jenkins 设置中注册的 username/password 秘密。
stage('Push') {
withCredentials([usernamePassword(
credentialsId: 'CREDENTIALS_ID1',
passwordVariable: 'PASSWORD',
usernameVariable: 'USER')]) {
echo "User name: $USER"
echo "Password: $PASSWORD"
}
}
jenkisn 控制台日志输出隐藏了真实值:
[Pipeline] echo
User name: ****
[Pipeline] echo
Password: ****
Jenkins和credentials是个大问题,大概看:credentials plugin
为了完整性:大多数时候,我们需要环境变量中的秘密,因为我们从 shell 脚本中使用它们,所以我们将 withCredentials 和 withEnv 组合如下:
stage('Push') {
withCredentials([usernamePassword(
credentialsId: 'CREDENTIALS_ID1',
passwordVariable: 'PASSWORD',
usernameVariable: 'USER')]) {
withEnv(["ENV_USERNAME=${USER}",
"ENV_PASSWORD=${PASSWORD}"
]) {
def result = sh(script: 'echo $ENV_USERNAME', returnStdout: true)
echo result
}
}
}
如果您使用的是 Jenkins 2.0,您可以加载 属性 文件(其中包含所有必需的环境变量及其相应的值)并自动读取那里列出的所有环境变量并将其注入 Jenkins提供的环境实体。
这是执行上述操作的方法。
def loadProperties(path) {
properties = new Properties()
File propertiesFile = new File(path)
properties.load(propertiesFile.newDataInputStream())
Set<Object> keys = properties.keySet();
for(Object k:keys){
String key = (String)k;
String value =(String) properties.getProperty(key)
env."${key}" = "${value}"
}
}
要调用此方法,我们需要将 属性 文件的路径作为字符串变量传递 例如,在我们使用 groovy 脚本的 Jenkins 文件中,我们可以调用 like
path = "${workspace}/pic_env_vars.properties"
loadProperties(path)
有什么问题可以问我
解决此问题的另一种方法是安装'Pipeline Utility Steps' 插件,它为我们提供了readProperties 方法(参考请前往link https://jenkins.io/doc/pipeline/steps/pipeline-utility-steps/#pipeline-utility-steps)
在示例中,我们可以看到它们将键存储到数组中并使用键检索值。
但是在那种情况下,如果我们稍后将任何变量添加到 属性 文件中,那么问题就会出现,该变量也需要添加到 Jenkins 文件的数组中。
为了摆脱这种紧密耦合,我们可以这样编写代码,以便 Jenkins 构建环境可以自动获取有关 属性 文件中当前存在的所有现有密钥的信息。这里有一个例子供参考
def loadEnvironmentVariables(path){
def props = readProperties file: path
keys= props.keySet()
for(key in keys) {
value = props["${key}"]
env."${key}" = "${value}"
}
}
客户端代码看起来像
path = '\ABS_Output\EnvVars\pic_env_vars.properties'
loadEnvironmentVariables(path)
使用 声明式 管道,您可以在一行中完成(根据您的值更改 path
):
script {
readProperties(file: path).each {key, value -> env[key] = value }
}
使用 withEnv() 从文件中传递环境变量,该文件被换行拆分并转换为列表:
writeFile file: 'version.txt', text: 'version=6.22.0'
withEnv(readFile('version.txt').split('\n') as List) {
sh "echo ${version}"
}
这里是一个完整的外部环境变量示例,并将它们加载到 Jenkins 管道执行中。流水线写成declarative style.
stage('Reading environment variable defined in groovy file') {
steps {
script {
load "./pipeline/basics/extenvvariable/env.groovy"
echo "${env.env_var1}"
echo "${env.env_var2}"
}
}
}
其中变量是从 groovy 文件中加载的,该文件仅放置了管道代码。
https://github.com/dhruv-bansal/jenkins-pipeline-exploration/blob/master/pipeline/basics/extenvvariable/env.groovy
当您创建可跨团队使用的通用管道时,此模式非常方便。
您可以在这样的 groovy 文件中外部化因变量,每个团队都可以根据他们的生态系统定义他们的值。
另一种解决方案是使用自定义方法,不允许额外的权限,例如 new Properties()
,这会在允许之前导致此错误:
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use new java.util.Properties
或添加额外的插件方法,例如readProperties
。
这里有一个方法可以读取一个名为 env_vars
的简单文件,格式如下:
FOO=bar
FOO2=bar
pipeline {
<... skipped lines ...>
script {
loadEnvironmentVariablesFromFile("env_vars")
echo "show time! ${BAR} ${BAR2}"
}
<... skipped lines ...>
}
private void loadEnvironmentVariablesFromFile(String path) {
def file = readFile(path)
file.split('\n').each { envLine ->
def (key, value) = envLine.tokenize('=')
env."${key}" = "${value}"
}
}
我正在做一个简单的管道:
构建 -> 暂存 -> 生产
我需要不同的环境变量来进行暂存和生产,所以我正在尝试 来源 变量。
sh 'source $JENKINS_HOME/.envvars/stacktest-staging.sh'
但是returns没找到
[Stack Test] Running shell script
+ source /var/jenkins_home/.envvars/stacktest-staging.sh
/var/jenkins_home/workspace/Stack Test@tmp/durable-bcbe1515/script.sh: 2: /var/jenkins_home/workspace/Stack Test@tmp/durable-bcbe1515/script.sh: source: not found
路径是正确的,因为我 运行 当我通过 ssh 登录时使用相同的命令,并且它工作正常。
管道思路如下:
node {
stage name: 'Build'
// git and gradle build OK
echo 'My build stage'
stage name: 'Staging'
sh 'source $JENKINS_HOME/.envvars/stacktest-staging.sh' // PROBLEM HERE
echo '$DB_URL' // Expects http://production_url/my_db
sh 'gradle flywayMigrate' // To staging
input message: "Does Staging server look good?"
stage name: 'Production'
sh 'source $JENKINS_HOME/.envvars/stacktest-production.sh'
echo '$DB_URL' // Expects http://production_url/my_db
sh 'gradle flywayMigrate' // To production
sh './deploy.sh'
}
我该怎么办?
- 我正在考虑不使用管道(但我将无法使用我的 Jenkinsfile)。
- 或者使用 EnvInject 插件为舞台和制作做不同的工作(但我失去了我的舞台视图)
- 或者使用 withEnv(但是代码变大了,因为今天我要使用 12 个环境变量)
从文件加载环境变量的一种方法是加载 Groovy 文件。
例如:
- 假设您在“$JENKINS_HOME/.envvars”中有一个名为 'stacktest-staging.groovy'. 的 groovy 文件
在此文件中,您定义了 2 个要加载的环境变量
env.DB_URL="hello" env.DB_URL2="hello2"
然后您可以使用
加载它load "$JENKINS_HOME/.envvars/stacktest-staging.groovy"
然后您可以在后续的 echo/shell 步骤中使用它们。
例如,这是一个简短的管道脚本:
node {
load "$JENKINS_HOME/.envvars/stacktest-staging.groovy"
echo "${env.DB_URL}"
echo "${env.DB_URL2}"
}
从评论到
不要使用全局 'env' 而是使用 'withEnv' 构造,例如: 问题 #9:不要在 top 10 best practices jenkins pipeline plugin
中使用全局环境设置环境变量在下面的例子中:VAR1 是一个普通的 java 字符串(没有 groovy 变量扩展),VAR2 是一个 groovy 字符串(所以变量 'someGroovyVar' 被扩展).
传递的脚本是一个普通的 java 字符串,因此 $VAR1 和 $VAR2 按字面意思传递给 shell,回显正在访问环境变量 VAR1 和 VAR2。
stage('build') {
def someGroovyVar = 'Hello world'
withEnv(['VAR1=VALUE ONE',
"VAR2=${someGroovyVar}"
]) {
def result = sh(script: 'echo $VAR1; echo $VAR2', returnStdout: true)
echo result
}
}
对于机密/密码,您可以使用 credentials binding plugin
示例:
注意:CREDENTIALS_ID1 是在 Jenkins 设置中注册的 username/password 秘密。
stage('Push') {
withCredentials([usernamePassword(
credentialsId: 'CREDENTIALS_ID1',
passwordVariable: 'PASSWORD',
usernameVariable: 'USER')]) {
echo "User name: $USER"
echo "Password: $PASSWORD"
}
}
jenkisn 控制台日志输出隐藏了真实值:
[Pipeline] echo
User name: ****
[Pipeline] echo
Password: ****
Jenkins和credentials是个大问题,大概看:credentials plugin
为了完整性:大多数时候,我们需要环境变量中的秘密,因为我们从 shell 脚本中使用它们,所以我们将 withCredentials 和 withEnv 组合如下:
stage('Push') {
withCredentials([usernamePassword(
credentialsId: 'CREDENTIALS_ID1',
passwordVariable: 'PASSWORD',
usernameVariable: 'USER')]) {
withEnv(["ENV_USERNAME=${USER}",
"ENV_PASSWORD=${PASSWORD}"
]) {
def result = sh(script: 'echo $ENV_USERNAME', returnStdout: true)
echo result
}
}
}
如果您使用的是 Jenkins 2.0,您可以加载 属性 文件(其中包含所有必需的环境变量及其相应的值)并自动读取那里列出的所有环境变量并将其注入 Jenkins提供的环境实体。
这是执行上述操作的方法。
def loadProperties(path) {
properties = new Properties()
File propertiesFile = new File(path)
properties.load(propertiesFile.newDataInputStream())
Set<Object> keys = properties.keySet();
for(Object k:keys){
String key = (String)k;
String value =(String) properties.getProperty(key)
env."${key}" = "${value}"
}
}
要调用此方法,我们需要将 属性 文件的路径作为字符串变量传递 例如,在我们使用 groovy 脚本的 Jenkins 文件中,我们可以调用 like
path = "${workspace}/pic_env_vars.properties"
loadProperties(path)
有什么问题可以问我
解决此问题的另一种方法是安装'Pipeline Utility Steps' 插件,它为我们提供了readProperties 方法(参考请前往link https://jenkins.io/doc/pipeline/steps/pipeline-utility-steps/#pipeline-utility-steps) 在示例中,我们可以看到它们将键存储到数组中并使用键检索值。 但是在那种情况下,如果我们稍后将任何变量添加到 属性 文件中,那么问题就会出现,该变量也需要添加到 Jenkins 文件的数组中。 为了摆脱这种紧密耦合,我们可以这样编写代码,以便 Jenkins 构建环境可以自动获取有关 属性 文件中当前存在的所有现有密钥的信息。这里有一个例子供参考
def loadEnvironmentVariables(path){
def props = readProperties file: path
keys= props.keySet()
for(key in keys) {
value = props["${key}"]
env."${key}" = "${value}"
}
}
客户端代码看起来像
path = '\ABS_Output\EnvVars\pic_env_vars.properties'
loadEnvironmentVariables(path)
使用 声明式 管道,您可以在一行中完成(根据您的值更改 path
):
script {
readProperties(file: path).each {key, value -> env[key] = value }
}
使用 withEnv() 从文件中传递环境变量,该文件被换行拆分并转换为列表:
writeFile file: 'version.txt', text: 'version=6.22.0'
withEnv(readFile('version.txt').split('\n') as List) {
sh "echo ${version}"
}
这里是一个完整的外部环境变量示例,并将它们加载到 Jenkins 管道执行中。流水线写成declarative style.
stage('Reading environment variable defined in groovy file') {
steps {
script {
load "./pipeline/basics/extenvvariable/env.groovy"
echo "${env.env_var1}"
echo "${env.env_var2}"
}
}
}
其中变量是从 groovy 文件中加载的,该文件仅放置了管道代码。 https://github.com/dhruv-bansal/jenkins-pipeline-exploration/blob/master/pipeline/basics/extenvvariable/env.groovy
当您创建可跨团队使用的通用管道时,此模式非常方便。 您可以在这样的 groovy 文件中外部化因变量,每个团队都可以根据他们的生态系统定义他们的值。
另一种解决方案是使用自定义方法,不允许额外的权限,例如 new Properties()
,这会在允许之前导致此错误:
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use new java.util.Properties
或添加额外的插件方法,例如readProperties
。
这里有一个方法可以读取一个名为 env_vars
的简单文件,格式如下:
FOO=bar
FOO2=bar
pipeline {
<... skipped lines ...>
script {
loadEnvironmentVariablesFromFile("env_vars")
echo "show time! ${BAR} ${BAR2}"
}
<... skipped lines ...>
}
private void loadEnvironmentVariablesFromFile(String path) {
def file = readFile(path)
file.split('\n').each { envLine ->
def (key, value) = envLine.tokenize('=')
env."${key}" = "${value}"
}
}