使用环境变量Jenkins Pipeline加载文件

Raf*_*ael 22 environment-variables jenkins jenkins-pipeline

我正在做一个简单的管道:

构建 - >分期 - >生产

我需要分期和生产不同的环境变量,所以我试图变量.

sh 'source $JENKINS_HOME/.envvars/stacktest-staging.sh' 
Run Code Online (Sandbox Code Playgroud)

但它返回Not found

[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
Run Code Online (Sandbox Code Playgroud)

路径是正确的,因为我在通过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'
}
Run Code Online (Sandbox Code Playgroud)

我该怎么办?

  • 我在考虑不使用管道(但我无法使用我的Jenkins文件).
  • 或者使用EnvInject插件为暂存和制作制作不同的作业(但我丢失了我的舞台视图)
  • 或使用Env(但代码变大,因为今天我正在使用12个env变量)

Dan*_*oto 38

从文件加载环境变量的一种方法是加载Groovy文件.

例如:

  1. 假设你在'$ JENKINS_HOME/.envvars'中有一个名为'stacktest-staging.groovy'的groovy文件.
  2. 在此文件中,您可以定义要加载的2个环境变量

    env.DB_URL="hello"
    env.DB_URL2="hello2"
    
    Run Code Online (Sandbox Code Playgroud)
  3. 然后,您可以在使用中加载它

    load "$JENKINS_HOME/.envvars/stacktest-staging.groovy"
    
    Run Code Online (Sandbox Code Playgroud)
  4. 然后,您可以在后续的echo/shell步骤中使用它们.

例如,这是一个简短的管道脚本:

node {
   load "$JENKINS_HOME/.envvars/stacktest-staging.groovy"
   echo "${env.DB_URL}"
   echo "${env.DB_URL2}"
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,不鼓励使用 env 变量,因为 env 变量是全局的。推荐的做法是加载局部变量,然后最终在管道的一部分中使用`withEnv`构造将它们设置为环境变量。 (5认同)

huc*_*uch 20

从评论到接受的答案

不要使用全局'env',而是使用'withEnv'构造,例如参见:issue#9:不要在全球env中设置env vars,在前10个最佳实践中使用jenkins管道插件

在下面的示例中:VAR1是一个普通的java字符串(没有groovy变量扩展),VAR2是一个groovy字符串(所以变量'someGroovyVar'被展开).

传递的脚本是一个普通的java字符串,因此$ VAR1和$ VAR2按字面传递给shell,而echo正在访问环境变量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
    }
}
Run Code Online (Sandbox Code Playgroud)

对于秘密/密码,您可以使用凭证绑定插件

例:

注意:CREDENTIALS_ID1是Jenkins设置上注册的用户名/密码密码.

stage('Push') {
    withCredentials([usernamePassword(
                         credentialsId: 'CREDENTIALS_ID1',
                         passwordVariable: 'PASSWORD',
                         usernameVariable: 'USER')]) {
        echo "User name: $USER"
        echo "Password:  $PASSWORD"
    }
}
Run Code Online (Sandbox Code Playgroud)

jenkisn控制台日志输出隐藏了实际值:

[Pipeline] echo
User name: ****
[Pipeline] echo
Password:  ****
Run Code Online (Sandbox Code Playgroud)

Jenkins和凭据是一个大问题,可能会看到:凭据插件

为了完整性:大多数时候,我们需要环境变量中的秘密,因为我们在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

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 当然,这是可能的。但是`withEnv` 仅在步骤中是局部的,而 env var 是全局的。(这就是问题 #9:不要使用全局 env 设置 env vars)。 (2认同)

Abd*_*UMI 7

使用声明式管道,您可以在一行中完成(path根据您的值更改):

script {
  readProperties(file: path).each {key, value -> env[key] = value }
}
Run Code Online (Sandbox Code Playgroud)


iEf*_*off 6

使用 withEnv() 从由新行拆分的文件中传递环境变量并转换为 List:

writeFile file: 'version.txt', text: 'version=6.22.0'
withEnv(readFile('version.txt').split('\n') as List) {
    sh "echo ${version}"
}
Run Code Online (Sandbox Code Playgroud)

  • 这对我来说是一个优雅的解决方案,很好又简单,谢谢! (2认同)

小智 5

如果您使用的是 Jenkins 2.0,您可以加载属性文件(其中包含所有必需的环境变量及其相应的值)并自动读取其中列出的所有环境变量,并将其注入到 Jenkins 提供的 env 实体中。

这是执行上述操作的方法。

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}"
    }
}
Run Code Online (Sandbox Code Playgroud)

要调用此方法,我们需要将属性文件的路径作为字符串变量传递,例如,在使用 groovy 脚本的 Jenkins 文件中,我们可以这样调用

path = "${workspace}/pic_env_vars.properties"
loadProperties(path)
Run Code Online (Sandbox Code Playgroud)

如果您有任何疑问请询问我


小智 5

解决此安装“ Pipeline Utility Steps”插件的另一种方法,该插件为我们提供了readProperties方法(作为参考,请转到链接https://jenkins.io/doc/pipeline/steps/pipeline-utility-steps/#pipeline-utility-步骤)在这里,在示例中,我们可以看到,它们被存储的密钥到一个数组并使用键来检索该值。但是在那种情况下,生产中的问题就像是我们稍后在属性文件中添加任何变量一样,该变量也需要添加到Jenkins文件数组中。为了摆脱这种紧密的耦合,我们可以以某种方式编写代码,以便Jenkins构建环境可以自动获取有关Property文件中当前存在的所有现有键的信息。这是供参考的示例

def loadEnvironmentVariables(path){
    def props = readProperties  file: path
    keys= props.keySet()
    for(key in keys) {
        value = props["${key}"]
        env."${key}" = "${value}"
    }
} 
Run Code Online (Sandbox Code Playgroud)

客户端代码看起来像

path = '\\ABS_Output\\EnvVars\\pic_env_vars.properties'
loadEnvironmentVariables(path)
Run Code Online (Sandbox Code Playgroud)