Using Groovy map for DSL arguments

97 views Asked by At

Following the link here we have our own DSL extension which is defined like this in a file vars/buildFlow.groovy:

Map<String, Object> mapFromClosure(Closure body) {
    Map<String, Object> config = [:]
    body.resolveStrategy = Closure.DELEGATE_FIRST
    body.delegate = config
    body()

    // other things
}

In my Jenkinsfile, I can use this as such:

buildFlow {
    // different args
}

I would like to be able to dynamically populate the args to buildFlow but am unsure how to work with Groovy and closures. I have a Map of everything I want based on my conditions, but I can't pass the Map to buildFlow like this. Is there a way to convert the Map args to the closure constructor?

Map flowArgs = [
    argA: 1,
    argB: 2
]

buildFlow {
    flowArgs
}

I've seen solutions which talk about using ConfigObject but that is restricted:

Scripts not permitted to use new groovy.util.ConfigObject
1

There are 1 answers

2
VonC On BEST ANSWER

To dynamically populate the arguments for buildFlow using a Map, you might consider converting the Map to a closure in Groovy.
That can be done by iterating through the Map and applying each key-value pair as properties to a closure.

First, modify the mapFromClosure method in vars/buildFlow.groovy to handle both Closure and Map types:

Map<String, Object> mapFromClosure(Object body) {
    Map<String, Object> config = [:]

    if (body instanceof Closure) {
        body.resolveStrategy = Closure.DELEGATE_FIRST
        body.delegate = config
        body()
    } else if (body instanceof Map) {
        config.putAll(body)
    }

    // other things
    return config
}

Then, create a utility method to convert a Map to a Closure (as in "Groovy converting between Map and Closure"):

Closure mapToClosure(Map map) {
    return {
        map.each { key, value ->
            delegate."$key" = value
        }
    }
}

Use this utility method in your Jenkinsfile to convert your Map to a closure before passing it to buildFlow:

Map flowArgs = [
    argA: 1,
    argB: 2
]

buildFlow mapToClosure(flowArgs)

Result:

Jenkinsfile
    |
    └── buildFlow (Accepts Closure or Map)
            |
            └── mapFromClosure (Handles both Closure and Map)
                    |
                    └── mapToClosure (Converts Map to Closure)

That would provide the flexibility to handle both closures and maps in your DSL, allowing dynamic argument passing to buildFlow.
The mapToClosure function is used within the Jenkins pipeline to convert the Map of arguments into a Closure before passing it to buildFlow. That makes sure the pipeline script stays within the boundaries of what is allowed by Jenkins' script security.