jenkinsci / gradle-jpi-plugin

Build Jenkins Plugins with Gradle
79 stars 50 forks source link

Adding the Gradle JPI Plugin as a dependency breaks Jenkins Test Harness #199

Closed steven-terrana closed 2 years ago

steven-terrana commented 2 years ago

Jenkins and plugins versions report

n/a

What Operating System are you using (both controller, and any agents involved in the problem)?

N/A

Reproduction steps

N/A

Expected Results

N/A

Actual Results

N/A

Anything else?

Hello!

I've got a bit of a random issue i'm hoping you might be able to point me in the right direction on. If this isn't the right medium to ask, please shamelessly send me somewhere else :)

I'm working on a custom Gradle plugin related to the Jenkins plugin i maintain (the Jenkins Templating Engine).

It extends this Gradle Plugin to automatically package a user's JTE-compatible pipeline libraries into a stand alone Jenkins Plugin they can install.

for some reason, when i add org.jenkins-ci.tools:gradle-jpi-plugin:0.43.0 as a dependency in my Gradle Plugin, it breaks the Jenkins Test Harness.

I get some weird issue with:

Error Log ``` org.jvnet.hudson.reactor.ReactorException: java.lang.NoSuchMethodError: hudson.Extension.optional()Z at org.jvnet.hudson.reactor.Reactor.execute(Reactor.java:282) at jenkins.InitReactorRunner.run(InitReactorRunner.java:50) at jenkins.model.Jenkins.executeReactor(Jenkins.java:1164) at jenkins.model.Jenkins.(Jenkins.java:964) at hudson.model.Hudson.(Hudson.java:85) at org.jvnet.hudson.test.JenkinsRule.newHudson(JenkinsRule.java:688) at org.jvnet.hudson.test.JenkinsRule.before(JenkinsRule.java:404) at org.jvnet.hudson.test.JenkinsRule$1.evaluate(JenkinsRule.java:595) at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:288) at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:282) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.lang.Thread.run(Thread.java:748) Caused by: java.lang.NoSuchMethodError: hudson.Extension.optional()Z at hudson.ExtensionFinder$Sezpoz.logLevel(ExtensionFinder.java:741) at hudson.ExtensionFinder$Sezpoz._find(ExtensionFinder.java:716) at hudson.ExtensionFinder$Sezpoz.find(ExtensionFinder.java:695) at hudson.ClassicPluginStrategy.findComponents(ClassicPluginStrategy.java:346) at hudson.ExtensionList.load(ExtensionList.java:381) at hudson.ExtensionList.ensureLoaded(ExtensionList.java:317) at hudson.ExtensionList.getComponents(ExtensionList.java:183) at jenkins.model.Jenkins$6.onInitMilestoneAttained(Jenkins.java:1161) at jenkins.InitReactorRunner$1.onAttained(InitReactorRunner.java:85) at org.jvnet.hudson.reactor.ReactorListener$Aggregator.lambda$onAttained$3(ReactorListener.java:102) at org.jvnet.hudson.reactor.ReactorListener$Aggregator.run(ReactorListener.java:109) at org.jvnet.hudson.reactor.ReactorListener$Aggregator.onAttained(ReactorListener.java:102) at org.jvnet.hudson.reactor.Reactor$1.run(Reactor.java:177) at org.jvnet.hudson.reactor.Reactor$Node.run(Reactor.java:117) at jenkins.security.ImpersonatingExecutorService$1.run(ImpersonatingExecutorService.java:59) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ... 1 more ```

commenting out this dependency, while breaking my plugin, allows the Jenkins Test Harness to run successfully again.

so while this isn't a bug with the Gradle JPI Plugin, i'm hoping you might have a lead you could send me on. Really at a loss for what could be causing this one.

sghill commented 2 years ago

Hi @steven-terrana, this is a good place to ask :)

Can you share what you have on the plugin so far? It almost seems like the Jenkins Test Harness is running with the buildscript classpath, as opposed to the testRuntimeClasspath, but it'd help to see the plugin.

I suspect this plugin is the culprit here, based on the first line of the stacktrace:

java.lang.NoSuchMethodError: hudson.Extension.optional()Z

