Re: Jenkins Trigger in Ruby

114 views
Skip to first unread message

Charles Lowell

unread,
Oct 23, 2011, 3:56:53 PM10/23/11
to Harry Wilkinson, jenkin...@googlegroups.com, jenk...@googlegroups.com

On Oct 23, 2011, at 2:12 PM, Harry Wilkinson wrote:

Hello,

I've read some of the recent Jenkins mailing list activity about Ruby-based plugins with great interest.  There's a particular kind of build trigger (for GitHub pull requests) that I'd love to have, and it seems like the best way to get it is to create it myself, but I have no experience with Java; I've used Ruby quite a bit, though, so the work that's been done with the Ruby plugin looks really promising.

The short answer is that it is definitely possible to create triggers in Ruby. The long answer is that we have yet to wrap the trigger interface into "native" Ruby, so you will need to implement the Java interface in Ruby, and then optionally implement a proxy to make it nice.



So, my question: is it possible/practical to create triggers in Ruby with what's done so far?  I don't know too much (only what I've read) about Jenkins development, but it looks like triggers might be a little different to the build steps that (as far as I can tell) are supported in Ruby so far.  Would it be a matter of creating a new model and task (and maybe a new proxy?) in the jenkins-plugin-runtime gem?

Yes, you will need to implement the Java interface with Ruby first. Then, once you have that working, you can convert your Java class (which is implemented in Ruby) into a proxy that wraps a plain old Ruby object. This is generally what I do. Does that make sense?

So, after you create your plugin with the 'jpi' tool. make  a file called "my_trigger.rb" in the models/ directory. It might look something like this:

<Ruby>

class MyTrigger < Java.hudson.triggers.Trigger 
def start(job)
           #code to do stuff here
        end
end

#now we need to explicitly register this, since it's not yet an "official" proxy interface, and it won't happen automatically
Jenkins::Plugin.instance.register_describable MyTrigger, Java.hudson.triggers.Trigger

</Ruby>

And you should be good to go. Keep in mind that this class is implementing a Java interface, and as such it receives Java objects in its methods, and must return Java objects that conform to the interface.

Can somebody from the Java part of the Jenkins community tell me if this will actually work? I noticed that the hudson.triggers.Trigger class is parameterized by Item... Does this class factor into when the trigger is invoked?


Sorry if this is naive; I'm trying to figure out how this all fits together.

It's daunting at first, especially since we have about 1% of all extension points available, but hopefully this will get you in the right direction.


Thanks.

Harry

cheers,
Charles


Harry Wilkinson

unread,
Oct 23, 2011, 6:35:50 PM10/23/11
to Charles Lowell, jenkin...@googlegroups.com, jenk...@googlegroups.com
Thanks for the advice Charles.  I will have a play with it and see what happens.

Much appreciated.

Harry

Kenny

unread,
Oct 28, 2011, 9:41:44 AM10/28/11
to jenkins.rb
Hello

I tried this but I got an exception as follow

wrong number of arguments for constructor (ArgumentError)
.rvm/gems/jruby-1.6.4/gems/jenkins-plugin-runtime-0.1.10/lib/jenkins/
model/descriptor.rb:9:in `initialize'
.rvm/gems/jruby-1.6.4/gems/jenkins-plugin-runtime-0.1.10/lib/jenkins/
plugin.rb:104:in `register_describable'

How can I solve this problem?

Harry Wilkinson

unread,
Oct 29, 2011, 8:11:08 AM10/29/11
to jenkins.rb
Hi Kenny,

I also saw that error. I tried redefining the initialize method on
Descriptor to add some logging, and just doing that made the exception
stop happening - I don't know why yet, but I hope to find out and fix
it properly at some point.

I have found that the descriptor provided by a
Java.hudson.triggers.Trigger object must actually derive from
TriggerDescriptor (which derives from Descriptor) rather than just
Descriptor, so register_describable doesn't actually work for triggers
yet. Or, I may have just got things mixed up - this is my first foray
into the Jenkins architecture.

I really haven't had much time to work on this yet, but I'd like to
try to make it work sensibly so I'd be very interested if Charles (or
perhaps someone else) has a good idea of how it ought to work.

Thanks.

Harry

Charles Lowell

unread,
Oct 31, 2011, 8:02:27 AM10/31/11
to jenk...@googlegroups.com

On Oct 29, 2011, at 7:11 AM, Harry Wilkinson wrote:

> Hi Kenny,
>
> I also saw that error. I tried redefining the initialize method on
> Descriptor to add some logging, and just doing that made the exception
> stop happening - I don't know why yet, but I hope to find out and fix
> it properly at some point.
>
> I have found that the descriptor provided by a
> Java.hudson.triggers.Trigger object must actually derive from
> TriggerDescriptor (which derives from Descriptor) rather than just
> Descriptor, so register_describable doesn't actually work for triggers
> yet. Or, I may have just got things mixed up - this is my first foray
> into the Jenkins architecture.
>


