Rafael
Rafael

Reputation: 1885

Load file with environment variables Jenkins Pipeline

I am doing a simple pipeline:

Build -> Staging -> Production

I need different environment variables for staging and production, so i am trying to source variables.

sh 'source $JENKINS_HOME/.envvars/stacktest-staging.sh' 

But it returns 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

The path is right, because i run the same command when i log via ssh, and it works fine.

Here is the pipeline idea:

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'
}

What should i do?

Upvotes: 50

Views: 143190

Answers (8)

poerror
poerror

Reputation: 344

Another solution is to use a custom method without allowing extra permissions such as for new Properties() which leads to this error before allowing:

org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use new java.util.Properties

or adding extra plugin methods such as readProperties.

here is a method which reads a simple file named env_vars in this format:

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}"
    }
}

Upvotes: 2

Dhruv
Dhruv

Reputation: 10703

Here is a complete example of externalizing environment variables and loading them in Jenkins pipeline execution. The pipeline is written in a 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}"
                }
            }
        }

Complete code example: https://github.com/dhruv-bansal/jenkins-pipeline-exploration/blob/master/pipeline/basics/extenvvariable/Jenkinsfile

Where variables are loaded from a groovy file placed with the pipeline code only. https://github.com/dhruv-bansal/jenkins-pipeline-exploration/blob/master/pipeline/basics/extenvvariable/env.groovy

This pattern comes very handy when you are creating a generic pipeline that could be used across teams. You can externalize the dependent variable in such groovy file and each team can define their values according to their ecosystem.

Upvotes: 3

iEfimoff
iEfimoff

Reputation: 372

Using withEnv() to pass environment variables from file splitted by new line and casted to List:

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

Upvotes: 10

Abdennour TOUMI
Abdennour TOUMI

Reputation: 93491

With declarative pipeline, you can do it in one line ( change path by your value):

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

Upvotes: 13

Sumanta Mondal
Sumanta Mondal

Reputation: 171

Another way to resolve this install 'Pipeline Utility Steps' plugin that provides us readProperties method ( for reference please go to the link https://jenkins.io/doc/pipeline/steps/pipeline-utility-steps/#pipeline-utility-steps) Here in the example we can see that they are storing the keys into an array and using the keys to retrieve the value. But in that case the in production the problem will be like if we add any variable later into property file that variable needs to be added into the array of Jenkins file as well. To get rid of this tight coupling, we can write code in such a way so that the Jenkins build environment can get information automatically about all the existing keys which presents currently in the Property file. Here is an example for the reference

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

And the client code looks like

path = '\\ABS_Output\\EnvVars\\pic_env_vars.properties'
loadEnvironmentVariables(path)

Upvotes: 12

Sumanta Mondal
Sumanta Mondal

Reputation: 171

If you are using Jenkins 2.0 you can load the property file (which consists of all required Environment variables along with their corresponding values) and read all the environment variables listed there automatically and inject it into the Jenkins provided env entity.

Here is a method which performs the above stated action.

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}"
    }
}

To call this method we need to pass the path of property file as a string variable For example, in our Jenkins file using groovy script we can call like

path = "${workspace}/pic_env_vars.properties"
loadProperties(path)

Please ask me if you have any doubt

Upvotes: 5

huch
huch

Reputation: 725

From the comments to the accepted answer

Don't use global 'env' but use 'withEnv' construct, eg see: issue #9: don't set env vars with global env in top 10 best practices jenkins pipeline plugin

In the following example: VAR1 is a plain java string (no groovy variable expansion), VAR2 is a groovy string (so variable 'someGroovyVar' is expanded).

The passed script is a plain java string, so $VAR1 and $VAR2 are passed literally to the shell, and the echo's are accessing environment variables VAR1 and 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
    }
}

For secrets / passwords you can use credentials binding plugin

Example:

NOTE: CREDENTIALS_ID1 is a registered username/password secret on the Jenkins settings.

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

The jenkisn console log output hides the real values:

[Pipeline] echo
User name: ****
[Pipeline] echo
Password:  ****

Jenkins and credentials is a big issue, probably see: credentials plugin

For completeness: Most of the time, we need the secrets in environment variables, as we use them from shell scripts, so we combine the withCredentials and withEnv like follows:

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

        }
    }
}

Upvotes: 32

Daniel Omoto
Daniel Omoto

Reputation: 2491

One way you could load environment variables from a file is to load a Groovy file.

For example:

  1. Let's say you have a groovy file in '$JENKINS_HOME/.envvars' called 'stacktest-staging.groovy'.
  2. Inside this file, you define 2 environment variables you want to load

    env.DB_URL="hello"
    env.DB_URL2="hello2"
    
  3. You can then load this in using

    load "$JENKINS_HOME/.envvars/stacktest-staging.groovy"
    
  4. Then you can use them in subsequent echo/shell steps.

For example, here is a short pipeline script:

node {
   load "$JENKINS_HOME/.envvars/stacktest-staging.groovy"
   echo "${env.DB_URL}"
   echo "${env.DB_URL2}"
}

Upvotes: 72

Related Questions