I took a look at hudson.Extension in jenkins-core and this method has existed for a very long time. However, the jpi plugin is bundling its own hudson.Extension. This change predates me by several years (https://github.com/jenkinsci/gradle-jpi-plugin/commit/507d6404397c01b3175ffc6b627106d73c75e73d), but my assumption is that the minimal version ends up resolved from the classloader, and that's causing issues.

I think there are a few options for what to do next.

One is delete the custom hudson.Extension from the jpi plugin and include jenkins-core as a dependency of the jpi plugin to resolve the real version. The problem with this is that its transitive dependency tree is massive, so we'd probably want to exclude its transitive dependencies.

jenkins-core dependency tree
org.jenkins-ci.main:jenkins-core:2.346.1
     +--- org.jenkins-ci.main:cli:2.346.1
     +--- org.jenkins-ci.main:remoting:4.13.2
     +--- antlr:antlr:2.7.7
     +--- args4j:args4j:2.33
     +--- com.github.spotbugs:spotbugs-annotations:4.6.0
     |    \--- org.junit:junit-bom:5.8.2
     +--- com.google.guava:guava:31.1-jre
     |    +--- com.google.guava:failureaccess:1.0.1
     |    +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
     |    +--- com.google.code.findbugs:jsr305:3.0.2
     |    +--- org.checkerframework:checker-qual:3.12.0
     |    +--- com.google.errorprone:error_prone_annotations:2.11.0
     |    \--- com.google.j2objc:j2objc-annotations:1.3
     +--- com.google.inject:guice:5.0.1
     |    +--- javax.inject:javax.inject:1
     |    \--- com.google.guava:guava:30.1-jre -> 31.1-jre (*)
     +--- com.infradna.tool:bridge-method-annotation:1.23
     |    \--- org.jenkins-ci:annotation-indexer:1.15 -> 1.16
     +--- com.jcraft:jzlib:1.1.3-kohsuke-1
     +--- com.sun.solaris:embedded_su4j:1.1
     +--- com.sun.xml.txw2:txw2:20110809
     |    \--- relaxngDatatype:relaxngDatatype:20020414
     +--- com.thoughtworks.xstream:xstream:1.4.19
     |    \--- io.github.x-stream:mxparser:1.2.2
     +--- commons-beanutils:commons-beanutils:1.9.4
     |    +--- commons-logging:commons-logging:1.2
     |    \--- commons-collections:commons-collections:3.2.2
     +--- commons-codec:commons-codec:1.15
     +--- commons-collections:commons-collections:3.2.2
     +--- commons-fileupload:commons-fileupload:1.4
     |    \--- commons-io:commons-io:2.2 -> 2.11.0
     +--- commons-httpclient:commons-httpclient:3.1-jenkins-3
     |    +--- commons-logging:commons-logging:1.2
     |    \--- commons-codec:commons-codec:1.8 -> 1.15
     +--- commons-io:commons-io:2.11.0
     +--- commons-jelly:commons-jelly-tags-fmt:1.0
     +--- commons-jelly:commons-jelly-tags-xml:1.1
     |    +--- commons-beanutils:commons-beanutils:1.6 -> 1.9.4 (*)
     |    +--- commons-collections:commons-collections:2.1 -> 3.2.2
     |    +--- commons-logging:commons-logging:1.0.3 -> 1.2
     |    +--- jaxen:jaxen:1.1-beta-2 -> 1.2.0
     |    \--- xerces:xerces:2.2.1
     +--- commons-lang:commons-lang:2.6
     +--- io.jenkins.stapler:jenkins-stapler-support:1.1
     +--- jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api:1.2.7
     +--- jaxen:jaxen:1.2.0
     +--- jline:jline:2.14.6
     +--- net.java.dev.jna:jna:5.11.0
     +--- net.java.sezpoz:sezpoz:1.13
     +--- net.jcip:jcip-annotations:1.0
     +--- net.sf.kxml:kxml2:2.3.0
     +--- org.apache.ant:ant:1.10.12
     |    \--- org.apache.ant:ant-launcher:1.10.12
     +--- org.apache.commons:commons-compress:1.21
     +--- org.codehaus.groovy:groovy-all:2.4.21
     +--- org.connectbot.jbcrypt:jbcrypt:1.0.0
     +--- org.fusesource.jansi:jansi:1.11
     +--- org.jenkins-ci:annotation-indexer:1.16
     +--- org.jenkins-ci:commons-jexl:1.1-jenkins-20111212
     |    \--- commons-logging:commons-logging:1.0.3 -> 1.2
     +--- org.jenkins-ci:crypto-util:1.7
     |    +--- commons-codec:commons-codec:1.15
     |    \--- commons-io:commons-io:2.11.0
     +--- org.jenkins-ci:memory-monitor:1.11
     |    \--- net.java.dev.jna:jna:5.10.0 -> 5.11.0
     +--- org.jenkins-ci:symbol-annotation:1.1
     |    \--- org.jenkins-ci:annotation-indexer:1.9 -> 1.16
     +--- org.jenkins-ci:task-reactor:1.7
     +--- org.jenkins-ci:version-number:1.10
     +--- org.jfree:jfreechart:1.0.19
     |    \--- org.jfree:jcommon:1.0.23
     +--- org.jvnet.hudson:commons-jelly-tags-define:1.0.1-hudson-20071021
     +--- org.jvnet.localizer:localizer:1.31
     +--- org.jvnet.robust-http-client:robust-http-client:1.2
     +--- org.jvnet.winp:winp:1.28
     +--- org.kohsuke:access-modifier-annotation:1.27
     |    \--- org.jenkins-ci:annotation-indexer:1.15 -> 1.16
     +--- org.kohsuke:windows-package-checker:1.2
     +--- org.kohsuke.jinterop:j-interop:2.0.8-kohsuke-1
     |    \--- org.kohsuke.jinterop:j-interopdeps:2.0.8-kohsuke-1
     |         \--- org.samba.jcifs:jcifs:1.2.19
     +--- org.kohsuke.stapler:json-lib:2.4-jenkins-3
     |    +--- commons-beanutils:commons-beanutils:1.8.0 -> 1.9.4 (*)
     |    +--- commons-collections:commons-collections:3.2.1 -> 3.2.2
     |    +--- commons-lang:commons-lang:2.5 -> 2.6
     |    +--- commons-logging:commons-logging:1.1.1 -> 1.2
     |    \--- net.sf.ezmorph:ezmorph:1.0.6
     |         \--- commons-lang:commons-lang:2.3 -> 2.6
     +--- org.kohsuke.stapler:stapler:1669.v95a_4b_919a_b_a_2
     |    +--- com.jcraft:jzlib:1.1.3-kohsuke-1
     |    +--- commons-beanutils:commons-beanutils:1.9.4 (*)
     |    +--- commons-codec:commons-codec:1.15
     |    +--- commons-discovery:commons-discovery:0.5
     |    |    \--- commons-logging:commons-logging:1.1.1 -> 1.2
     |    +--- commons-fileupload:commons-fileupload:1.4 (*)
     |    +--- commons-io:commons-io:2.11.0
     |    +--- javax.annotation:javax.annotation-api:1.3.2
     |    +--- jakarta.annotation:jakarta.annotation-api:2.0.0
     |    +--- org.jvnet:tiger-types:2.2
     |    +--- org.jvnet.localizer:localizer:1.31
     |    +--- org.kohsuke.stapler:json-lib:2.4-jenkins-3 (*)
     |    \--- org.ow2.asm:asm:9.2 -> 9.3
     +--- org.kohsuke.stapler:stapler-adjunct-codemirror:1.3
     |    \--- org.kohsuke.stapler:stapler:1.140 -> 1669.v95a_4b_919a_b_a_2 (*)
     +--- org.kohsuke.stapler:stapler-adjunct-timeline:1.5
     |    \--- org.kohsuke.stapler:stapler:1.140 -> 1669.v95a_4b_919a_b_a_2 (*)
     +--- org.kohsuke.stapler:stapler-groovy:1669.v95a_4b_919a_b_a_2
     |    +--- org.kohsuke.stapler:stapler-jelly:1669.v95a_4b_919a_b_a_2
     |    |    +--- org.kohsuke.stapler:stapler:1669.v95a_4b_919a_b_a_2 (*)
     |    |    +--- org.dom4j:dom4j:2.1.3
     |    |    |    +--- jaxen:jaxen:1.1.6 -> 1.2.0
     |    |    |    +--- javax.xml.stream:stax-api:1.0-2
     |    |    |    +--- net.java.dev.msv:xsdlib:2013.6.1
     |    |    |    |    \--- relaxngDatatype:relaxngDatatype:20020414
     |    |    |    +--- javax.xml.bind:jaxb-api:2.2.12
     |    |    |    +--- pull-parser:pull-parser:2
     |    |    |    \--- xpp3:xpp3:1.1.4c
     |    |    \--- org.jenkins-ci:commons-jelly:1.1-jenkins-20220125
     |    |         +--- org.jenkins-ci:commons-jexl:1.1-jenkins-20111212 (*)
     |    |         +--- commons-beanutils:commons-beanutils:1.9.4 (*)
     |    |         \--- commons-collections:commons-collections:3.2.2
     |    \--- org.codehaus.groovy:groovy-all:2.4.21
     +--- org.ow2.asm:asm:9.3
     +--- org.ow2.asm:asm-analysis:9.3
     |    \--- org.ow2.asm:asm-tree:9.3
     |         \--- org.ow2.asm:asm:9.3
     +--- org.ow2.asm:asm-commons:9.3
     |    +--- org.ow2.asm:asm:9.3
     |    +--- org.ow2.asm:asm-tree:9.3 (*)
     |    \--- org.ow2.asm:asm-analysis:9.3 (*)
     +--- org.ow2.asm:asm-tree:9.3 (*)
     +--- org.ow2.asm:asm-util:9.3
     |    +--- org.ow2.asm:asm:9.3
     |    +--- org.ow2.asm:asm-tree:9.3 (*)
     |    \--- org.ow2.asm:asm-analysis:9.3 (*)
     +--- org.slf4j:jcl-over-slf4j:1.7.36
     |    \--- org.slf4j:slf4j-api:1.7.36
     +--- org.slf4j:log4j-over-slf4j:1.7.36
     |    \--- org.slf4j:slf4j-api:1.7.36
     +--- org.springframework.security:spring-security-web:5.6.3
     |    +--- org.springframework.security:spring-security-core:5.6.3
     |    |    +--- org.springframework.security:spring-security-crypto:5.6.3
     |    |    +--- org.springframework:spring-aop:5.3.19
     |    |    |    +--- org.springframework:spring-beans:5.3.19
     |    |    |    |    \--- org.springframework:spring-core:5.3.19
     |    |    |    \--- org.springframework:spring-core:5.3.19
     |    |    +--- org.springframework:spring-beans:5.3.19 (*)
     |    |    +--- org.springframework:spring-context:5.3.19
     |    |    |    +--- org.springframework:spring-aop:5.3.19 (*)
     |    |    |    +--- org.springframework:spring-beans:5.3.19 (*)
     |    |    |    +--- org.springframework:spring-core:5.3.19
     |    |    |    \--- org.springframework:spring-expression:5.3.19
     |    |    |         \--- org.springframework:spring-core:5.3.19
     |    |    +--- org.springframework:spring-core:5.3.19
     |    |    \--- org.springframework:spring-expression:5.3.19 (*)
     |    +--- org.springframework:spring-core:5.3.19
     |    +--- org.springframework:spring-aop:5.3.19 (*)
     |    +--- org.springframework:spring-beans:5.3.19 (*)
     |    +--- org.springframework:spring-context:5.3.19 (*)
     |    +--- org.springframework:spring-expression:5.3.19 (*)
     |    \--- org.springframework:spring-web:5.3.19
     |         +--- org.springframework:spring-beans:5.3.19 (*)
     |         \--- org.springframework:spring-core:5.3.19
     \--- xpp3:xpp3:1.1.4c
dependencies {
    implementation('org.jenkins-ci.main:jenkins-core:2.346.1') { transitive = false }
}

Another option is to move this dependency off the buildscript classpath and to its own configuration by using the Worker API's classloader isolation feature. I think this is better, but it's a bit more work.

In either case I don't think I'll be able to get to this until next week sometime, but if you'd like to make the change, try it out and report back that would be awesome.

steven-terrana commented 2 years ago

Sure thing!

The plugin can be found here: Gradle JTE Plugin There's a sample application i'm using to test it manually alongside the automated tests: Test Gradle JTE Plugin

Thanks for the pointers! I'll take a stab at it and report back if i'm able to make any progress. Knowing that this plugin bundles a custom hudson.Extension is super helpful.

steven-terrana commented 2 years ago

so i got two things working..

Your recommendation of adding jenkins-core as a dependency and excluding all transitive dependencies worked as expected.

but it also worked to simply update the minimal clone of hudson.Extension

https://github.com/steven-terrana/gradle-jpi-plugin/commit/d8fb530ad70e4e0447a53cb8997446a6911dfb78

I'm not sure i'm brave enough to attempt the Worker API's classloader isolation.

I'd recommend the minimally intrusive change of updating the clone of hudson.Extension with those two expected lines.