I don't believe that's what's happening, but if Triggers do require TriggerDescriptors,
then that is something we will need to address further down the road. My suspicion is that it will through a ClassCastException at some point after or during instantiation. However, the trace here shows it failing at registration time, well before that happens.

I'm not sure what's going on here since descriptor.rb:9 is just explicitly invoking the super constructor on Descriptor.

Can you paste the code somewhere I can have a look at it online?

Kenny

unread,
Nov 2, 2011, 2:21:02 AM11/2/11
to jenkins.rb
Hi Charles,

The code is exactly the same you pasted above.

<code>
class MyTrigger < Java.hudson.triggers.Trigger
def start(job)
#code to do stuff here
end
end
Jenkins::Plugin.instance.register_describable MyTrigger,
Java.hudson.triggers.Trigger
<code>

And the full exception messages are as below:

<exception>
Loading jenkins/test/models/trigger.rb
wrong number of arguments for constructor (ArgumentError)
.rvm/gems/jruby-1.6.4/gems/jenkins-plugin-runtime-0.1.10/lib/jenkins/
model/descriptor.rb:9:in `initialize'
.rvm/gems/jruby-1.6.4/gems/jenkins-plugin-runtime-0.1.10/lib/jenkins/
plugin.rb:104:in `register_describable'
Documents/jenkins/test/models/trigger.rb:6:in `(root)'
org/jruby/RubyKernel.java:1063:in `load'
jenkins/test/models/trigger.rb:180:in `load_file_in_dir'
org/jruby/RubyDir.java:522:in `each'
.rvm/gems/jruby-1.6.4/gems/jenkins-plugin-runtime-0.1.10/lib/jenkins/
plugin.rb:172:in `load_file_in_dir'
.rvm/gems/jruby-1.6.4/gems/jenkins-plugin-runtime-0.1.10/lib/jenkins/
plugin.rb:163:in `load_models'
.rvm/gems/jruby-1.6.4/gems/jenkins-plugin-runtime-0.1.10/lib/jenkins/
plugin.rb:52:in `initialize'
<exception>

By the way, I tried the code below and got different exception.
<code>
class Builder2 < Java.hundson.tasks.Builder
def prebuild(build, listener)
puts 'test'
end
end
Jenkins::Plugin.instance.register_describable Builder2,
Java.hundson.tasks.Builder
<code>

<exception>
Loading jenkins/test/models/builder2.rb
cannot load Java class hundson.tasks.Builder (NameError)
org/jruby/javasupport/JavaUtilities.java:54:in
`get_proxy_or_package_under_package'
file:jenkins/test/work/plugins/ruby-runtime/WEB-INF/lib/jruby-
complete-1.6.1.jar!/builtin/javasupport/java.rb:51:in `method_missing'
jenkins/test/models/builder2.rb:1:in `(root)'
org/jruby/RubyKernel.java:1063:in `load'
jenkins/test/models/builder2.rb:180:in `load_file_in_dir'
org/jruby/RubyDir.java:522:in `each'
.rvm/gems/jruby-1.6.4/gems/jenkins-plugin-runtime-0.1.10/lib/jenkins/
plugin.rb:172:in `load_file_in_dir'
.rvm/gems/jruby-1.6.4/gems/jenkins-plugin-runtime-0.1.10/lib/jenkins/
plugin.rb:163:in `load_models'
.rvm/gems/jruby-1.6.4/gems/jenkins-plugin-runtime-0.1.10/lib/jenkins/
plugin.rb:52:in `initialize'
<exception>

Thanks.

Kenny

Harry Wilkinson

unread,
Nov 2, 2011, 2:28:06 PM11/2/11
to jenkins.rb
Kenny,

Using the first code example, I got the same exception. I haven't yet
spent the time to figure out why.

The second example has a typo, that's all - it is 'hudson' not
'hundson'.

Harry

Wenting Gu

unread,
Nov 3, 2011, 8:39:33 AM11/3/11
to jenk...@googlegroups.com
I tried to do the same thing and I got a different error as follow.
------------------------------------------------------------
jenkins.model.Jenkins$6 onTaskFailed
致命的: Failed Loading plugin jenkins-jruby
java.io.IOException: Dependency git (1.1.11) doesn't exist
at hudson.PluginWrapper.resolvePluginDependencies(PluginWrapper.java:424)
at hudson.PluginManager$2$1$1.run(PluginManager.java:289)
at org.jvnet.hudson.reactor.TaskGraphBuilder$TaskImpl.run(TaskGraphBuilder.java:146)
at org.jvnet.hudson.reactor.Reactor.runTask(Reactor.java:259)
at jenkins.model.Jenkins$5.runTask(Jenkins.java:798)
at org.jvnet.hudson.reactor.Reactor$2.run(Reactor.java:187)
at org.jvnet.hudson.reactor.Reactor$Node.run(Reactor.java:94)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
2011/11/03 21:35:53 jenkins.model.Jenkins$6 onAttained
情報: Prepared all plugins
2011/11/03 21:35:53 jenkins.model.Jenkins$6 onTaskFailed
致命的: Failed Initializing plugin jenkins-jruby
java.lang.NullPointerException
at hudson.PluginManager$2$1$2.run(PluginManager.java:306)
at org.jvnet.hudson.reactor.TaskGraphBuilder$TaskImpl.run(TaskGraphBuilder.java:146)
at org.jvnet.hudson.reactor.Reactor.runTask(Reactor.java:259)
at jenkins.model.Jenkins$5.runTask(Jenkins.java:798)
at org.jvnet.hudson.reactor.Reactor$2.run(Reactor.java:187)
at org.jvnet.hudson.reactor.Reactor$Node.run(Reactor.java:94)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
----------------------------------------------

