先日 Yaml Axis Plugin を作った時にgradleとgroovyでJenkinsのプラグインを作ったのですが、その時のハマリポイントを中心にメモ
tl;dr
- https://wiki.jenkins-ci.org/display/JENKINS/Hosting+Plugins
- https://wiki.jenkins-ci.org/display/JENKINS/Gradle+JPI+Plugin
Gradle
https://wiki.jenkins-ci.org/display/JENKINS/Gradle+JPI+Plugin にある build.gradle
のサンプルをコピペして適宜編集して保存。
confluenceよりもGitHubの説明の方が詳しいです。
https://github.com/jenkinsci/gradle-jpi-plugin
Gradle JPI Plugin が gradle 2.3でしか動かないようなので
task wrapper(type: Wrapper) {
gradleVersion = '2.3'
}
のようにバージョンを合わせた方がいいです。
ちなみに2.10(現行最新)だと下記のような StackOverflowError
が出ました。
$ ./gradlew build --stacktrace
feature.
:localizer UP-TO-DATE
:stapler UP-TO-DATE
:compileJava
:compileGroovy
Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8
:processResources UP-TO-DATE
:classes
:jpi FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':jpi'.
> java.lang.StackOverflowError (no error message)
* Try:
Run with --info or --debug option to get more log output.
* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':jpi'.
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:69)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:35)
at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:64)
at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:52)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:203)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:185)
at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:66)
at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:50)
at org.gradle.execution.taskgraph.ParallelTaskPlanExecutor.process(ParallelTaskPlanExecutor.java:47)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:110)
at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:37)
at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
at org.gradle.execution.DefaultBuildExecuter.access$000(DefaultBuildExecuter.java:23)
at org.gradle.execution.DefaultBuildExecuter$1.proceed(DefaultBuildExecuter.java:43)
at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:30)
at org.gradle.initialization.DefaultGradleLauncher$4.run(DefaultGradleLauncher.java:154)
at org.gradle.internal.Factories$1.create(Factories.java:22)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:52)
at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:151)
at org.gradle.initialization.DefaultGradleLauncher.access$200(DefaultGradleLauncher.java:32)
at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:99)
at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:93)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:62)
at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:93)
at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:82)
at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:94)
at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:28)
at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:43)
at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:28)
at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:78)
at org.gradle.launcher.exec.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:48)
at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:52)
at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:37)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
at org.gradle.util.Swapper.swap(Swapper.java:38)
at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.health.DaemonHealthTracker.execute(DaemonHealthTracker.java:47)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:66)
at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:72)
at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.health.HintGCAfterBuild.execute(HintGCAfterBuild.java:41)
at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:246)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
Caused by: java.lang.StackOverflowError
at net.java.sezpoz.Index$LazyIndexIterator.peek(Index.java:144)
at net.java.sezpoz.Index$LazyIndexIterator.hasNext(Index.java:185)
at org.jenkinsci.gradle.plugins.jpi.JpiManifest.isSupportDynamicLoading(JpiManifest.groovy:129)
at org.jenkinsci.gradle.plugins.jpi.JpiManifest.<init>(JpiManifest.groovy:92)
at org.jenkinsci.gradle.plugins.jpi.Jpi.copy(Jpi.groovy:31)
at org.jenkinsci.gradle.plugins.jpi.Jpi.copy(Jpi.groovy:32)
at org.jenkinsci.gradle.plugins.jpi.Jpi.copy(Jpi.groovy:32)
(略)
BUILD FAILED
Total time: 1 mins 7.717 secs
CHANGELOG を見ると「fixed StackOverflowError when using Gradle 2.8」とあるので治ってそうなんですが、2.8でも同様のエラーが出たので2.3を使うのが確実みたいです。
Gradle JPI Pluginでリリースする時にversionのタグをpushする
Maven Jenkins Plugin だと mvn release:prepare release:perform
で
- バージョンでtagを作成してpush
- リリース時のみバージョンの
-SNAPSHOT
を削り、リリース後にバージョンをインクリメントして-SNAPSHOTを付加
ということができるのですが、Gradle JPI Pluginだとできないので下記のようなタスクを作りました
buildscript {
dependencies {
classpath 'org.ajoberstar:gradle-git:1.3.2'
}
}
import org.ajoberstar.grgit.*
ext.repo = Grgit.open()
task tagRelease << {
repo.tag.add {
name = version
message = "Release of ${version}"
}
repo.push(tags: true)
}
task checkVersion << {
if(version.endsWith("-SNAPSHOT")){
throw new GradleException("Remove '-SNAPSHOT' in version!")
}
}
task release(dependsOn: ["checkVersion", "publish", "tagRelease"])
./gradlew release
で
-
checkVersion
:version
の末尾に-SNAPSHOT
がついていたら中断 -
publish
1 : Update Centerにプラグインを公開 -
tagRelease
:version
でtagを作成してpush
が実行されます
build.gradle
今回のbuild.gradleは https://github.com/jenkinsci/yaml-axis-plugin/blob/master/build.gradle にあります
Groovy
普通に src/main/groovy とかにファイルを置くだけでいいですが、注意点としてはGroovyはJenkinsにバンドルされている1.8.9しか使えないこと。
最初2.4.5(現行最新)を使った時に、ビルドは通るのに gradlew server
でローカルのJenkinsにプラグインを乗せた時に下記のようなエラーが出てハマりました。サブクラスからスーパークラスのコンストラクタを呼び出す時になぜかコンストラクタの数が一致しないというエラー。
javax.servlet.ServletException: java.lang.IncompatibleClassChangeError: the number of constructors during runtime and compile time for hudson.matrix.Axis do not match. Expected -1 but got 3
at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:796)
at org.kohsuke.stapler.Stapler.invoke(Stapler.java:876)
at org.kohsuke.stapler.MetaClass$6.doDispatch(MetaClass.java:249)
at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:53)
at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:746)
at org.kohsuke.stapler.Stapler.invoke(Stapler.java:876)
at org.kohsuke.stapler.Stapler.invoke(Stapler.java:649)
at org.kohsuke.stapler.Stapler.service(Stapler.java:238)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:686)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1494)
at hudson.util.PluginServletFilter$1.doFilter(PluginServletFilter.java:123)
at hudson.util.PluginServletFilter.doFilter(PluginServletFilter.java:114)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482)
at hudson.security.csrf.CrumbFilter.doFilter(CrumbFilter.java:48)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482)
at hudson.security.ChainedServletFilter$1.doFilter(ChainedServletFilter.java:84)
at hudson.security.ChainedServletFilter.doFilter(ChainedServletFilter.java:76)
at hudson.security.HudsonFilter.doFilter(HudsonFilter.java:168)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482)
at org.kohsuke.stapler.compression.CompressionFilter.doFilter(CompressionFilter.java:49)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482)
at hudson.util.CharacterEncodingFilter.doFilter(CharacterEncodingFilter.java:81)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1482)
at org.kohsuke.stapler.DiagnosticThreadNameFilter.doFilter(DiagnosticThreadNameFilter.java:30)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1474)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:499)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:533)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1086)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:428)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1020)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
at org.eclipse.jetty.server.Server.handle(Server.java:366)
at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:489)
at org.eclipse.jetty.server.AbstractHttpConnection.content(AbstractHttpConnection.java:960)
at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.content(AbstractHttpConnection.java:1021)
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:865)
at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:240)
at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:668)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:52)
at winstone.BoundedExecutorService$1.run(BoundedExecutorService.java:77)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.IncompatibleClassChangeError: the number of constructors during runtime and compile time for hudson.matrix.Axis do not match. Expected -1 but got 3
at groovy.lang.MetaClassImpl.selectConstructorAndTransformArguments(MetaClassImpl.java:1413)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.selectConstructorAndTransformArguments(ScriptBytecodeAdapter.java:234)
at org.jenkinsci.plugins.yamlaxis.YamlAxis.<init>(YamlAxis.groovy:25)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77)
at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:102)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:54)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:198)
at org.jenkinsci.plugins.yamlaxis.YamlAxis$DescriptorImpl.newInstance(YamlAxis.groovy:80)
at org.jenkinsci.plugins.yamlaxis.YamlAxis$DescriptorImpl.newInstance(YamlAxis.groovy)
at hudson.model.Descriptor.newInstancesFromHeteroList(Descriptor.java:919)
at hudson.model.Descriptor.newInstancesFromHeteroList(Descriptor.java:900)
at hudson.util.DescribableList.rebuildHetero(DescribableList.java:205)
at hudson.matrix.MatrixProject.submit(MatrixProject.java:881)
at hudson.model.Job.doConfigSubmit(Job.java:1203)
at hudson.model.AbstractProject.doConfigSubmit(AbstractProject.java:785)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.kohsuke.stapler.Function$InstanceFunction.invoke(Function.java:298)
at org.kohsuke.stapler.Function.bindAndInvoke(Function.java:161)
at org.kohsuke.stapler.Function.bindAndInvokeAndServeResponse(Function.java:96)
at org.kohsuke.stapler.MetaClass$1.doDispatch(MetaClass.java:121)
at org.kohsuke.stapler.NameBasedDispatcher.dispatch(NameBasedDispatcher.java:53)
at org.kohsuke.stapler.Stapler.tryInvoke(Stapler.java:746)
... 48 more
build.gradle でGroovyのバージョンを固定するのが安全かもしれません。
dependencies {
runtime 'org.codehaus.groovy:groovy-all:1.8.9'
}
Gradleで作ったプラグインを公開する
先述の gradle publish
でupdate centerには公開されるのですが
- 自分が作ったプラグインをgithubの jenkinsci organization からfork
- https://jenkins.ci.cloudbees.com/job/plugins/ でプラグインのビルドを設定
のためには別途依頼をする必要があります
Jenkins Pluginを作成してみた。 - Qiita だとメーリングリストで申請をするようになってるのですが、今はJiraで依頼をするように変わっています
https://wiki.jenkins-ci.org/display/JENKINS/Hosting+Plugins#HostingPlugins-Requesthosting にもあるようにJiraにログイン後 https://issues.jenkins-ci.org/projects/HOSTING でissueを登録する必要があります
ちなみに自分が登録した時のはこれ
https://issues.jenkins-ci.org/browse/HOSTING-12 (Jiraのアカウントがないと見れません)
cloudbeesのジョブを設定してもらった時にMavenのテンプレートが使われていたのでGradleのテンプレートでジョブを作りなおしてもらいました。
Jenkinsのプラグイン作る時はMavenが多数派なのでそれ以外でプラグインを作ってる場合はissueを立てる時に「このプロジェクトはGradleを使ってるのでGradleのテンプレートを使ってジョブを作って」と書くのが親切かもしれません。
-
publish
はGradle JPI Pluginのタスク ↩