General Questions - Jenkins DSL

Jenkins setup:
Jenkins 2.319.2 core

Hi Jenkins Expert,

I’m using the Jenkins Job DSL plugin and the jenkins declarative pipeline syntax for creating Jenkin jobs with multiple build stages. Issues I have:

  1. No quick/good way to test the DSL other than commit and push the code to trigger a job generation and then run the job. Is there a quicker way to do this?

  2. No good documentation on the data types and APIs. E.g. for the build step How do I know the return type of the build call? And what is data is available on the returned object?

  3. Is there a good way to check the underlying hudson.model related to the pipline syntax? It would be very helpful to be able to know the underlying Java type so I can look up what data/method is available.

Thanks in advance for any help!

Hello @current817 and welcome to this community. :wave:

Jenkins Job DSL scripts can be tested locally using the Gradle build tool and the Jenkins Job DSL Gradle Example project. Or at least, it used to be possible, the project has not moved in 7 years, so I can’t guarantee it still works.

Here’s a basic outline of the steps:

  • Clone the Jenkins Job DSL Gradle Example project from GitHub: git clone https://github.com/sheehan/job-dsl-gradle-example.git
  • Write your Job DSL scripts in the jobs directory of the cloned project.
  • Run the gradle test command to execute the tests.
  • Run the gradle runDsl command to create an XML output of the jobs.

This should allow you to test your Job DSL scripts without having to commit and push the code to Jenkins… at least, if the project still works.

Sorry @poddingue for the super late response. I didn’t see any notifications so thought nobody responded to my question. Thanks for the warm welcome!

Sounds good. I will take a look at gradle build solution in the given git repo and follow up with any questions.

Thanks again!

1 Like

Best of luck, @current817 !

1 Like

Hi both, sorry to necro bump this thread but I am very stuck and this is directly related.
Have either of you found out anymore about being able to do this?

I have been trying to explore getting a proof of concept for JobBuilder classes to work which are highlighted in Sheehans repo. This will allow some nicer configuration + testing to be done on my jobs, but I cannot get it to work as well as allow more sharable modules to share with other teams.

I posted a StackOverflow question yesterday so will link it, but I will also explain here.


