You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题

  1. 该Builder模式是否仍推荐使用?自v1.60起此方式需调整安全设置,是否有更优方案?
  2. 如何解决调用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版本不一致。解决步骤:

  1. 统一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插件版本
}
  1. 手动实现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)
            }
        }
    }
}
  1. 兼容Job DSL插件版本:确保Job DSL插件版本与Jenkins版本匹配,在插件管理中更新/降级到稳定版本。
  2. 重新运行种子Job:修改后执行种子Job,即可同时创建test和test-job-builder两个Job。

内容的提问来源于stack exchange,提问作者Mo0rBy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 13:10:53