Jenkins. Inserting environment variable in the file

Viewed 1272

I have a .env file that contains the following data

API_URL=${API_URL}
API_KEY=${API_KEY}
API_SECRET=${API_SECRET}

Setting environment variables in Jenkins and passing them to the pipeline is clear. But it is not clear how do I replace ${API_URL}, ${API_KEY} & ${API_SECRET} in the .env file with their values in the Jenkins environment variable? Plus, how do I loop through all the Jenkins variables?

2 Answers

This basically requires two steps:

  1. Get all environment variables
  2. Replace values of environment variables in the template (.env) file

Let's start with #2, because it dictates which kind of data #1 must produce.

2. Replace variables in a template

We can use Groovy's SimpleTemplateEngine for this task.

def result = new SimpleTemplateEngine().createTemplate( templateStr ).make( dataMap )

Here templateStr is the template string (content of your .env file) and dataMap must be a Map consisting of string keys and values (the actual values of the environment variables). Getting the template string is trivial (use Jenkins readFile step), reading the environment variables into a Map is slightly more involved.

1. Read environment variables into a Map

I wrote "slightly more involved" because Groovy goodness makes this task quite easy aswell.

@Chris has already shown how to read environment variables into a string. What we need to do is split this string, first into separate lines and then each line into key and value. Fortunately, Groovy provides the member function splitEachLine of the String class, which can do both steps with a single call!
There is a little caveat, because splitEachLine is one of the functions that doesn't behave well in Jenkins pipeline context - it would only return the first line. Moving the critical code into a separate function, annotated with @NonCPS works around this problem.

@NonCPS
Map<String,String> envStrToMap( String envStr ) {
    def envMap = [:]
    envStr.splitEachLine('=') {
        envMap[it[0]] = it[1]
    } 
    return envMap
}

Finally

Now we have all ingredients for letting Jenkins cook us a tasty template soup!

Here is a complete pipeline demo. It uses scripted style, but it should be easy to use in declarative style as well. Just replace node with a script block.

import groovy.text.SimpleTemplateEngine     

node {
    // TODO: Replace the hardcoded string with: 
    // def tmp = readFile file: 'yourfile.env'
    def tmp = '''\
API_URL=${API_URL}
API_KEY=${API_KEY}
API_SECRET=${API_SECRET}'''
    
    withEnv(['API_URL=http://someurl', 'API_KEY=123', 'API_SECRET=456']) {    
    
        def envMap = getEnvMap()
        echo "envMap:\n$envMap"
        
        def tmpResolved = new SimpleTemplateEngine().createTemplate( tmp ).make( envMap )
  
        writeFile file: 'test.env', text: tmpResolved.toString()

        // Just for demo, to let me see the result
        archiveArtifacts artifacts: 'test.env' 
    }
}

// Read all environment variables into a map.
// Here, @NonCPS must NOT be used, because we are calling a Jenkins step.
Map<String,String> getEnvMap() {
    def envStr = sh(script: 'env', returnStdout: true)
    return envStrToMap( envStr )
}

// Split a multiline string, where each line consists of key and value separated by '='.
// It is critical to use @NonCPS to make splitEachLine() work!
@NonCPS
Map<String,String> envStrToMap( String envStr ) {
    def envMap = [:]
    envStr.splitEachLine('=') {
        envMap[it[0]] = it[1]
    } 
    return envMap
}

The pipeline creates an artifact "test.env" with this content:

API_URL=http://someurl
API_KEY=123
API_SECRET=456

You can access variables by executing simple shell in scripted pipeline:

def variables = sh(script: 'env|sort', returnStdout: true)

Then programatically in Groovy convert it to list and iterate using each loop.

According to replacing variables, if you're not using any solution which can access env variables then you can use simple text operations like executing sed on that file.

Related