I have set up Jenkins on a local Kind cluster using the Helm chart 4.12.1, Jenkins controller image 2.492.2. Fairly default settings, but disabling jobdsl script security (as now required, see here (I am limited to 2 hyperlinks! So here is what you need jenkins github → job-dsl-plugin/wiki/Migration#script-security)) and allowing execution of the seed job on local executor.

Also make sure to set your AdditionalClasspath in the JobDsl script reading step for you seed job, your Gradle project structure will determine what this. In my case, it is app/build/classes/groovy/main.
I mention this specifically as you need to do this to import the class into the JobDsl files in jobs/ and because I get a class error and I want to ensure this isn’t causing it.

I have created a Gradle project similar to Sheehan’s repo and have a seed job to build my Groovy classes and then read JobDsl scripts.

Here is my very basic and dumbed down class:

// app/src/main/groovy/com/dsohub/jenkins/builder/JobBuilder.groovy
package com.dsohub.jenkins.builder

import groovy.transform.builder.Builder
import groovy.transform.builder.SimpleStrategy
import javaposse.jobdsl.dsl.DslFactory
import javaposse.jobdsl.dsl.jobs.FreeStyleJob


@Builder(builderStrategy = SimpleStrategy, prefix = '')
class JobBuilder {
    String name
    String description

    FreeStyleJob build(DslFactory dslFactory) {
        dslFactory.job(name) {
            // I have tried with and without a description and some things in the closure.
        }
    }
}

And here is my basic JobDsl script:

// jobs/jobs.groovy
import com.dsohub.jenkins.builder.JobBuilder

// Tried with and without this normally defined Job, as suggested on StackOverflow
job('test') {
    description('a test job')
}

// This is the thing I want to get working
new JobBuilder()
    .name('test-job-builder')
    .description('description')
    .build(this)                            // Tried with and without this line

I get some different results depending on what I do in the jobs/jobs.groovy file.

1st, not setting AdditionalClasspath, as I stated earlier, in the seed job will result in an import error for the JobBuilder class.
2nd, Adding/removing the normally defined test job results in no unexpected changes and does not affect the JobBuilder class
3rd, Adding/removing the .build(this) results in 2 different results, 1 expected, and 1 that seems to be erroring.

The expected result without the .build(this) is that nothing happens. The class is instantiated in the script and does not call any of it’s methods.

The error result with the .build(this) is this stack trace showing a missing class error:

Processing DSL script jobs/jobs.groovy
FATAL: org/codehaus/groovy/vmplugin/v8/IndyInterface
java.lang.ClassNotFoundException: org.codehaus.groovy.vmplugin.v8.IndyInterface
    at java.base/java.net.URLClassLoader.findClass(Unknown Source)
    at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
    at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
Caused: java.lang.NoClassDefFoundError: org/codehaus/groovy/vmplugin/v8/IndyInterface
    at com.dsohub.jenkins.builder.JobBuilder.build(JobBuilder.groovy:15)
    at com.dsohub.jenkins.builder.JobBuilder$build$1.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:128)
    at jobs.run(jobs.groovy:11)
    at jobs$run.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
    at script$run.call(Unknown Source)
    at PluginClassLoader for job-dsl//javaposse.jobdsl.dsl.AbstractDslScriptLoader.runScript(AbstractDslScriptLoader.groovy:138)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.base/java.lang.reflect.Method.invoke(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:210)
    at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:59)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:51)
    at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:64)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:169)
    at PluginClassLoader for job-dsl//javaposse.jobdsl.dsl.AbstractDslScriptLoader.runScriptEngine(AbstractDslScriptLoader.groovy:108)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.base/java.lang.reflect.Method.invoke(Unknown Source)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:98)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:352)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1034)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:68)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:177)
    at PluginClassLoader for job-dsl//javaposse.jobdsl.dsl.AbstractDslScriptLoader$_runScripts_closure1.doCall(AbstractDslScriptLoader.groovy:61)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.base/java.lang.reflect.Method.invoke(Unknown Source)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:98)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:264)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1034)
    at groovy.lang.Closure.call(Closure.java:420)
    at groovy.lang.Closure.call(Closure.java:436)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2125)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2110)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2163)
    at org.codehaus.groovy.runtime.dgm$165.invoke(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:274)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:58)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:128)
    at PluginClassLoader for job-dsl//javaposse.jobdsl.dsl.AbstractDslScriptLoader.runScripts(AbstractDslScriptLoader.groovy:46)
    at PluginClassLoader for job-dsl//javaposse.jobdsl.plugin.ExecuteDslScripts.perform(ExecuteDslScripts.java:363)
    at jenkins.tasks.SimpleBuildStep.perform(SimpleBuildStep.java:123)
    at hudson.tasks.BuildStepCompatibilityLayer.perform(BuildStepCompatibilityLayer.java:80)
    at hudson.tasks.BuildStepMonitor$1.perform(BuildStepMonitor.java:20)
    at hudson.model.AbstractBuild$AbstractBuildExecution.perform(AbstractBuild.java:818)
    at hudson.model.Build$BuildExecution.build(Build.java:199)
    at hudson.model.Build$BuildExecution.doRun(Build.java:164)
    at hudson.model.AbstractBuild$AbstractBuildExecution.run(AbstractBuild.java:527)
    at hudson.model.Run.execute(Run.java:1833)
    at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:44)
    at hudson.model.ResourceController.execute(ResourceController.java:101)
    at hudson.model.Executor.run(Executor.java:446)
Finished: FAILURE

I have tried googling and searching for what this class and anyone else having the same error, but I just can’t find anything.

1st want to make sure that setting the AdditionalClasspath in the seed job is not doing this? I doubt it is, but I want to make sure.
And 2nd, assuming that the 1st question is a no, then I want to fix this other org/codehaus/groovy/vmplugin/v8/IndyInterface missing class so I can use this very nice Builder pattern.