Gradle项目中Groovy Builder模式创建Jenkins Job遇类缺失问题咨询
关于Jenkins Job DSL中Builder模式的使用与类缺失问题解决
背景
我正在寻找跨多个Jenkins Job共享值的方案,发现可通过Groovy类结合Builder模式创建Job,已搭建Gradle项目及Job DSL种子Job,能读取自定义类。
自定义JobBuilder类代码
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) {} } }
Job创建脚本(jobs/jobs.groovy)
import com.dsohub.jenkins.builder.JobBuilder job('test') { // 标准JobDsl语法 displayName('TEST') } new JobBuilder() // 尝试使用Builder模式 .name('test-job-builder') .description('description') .build(this) // 移除该行则无错误
运行种子Job时的错误栈
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
问题
- 该Builder模式是否仍推荐使用?自v1.60起此方式需调整安全设置,是否有更优方案?
- 如何解决调用JobBuilder的build()方法时出现的类缺失问题,实现创建test和test-job-builder两个Job的预期?
解决方案
问题1:Builder模式的适用性与替代方案
- Builder模式仍可使用,但从Job DSL v1.60开始,受安全沙箱限制,需要额外配置:在Jenkins全局安全设置的脚本安全中,将自定义类的包路径添加到批准列表,或通过Job DSL Script Approval插件批准相关方法调用。
- 更优方案推荐:
- Jenkins共享库(Shared Libraries):官方推荐的代码复用方式,无需额外调整安全沙箱,可将通用Job配置逻辑封装到共享库的类/方法中,跨脚本复用。
- 闭包封装通用配置:无需自定义类,用Groovy闭包封装重复配置块,比如定义通用的
jobConfig闭包,创建Job时传入参数调用,轻量且避免安全沙箱问题。
问题2:类缺失错误的解决
该错误源于Groovy版本不兼容:org.codehaus.groovy.vmplugin.v8.IndyInterface是Groovy 3.x+的类,而Jenkins内置Groovy版本可能低于此,或Gradle编译与Jenkins运行时的Groovy版本不一致。解决步骤:
- 统一Groovy版本:在Gradle的
build.gradle中指定与Jenkins匹配的Groovy版本:
plugins { id 'groovy' } dependencies { implementation localGroovy() // 使用Jenkins内置Groovy版本 implementation 'org.jenkins-ci.plugins:job-dsl-core:xxx' // 匹配Job DSL插件版本 }
- 手动实现Builder模式:避免依赖高版本Groovy的
@Builder注解,手动编写Builder逻辑:
package com.dsohub.jenkins.builder import javaposse.jobdsl.dsl.DslFactory import javaposse.jobdsl.dsl.jobs.FreeStyleJob class JobBuilder { String name String description static JobBuilder newBuilder() { new JobBuilder() } JobBuilder name(String name) { this.name = name this } JobBuilder description(String description) { this.description = description this } FreeStyleJob build(DslFactory dslFactory) { dslFactory.job(name) { if (description) { description(description) } } } }
- 兼容Job DSL插件版本:确保Job DSL插件版本与Jenkins版本匹配,在插件管理中更新/降级到稳定版本。
- 重新运行种子Job:修改后执行种子Job,即可同时创建
test和test-job-builder两个Job。
内容的提问来源于stack exchange,提问作者Mo0rBy
相关产品推荐
相关产品推荐