I created a new plugin,and I wrote the code.
<code>
> class Builder2 < Java.hudson.tasks.Builder

>   def prebuild(build, listener)
>     puts 'test'
>   end
> end
> Jenkins::Plugin.instance.register_describable Builder2,
> Java.hudson.tasks.Builder
> <code>

But if I put this code into jenkins-prototype-ruby-plugin, I got the same error as yours.
How can I solve the error? And my git version is 1.7.5.4.

Thank you.
yours,
Wendy


2011/11/3 Harry Wilkinson <hwilk...@mdsol.com>

Charles Lowell

unread,
Nov 3, 2011, 9:04:53 AM11/3/11
to jenk...@googlegroups.com
Wenting,

Looks like you need to install the Git plugin. If you invoke with the 'jpi server' command, it should work.

cheers,
Charles

Wenting Gu

unread,
Nov 3, 2011, 9:16:56 AM11/3/11
to jenk...@googlegroups.com
Charles,

Thank you for your reply, but I don't understand what you said.
I used jpi new to create a new plugin, and I created a new folder named "models", then I put the code in the folder.
and I run command 'bundle' and 'jpi server', but I got error   '致命的: Failed Loading plugin jenkins-jruby
java.io.IOException: Dependency git (1.1.11) doesn't exist'
How can I install the Git plugin?

Charles Lowell

unread,
Nov 3, 2011, 9:27:27 AM11/3/11
to jenk...@googlegroups.com
On Nov 3, 2011, at 8:16 AM, Wenting Gu wrote:

Charles,

Thank you for your reply, but I don't understand what you said.
I used jpi new to create a new plugin, and I created a new folder named "models", then I put the code in the folder.
and I run command 'bundle' and 'jpi server', but I got error   '致命的: Failed Loading plugin jenkins-jruby
java.io.IOException: Dependency git (1.1.11) doesn't exist'
How can I install the Git plugin?

Actually, to get started, you don't need the git plugin, especially if you're not using Git.

Just open up your .pluginspec file in the root of your plugin directory and repmove the line that says:

plugin.depends_on 'git', '1.1.11'


cheers,
Charles

Kohsuke Kawaguchi

unread,
Nov 4, 2011, 11:54:34 AM11/4/11
to jenk...@googlegroups.com, Kenny

I found out the root cause. In the 2nd argument, this is actually
passing in a Ruby class object, whereas the code is expecting the Java
class.

>>> Jenkins::Plugin.instance.register_describable MyTrigger, Java.hudson.triggers.Trigger

By changing this to the following, it got passed this problem:

> Jenkins::Plugin.instance.register_describable MyTrigger, Java.hudson.triggers.Trigger.java_class

The error message was very confusing. It's not the wrong number of
arguments, but rather just a type mismatch. I wonder if this is a bug in
JRuby.


There are other challenges before making this example work. So stay
tuned for further updates.


--
Kohsuke Kawaguchi | CloudBees, Inc. | http://cloudbees.com/
Try Nectar, our professional version of Jenkins

Charles Lowell

unread,
Nov 4, 2011, 12:26:09 PM11/4/11
to jenk...@googlegroups.com, Kenny
On Nov 4, 2011, at 10:54 AM, Kohsuke Kawaguchi wrote:


I found out the root cause. In the 2nd argument, this is actually passing in a Ruby class object, whereas the code is expecting the Java class.

Jenkins::Plugin.instance.register_describable MyTrigger, Java.hudson.triggers.Trigger

By changing this to the following, it got passed this problem:

Jenkins::Plugin.instance.register_describable MyTrigger, Java.hudson.triggers.Trigger.java_class

The error message was very confusing. It's not the wrong number of arguments, but rather just a type mismatch. I wonder if this is a bug in JRuby.


There are other challenges before making this example work. So stay tuned for further updates.

Yes, the other challenge is that the out-of-the-box descriptor that ships with jenkins-plugin-runtime is expecting that your object will be a pure ruby object.

I think you're best bet is to manually register the trigger and it's descriptor (which has to be an instance of TriggerDescriptor?)

Here is a working example:


we can work out how to abtract different descriptor subclasses later.

cheers,
Charles
Reply all
Reply to author
Forward
0 new messages