user5544183
user5544183

Reputation: 75

String Interpolation in Groovy with Jenkins Pipeline file not working

So I have a Jenkins Pipeline which reads a text file (JSON) using the readFile method provided by Jenkins Pipeline. The text file app.JSON has multiple variables which are already defined in the Jenkins Pipeline.

While the readFile does read the file and convert into string it does not interpolate these variables. What are my options to interpolate these variables besides a simple string replace (Which I want to avoid)

I know I can use the readJSON or JSON parser but I want the output in string so it makes it easier for me to just read it as string and pass it along.

I have tried using Gstrings, ${-> variable} and .toString() method. Nothing worked for me.

Jenkins Pipeline Code

appServerName = 'gaga'
def appMachine = readFile file: 'util-silo-create-v2/app.json'
println appMachine

app.json

{
   "name":"${appServerName}",
   "fqdn":"${appServerName}"
}

There are more than one variable in both the pipeline and app.json that I want to substitute

The issue is with the readFile method provided by Jenkins Pipeline. Although it is very neat and easy to use it does not interpolate strings.

I expect below output

println appMachine

{
   "name":"gaga",
   "fqdn":"gaga"
}

Output I am getting

{
   "name":"${appServerName}",
   "fqdn":"${appServerName}"
}

Upvotes: 5

Views: 5586

Answers (1)

Szymon Stepniak
Szymon Stepniak

Reputation: 42184

Your assumption that readFile step (or any other method that reads content from a text file) should bind the variables from the current scope and interpolate variables placeholders in the raw text is wrong. However, you can use Groovy template engine to invoke something similar to GString variables interpolation. Consider the following example:

import groovy.text.SimpleTemplateEngine

def jsonText = '''{
   "name":"${appServerName}",
   "fqdn":"${appServerName}"
}'''

@NonCPS
def parseJsonWithVariables(String json, Map variables) {
    def template = new SimpleTemplateEngine()
    return template.createTemplate(json).make(variables.withDefault { it -> "\${$it}" }).toString()
}

node {
    stage("Test") {
        def parsed =  parseJsonWithVariables(jsonText, [
            appServerName: "gaga"
        ])

        echo parsed
    }
}

The method parseJsonWithVariables does what you expect to get. It is critical to make this method @NonCPS, because the SimpleTemplateEngine, as well as map created using withDefault() are not serializable. It takes a JSON read previously from a file (in this example I use a variable instead for simplicity) and a map of parameters. It converts this map to a map with a default value (the part variables.withDefault { ... } is responsible for that) so the template engine does not complain that there is no property with a given name. In this case the default method returns a variable "as is", but you can return an empty string or a null value instead. Whatever works for you better.

When you run it you will something like this:

[Pipeline] Start of Pipeline (hide)
[Pipeline] node
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Test)
[Pipeline] echo
{
   "name":"gaga",
   "fqdn":"gaga"
}
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

Upvotes: 7

Related Questions