Georgios F.
Georgios F.

Reputation: 455

How to parse Jenkinsfile from Groovy code

I have a Jenkinsfile with some properties, for example

trace = false

userNotifications = [
    build_master :                 [name : 'name',
                                    email : 'email',
                                    slackid: 'slack id',
                                    slackchannel: 'slack channel']
]
env.aProperty = "aValue"
node('COMPILE')
{
...
}

I want to parse the above Jenkinsfile inside groovy code in order to access some of the property values.

When I use GroovyShell like this

        Binding binding = new Binding()
        GroovyShell shell = new GroovyShell(binding)
        Object groovyDsl = shell.evaluate(clean)

I get this error

groovy.lang.MissingMethodException: No signature of method: Script1.node() is applicable for argument types: (String, Script1$_run_closure1) values: [COMPILE, Script1$_run_closure1@7d804e7]

I might be able to get around the particular error with some Groovy meta-programming, however, I am unsure if this is the right direction. My question is what is the best way to parse a Jenkinsfile in Groovy code? This is Groovy DSL at the end of the day and I would expect it to be more straightforward.

Upvotes: 1

Views: 1933

Answers (2)

Georgios F.
Georgios F.

Reputation: 455

Here is how I eventually did what I wanted:

import org.codehaus.groovy.control.CompilerConfiguration

class JenkinsfileParser {
    static void main(String[] args) {
        LinkedHashMap vars = JenkinsfileParser.parse('Jenkinsfile' as File)
        println vars.toString()
    }

    static LinkedHashMap parse(File jenkinsfile) {
        CompilerConfiguration config = new CompilerConfiguration()
        config.scriptBaseClass = JenkinsfileBaseClass.class.name
        Binding binding = new Binding()
        GroovyShell shell = new GroovyShell(this.class.classLoader, binding, config)
        String clean = jenkinsfile.text.replace('import hudson.model.*', '')
                .replace('import hudson.EnvVars', '')
        Script script = shell.parse(clean)
        script.run()
        return binding.variables
    }

    static abstract class JenkinsfileBaseClass extends Script {
        LinkedHashMap<String, Closure> nodeClosures = new LinkedHashMap<>()
        LinkedHashMap env = new LinkedHashMap()
        CurrentBuild currentBuild = new CurrentBuild()
        LinkedHashMap parallelClosures = new LinkedHashMap<>()

        void node(String name, Closure closure) {
            nodeClosures.put(name, closure)
        }

        void parallel(LinkedHashMap closures) {
            parallelClosures.putAll(closures)
        }

        class CurrentBuild {
            List<String> expectedResults = new ArrayList<>()

            boolean resultIsBetterOrEqualTo(String expected) {
                expectedResults << expected
                return true
            }
        }
    }
}

The JenkinsfileBaseClass is specific to my Jenkinsfile and would need adaptation for a different Jenkinsfile.

Relevant documentation can be found here: https://groovy-lang.org/integrating.html

I tried using the implementation group: 'org.jenkins-ci.main', name: 'jenkins-core', version: '2.9' package from the maven repository http://repo.jenkins-ci.org/public/, however, there doesn't seem to be anything in there useful for my case.

Let me know if there's a better or more elegant way of doing it.

Upvotes: 3

HumayunM
HumayunM

Reputation: 554

DSLs are context specific and can't be run out of context.

you could try

  • Script Console under Manage Jenkins.

Upvotes: 0

Related Questions