├── .gitignore ├── .gitmodules ├── Gemfile ├── Gemfile.lock ├── README.md ├── pom.xml ├── spec ├── jenkins │ └── model_spec.rb └── spec_helper.rb └── src └── main ├── java └── ruby │ ├── RubyDoDynamic.java │ ├── RubyExtensionFinder.java │ ├── RubyPlugin.java │ └── SimpleGet.java ├── resources ├── index.jelly ├── ruby │ ├── plugin.rb │ ├── plugin │ │ ├── models │ │ │ ├── fog_cloud.rb │ │ │ ├── noop_wrapper.rb │ │ │ └── test_root_action.rb │ │ └── views │ │ │ └── fog_cloud │ │ │ ├── computerSet.jelly │ │ │ ├── config.erb │ │ │ ├── config.jelly │ │ │ └── global.jelly │ └── support │ │ ├── bundled-gems.jar │ │ └── hudson │ │ └── plugin │ │ ├── build_wrapper.rb │ │ ├── cloud.rb │ │ ├── controller.rb │ │ ├── descriptor.rb │ │ ├── models.rb │ │ └── root_action.rb └── test_root_action │ ├── call-tags.erb │ └── index.erb └── webapp └── help-globalConfig.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.ipr 2 | *.iml 3 | *.iws 4 | .idea/ 5 | target/ 6 | work/ 7 | vendor/ 8 | .bundle 9 | .rvmrc -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/main/resources/ruby/jenkins-plugins"] 2 | path = src/main/resources/ruby/jenkins-plugins 3 | url = https://github.com/cowboyd/jenkins-plugins.rb.git 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | 3 | group :development do 4 | gem "rspec", ">= 2.5.0" 5 | end -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | diff-lcs (1.1.2) 5 | rspec (2.5.0) 6 | rspec-core (~> 2.5.0) 7 | rspec-expectations (~> 2.5.0) 8 | rspec-mocks (~> 2.5.0) 9 | rspec-core (2.5.1) 10 | rspec-expectations (2.5.0) 11 | diff-lcs (~> 1.1.2) 12 | rspec-mocks (2.5.0) 13 | 14 | PLATFORMS 15 | java 16 | ruby 17 | 18 | DEPENDENCIES 19 | rspec (>= 2.5.0) 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Provision Hudson Slave Nodes with Fog (formerly hello world in ruby) 3 | 4 | This hudson plugin will allow you to spin up hudson slave nodes on-demand using the [Fog](http://github.com/geemus/fog) 5 | ruby library. 6 | 7 | It addresses my immediate need for a cloud plugin that I can maintain, but it also serves as a template / proving ground for 8 | authoring hudson plugins in ruby. 9 | 10 | # Hacking 11 | 12 | to run, you need a the plugin development maven setup. To summarize, add the following to `~/.m2/settings.xml` 13 | 14 | 15 | 16 | org.jvnet.hudson.tools 17 | 18 | 19 | 20 | check it out and run it: 21 | 22 | git clone git://github.com/cowboyd/fog.hpi.git 23 | cd fog.hpi 24 | mvn hpi:run 25 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.jenkins-ci.plugins 6 | plugin 7 | 1.406-SNAPSHOT 8 | 9 | 10 | thefrontside.ci 11 | fog 12 | 1.0-SNAPSHOT 13 | hpi 14 | 15 | 16 | 17 | 18 | org.jruby 19 | jruby-complete 20 | 1.6.1 21 | 22 | 23 | org.jenkins-ci 24 | jruby-xstream 25 | 1.0-SNAPSHOT 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | m.g.o-public 34 | http://maven.glassfish.org/content/groups/public/ 35 | 36 | 37 | codehaus 38 | http://repository.codehaus.org/ 39 | 40 | 41 | maven.jenkins-ci.org 42 | http://maven.jenkins-ci.org/content/repositories/snapshots/ 43 | 44 | true 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | m.g.o-public 53 | http://maven.glassfish.org/content/groups/public/ 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /spec/jenkins/model_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Jenkins::Model do 4 | 5 | it "has a display name which is settable via the class, and accessable via the class and instance" do 6 | cls = new_model do 7 | display_name "One-Off Class" 8 | end 9 | cls.display_name.should eql "One-Off Class" 10 | cls.new.display_name.should eql "One-Off Class" 11 | end 12 | 13 | it "passes down display_name capabilities to subclasses" do 14 | parent = new_model 15 | child = Class.new(parent) 16 | child.class_eval do 17 | display_name "Child" 18 | end 19 | child.display_name.should eql "Child" 20 | end 21 | 22 | it "has a default display name of the class name" do 23 | cls = new_model do 24 | def self.name 25 | "AwesomeClass" 26 | end 27 | end 28 | cls.display_name.should eql "AwesomeClass" 29 | end 30 | 31 | it "keeps a record of properties for all readers" do 32 | cls = new_model do 33 | attr_reader :foo, :bar, :baz 34 | end 35 | cls.properties.should eql [:foo, :bar, :baz] 36 | end 37 | 38 | it "keeps a record of all properties for all accessors" do 39 | cls = new_model do 40 | attr_reader :foo 41 | attr_accessor :bar, :baz 42 | attr_reader :bang 43 | end 44 | cls.properties.should eql [:foo, :bar, :baz, :bang] 45 | end 46 | 47 | it "includes parent classes's properties in the property list, but doesn't affect the parent property list" do 48 | parent = new_model do 49 | attr_reader :foo 50 | end 51 | child = Class.new(parent) 52 | child.class_eval do 53 | attr_reader :bar 54 | end 55 | parent.properties.should eq([:foo]) 56 | child.properties.should eql([:foo, :bar]) 57 | child.properties(false).should eql([:bar]) 58 | end 59 | 60 | private 61 | 62 | def new_model(&block) 63 | cls = Class.new 64 | cls.send(:include, Jenkins::Model) 65 | cls.class_eval(&block) if block_given? 66 | return cls 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | 2 | $:.unshift Pathname(__FILE__).dirname.join('../lib') 3 | 4 | require 'jenkins/model' -------------------------------------------------------------------------------- /src/main/java/ruby/RubyDoDynamic.java: -------------------------------------------------------------------------------- 1 | package ruby; 2 | 3 | 4 | import org.kohsuke.stapler.StaplerRequest; 5 | import org.kohsuke.stapler.StaplerResponse; 6 | 7 | /** 8 | * This interface is meant to be included by JRuby proxies so that they 9 | * can respond directly to stapler requests. 10 | * 11 | * If I understand correctly, stapler will see if the doDynamic 12 | * method exists and if so, dispatch it via that method. 13 | */ 14 | 15 | public interface RubyDoDynamic { 16 | 17 | void doDynamic(StaplerRequest request, StaplerResponse response); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/ruby/RubyExtensionFinder.java: -------------------------------------------------------------------------------- 1 | package ruby; 2 | 3 | 4 | import hudson.Extension; 5 | import hudson.ExtensionComponent; 6 | import hudson.ExtensionFinder; 7 | import hudson.model.Hudson; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Collection; 11 | 12 | 13 | /** 14 | * Presents Ruby extensions to Jenkins. 15 | * 16 | * Whenever a ruby plugin loads, it scans its codebase and finds all of the objects 17 | * which implement Jenkins extensions points (there can be any number of these per plugin) 18 | * 19 | * Sometime later, whenever Jenkins is asking about a particular extension type, like a 20 | * Publisher on BuildWrapper, it will query this ExtensionFinder among others. This finder then 21 | * delegates to the ruby plugin to see if it has any extensions of the requested type. 22 | * 23 | * @see hudson.ExtensionPoint 24 | */ 25 | 26 | @SuppressWarnings({"UnusedDeclaration"}) 27 | @Extension 28 | public class RubyExtensionFinder extends ExtensionFinder { 29 | 30 | @Override 31 | public Collection> find(Class type, Hudson hudson) { 32 | Collection> hits = new ArrayList>(); 33 | for (ExtensionComponent c: RubyPlugin.getExtensions()) { 34 | if (type.isAssignableFrom(c.getInstance().getClass())) { 35 | hits.add(c); 36 | } 37 | } 38 | return hits; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/ruby/RubyPlugin.java: -------------------------------------------------------------------------------- 1 | package ruby; 2 | 3 | import hudson.Extension; 4 | import hudson.ExtensionComponent; 5 | import hudson.Plugin; 6 | import hudson.model.Describable; 7 | import hudson.model.Descriptor; 8 | import hudson.model.Hudson; 9 | import hudson.model.Items; 10 | import hudson.util.XStream2; 11 | import org.jenkinsci.jruby.JRubyMapper; 12 | import org.jenkinsci.jruby.JRubyXStream; 13 | import org.jruby.embed.LocalContextScope; 14 | import org.jruby.embed.ScriptingContainer; 15 | 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | import java.util.ArrayList; 19 | import java.util.Collection; 20 | 21 | 22 | /** 23 | * The primary Java interface to a plugin which is implemented in Ruby 24 | * 25 | * When this plugin initializes, it will instantiate a Jenkins::Plugin 26 | * object which acts as the gateway for Ruby to interact with the java 27 | * side. 28 | * 29 | * When the RubyPlugin is loaded, it will discover, load and provide 30 | * a mechanism for extensions written in Ruby that it contains to register 31 | * themselves. 32 | * 33 | * These Extensions are presented to Jenkins via the {@link RubyExtensionFinder} 34 | * 35 | * Each plugin has its own JRuby environment 36 | */ 37 | @SuppressWarnings({"UnusedDeclaration"}) 38 | @Extension 39 | public class RubyPlugin extends Plugin implements Describable { 40 | /** 41 | * The unique JRuby environment used by this plugin and all the objects 42 | * and classes that it contains. 43 | */ 44 | private ScriptingContainer ruby; 45 | 46 | 47 | private Object plugin; 48 | private ArrayList extensions; 49 | 50 | public static RubyPlugin get() { 51 | return Hudson.getInstance().getPlugin(RubyPlugin.class); 52 | } 53 | 54 | /** 55 | * Kinda acts like the "agent" of this ruby plugin in the Ruby world. 56 | * This is the object that the internals of the ruby side talk to when 57 | * then want to talk back to Java. 58 | * @return an instance of Jenkins::Plugin 59 | */ 60 | public static Object getRubyController() { 61 | return get().plugin; 62 | } 63 | 64 | /** 65 | * invokes a Ruby method on the specified object in the context of this plugin's 66 | * {@link ScriptingContainer} 67 | * 68 | * @param object JRuby object to use as invocant 69 | * @param methodName the method to end 70 | * @param args arguments to the method 71 | * @return the return value of the method call. 72 | */ 73 | public static Object callMethod(Object object, String methodName, Object... args) { 74 | return RubyPlugin.get().ruby.callMethod(object, methodName, args); 75 | } 76 | 77 | /** 78 | * Registers an extenion with this Ruby plugin so that it will be found later on 79 | * 80 | * This method is generally called from inside Ruby, as objects that implement 81 | * extension points register themselves. 82 | * @param extension 83 | */ 84 | public void addExtension(Object extension) { 85 | extensions.add(new ExtensionComponent(extension)); 86 | } 87 | 88 | /** 89 | * @return the list of extensions registered with this Plugin. this is used by 90 | * the {@link RubyExtensionFinder} to present extension points to Jenkins 91 | */ 92 | public static Collection getExtensions() { 93 | return get().extensions; 94 | } 95 | 96 | /** 97 | * Reads a resource relative to this plugin's Java class using a formatted string 98 | * @param resource the string template specifying the resource 99 | * @param args format arguments 100 | * @return the content of the resource 101 | */ 102 | public static String readf(String resource, Object... args) { 103 | return RubyPlugin.get().read(String.format(resource, args)); 104 | } 105 | 106 | /** 107 | * Initializes this plugin by setting up the JRuby scripting container 108 | * and then loading up the ruby side of the plugin by creating an 109 | * instance of the Ruby class Jenkins::Plugin which will serve as 110 | * its agent in the Ruby world. 111 | * 112 | * We also register xstream mappers for JRuby objects so that they 113 | * can be persisted along with other objects in Jenkins. 114 | */ 115 | public RubyPlugin() { 116 | this.ruby = new ScriptingContainer(LocalContextScope.THREADSAFE); 117 | this.ruby.setClassLoader(this.getClass().getClassLoader()); 118 | this.ruby.getLoadPaths().add(0, this.getClass().getResource("support").getPath()); 119 | this.ruby.getLoadPaths().add(this.getClass().getResource("jenkins-plugins/lib").getPath()); 120 | this.ruby.getLoadPaths().add(this.getClass().getResource(".").getPath()); 121 | this.extensions = new ArrayList(); 122 | this.ruby.runScriptlet("require 'rubygems'"); 123 | this.ruby.runScriptlet("require 'bundled-gems.jar'"); 124 | this.ruby.runScriptlet("require 'jenkins/plugins'"); 125 | Object pluginClass = this.ruby.runScriptlet("Jenkins::Plugin"); 126 | this.plugin = this.ruby.callMethod(pluginClass, "new", this); 127 | 128 | register((XStream2)Hudson.XSTREAM, ruby); 129 | register((XStream2)Items.XSTREAM, ruby); 130 | } 131 | 132 | private void register(XStream2 xs, ScriptingContainer ruby) { 133 | JRubyXStream.register(xs,ruby); 134 | synchronized (xs) { 135 | xs.setMapper(new JRubyMapper(xs.getMapperInjectionPoint())); 136 | } 137 | } 138 | 139 | /** 140 | * Read a resource relative to this plugin clas 141 | * @param resource the name of the resource to be read 142 | * @return the content of the resource 143 | */ 144 | public String read(String resource) { 145 | InputStream stream = this.getClass().getResourceAsStream(resource); 146 | try { 147 | if (stream == null) { 148 | throw new RuntimeException("no such resource: " + resource); 149 | } 150 | StringBuffer buffer = new StringBuffer(); 151 | for (int c = stream.read(); c > 0; c = stream.read()) { 152 | buffer.append((char)c); 153 | } 154 | return buffer.toString(); 155 | } catch (IOException e) { 156 | throw new RuntimeException(e); 157 | } 158 | } 159 | 160 | /** 161 | * Jenkins will call this method whenever the plugin is initialized 162 | * The plugin will in turn delegate to its instance of Jenkins::Plugin 163 | * which can take action on the Ruby side. 164 | * @throws Exception 165 | */ 166 | @Override 167 | public void start() throws Exception { 168 | this.ruby.callMethod(plugin, "start"); 169 | } 170 | 171 | /** 172 | * Jenkins will call this method whenever the plugin is shut down 173 | * The plugin will in turn delegate to its instance of Jenkins::Plugin 174 | * which can take action on the Ruby side 175 | * @throws Exception 176 | */ 177 | @Override 178 | public void stop() throws Exception { 179 | this.ruby.callMethod(plugin, "stop"); 180 | } 181 | 182 | 183 | /** 184 | * This is mandatory for Jenkins to find this plugin, although I'm not 185 | * exactly sure why. 186 | * @return 187 | */ 188 | public DescriptorImpl getDescriptor() { 189 | return (DescriptorImpl)Hudson.getInstance().getDescriptorOrDie(getClass()); 190 | } 191 | 192 | public static String getResourceURI(String relativePathFormat, Object... args) { 193 | return get().getClass().getResource(String.format(relativePathFormat, args)).getPath(); 194 | } 195 | 196 | /** 197 | * Again, this is mandatory for Jenkins to find this plugin, although I'm not 198 | * exactly sure why. 199 | */ 200 | @Extension 201 | public static final class DescriptorImpl extends Descriptor { 202 | @Override 203 | public String getDisplayName() { 204 | return "Ruby Plugin"; 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/main/java/ruby/SimpleGet.java: -------------------------------------------------------------------------------- 1 | package ruby; 2 | 3 | 4 | /** 5 | * When stapler is querying a Java object to see which properties it has 6 | * it normally uses reflection to see if there is a field or getter with the 7 | * corresponding name which it can use. 8 | * 9 | * You obviously can't do this on a JRuby object, so instead Stapler and Jenkins 10 | * will look and see if it has a get(String) method and if so, use that for 11 | * property lookup. 12 | * 13 | * JRuby proxies include this interface to support this. 14 | */ 15 | public interface SimpleGet { 16 | 17 | Object get(String name); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/resources/index.jelly: -------------------------------------------------------------------------------- 1 |
2 | This plugin integrates Hudson with the Ruby cloud computing library, fog. 3 |
-------------------------------------------------------------------------------- /src/main/resources/ruby/plugin.rb: -------------------------------------------------------------------------------- 1 | 2 | # require 'fog' 3 | 4 | start do 5 | puts "starting Fog 0.4.0" 6 | end 7 | 8 | stop do 9 | 10 | end -------------------------------------------------------------------------------- /src/main/resources/ruby/plugin/models/fog_cloud.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class FogCloud < Jenkins::Slaves::Cloud 4 | 5 | attr_reader :name, :aws_access_id, :aws_secret_key 6 | 7 | display_name "Fog" 8 | 9 | def initialize 10 | puts "FogCloud#initialize: #{self.inspect}" 11 | end 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /src/main/resources/ruby/plugin/models/noop_wrapper.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class NoopWrapper < Jenkins::Tasks::BuildWrapper 4 | 5 | display_name "The Amazing Noop Wrapper" 6 | 7 | def setup(*args) 8 | puts "Hello from the NoopWrapper: (#{args.inspect})" 9 | return true 10 | end 11 | 12 | def teardown(build, listener) 13 | puts "Hello from the NoopWrapper.teardown: (#{build.inspect}, #{listener.inspect})" 14 | return true 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /src/main/resources/ruby/plugin/models/test_root_action.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'hudson/plugin/root_action' 3 | 4 | class TestRootAction < Hudson::Plugin::RootAction 5 | def icon_file_name 6 | "gear.png"; 7 | end 8 | 9 | def display_name 10 | "ERB Test - as a ruby object" 11 | end 12 | 13 | def url_name 14 | "erb" 15 | end 16 | end -------------------------------------------------------------------------------- /src/main/resources/ruby/plugin/views/fog_cloud/computerSet.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/main/resources/ruby/plugin/views/fog_cloud/config.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | <% f.entry :title => "Name", :field => "name" do %> 15 | <%= f.textbox %> 16 | <% end %> 17 | -------------------------------------------------------------------------------- /src/main/resources/ruby/plugin/views/fog_cloud/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/ruby/plugin/views/fog_cloud/global.jelly: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/main/resources/ruby/support/bundled-gems.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cowboyd/jenkins-ruby-plugins-playground/82e5abca45a4ac2a06c3bd753286dbf2877e0fe8/src/main/resources/ruby/support/bundled-gems.jar -------------------------------------------------------------------------------- /src/main/resources/ruby/support/hudson/plugin/build_wrapper.rb: -------------------------------------------------------------------------------- 1 | 2 | module Hudson 3 | module Plugin 4 | class BuildWrapper 5 | 6 | def self.inherited(cls) 7 | super(cls) 8 | cls.send(:extend, ClassMethods) 9 | end 10 | 11 | 12 | def setup(build, launcher, lister) 13 | raise "Clouds must implement can_provision?(label)" 14 | end 15 | 16 | class Wrapper < Java::HudsonTasks::BuildWrapper 17 | include Java::Ruby::SimpleGet 18 | include Java::Ruby::RubyDoDynamic 19 | 20 | def initialize(plugin, object) 21 | super() 22 | puts "Hudson::Plugin::BuildWrapper::Wrapper.new(#{plugin}, #{object})" 23 | @object = object 24 | end 25 | 26 | def setUp(build, launcher, listener) 27 | @object.setup(build, launcher, listener) 28 | Hudson::Plugin::BuildWrapper::EnvironmentWrapper.new(self) 29 | end 30 | 31 | def getDescriptor 32 | puts "object.class: #{@object.class} -> #{plugin.descriptors[@object.class]}" 33 | plugin.descriptors[@object.class] 34 | end 35 | 36 | def plugin 37 | Java::Ruby::RubyPlugin.getRubyController() 38 | end 39 | 40 | def unwrap 41 | @object 42 | end 43 | 44 | def get(name) 45 | @object.respond_to?(name) ? @object.send(name) : nil 46 | end 47 | 48 | def doDynamic(request, response) 49 | response.getWriter().println("Hello") 50 | end 51 | 52 | end 53 | 54 | 55 | class EnvironmentWrapper < Java::HudsonTasks::BuildWrapper::Environment 56 | 57 | def initialize(build_wrapper) 58 | super(build_wrapper) 59 | @build_wrapper = build_wrapper 60 | end 61 | 62 | def tearDown(*args) 63 | @build_wrapper.unwrap.teardown(*args) 64 | end 65 | end 66 | 67 | module ClassMethods 68 | def new(properties) 69 | allocate.tap do |instance| 70 | for k,v in properties 71 | instance.instance_variable_set("@#{k}", v) 72 | end 73 | instance.send(:initialize) 74 | puts "new instance created: #{instance.inspect}" 75 | $stdout.flush() 76 | end 77 | end 78 | end 79 | end 80 | end 81 | end -------------------------------------------------------------------------------- /src/main/resources/ruby/support/hudson/plugin/cloud.rb: -------------------------------------------------------------------------------- 1 | 2 | module Hudson 3 | module Plugin 4 | class Cloud 5 | 6 | def self.inherited(cls) 7 | super(cls) 8 | cls.send(:extend, ClassMethods) 9 | end 10 | 11 | 12 | def can_provision?(label) 13 | raise "Clouds must implement can_provision?(label)" 14 | end 15 | 16 | 17 | def provision(label, excess_workload) 18 | [] 19 | end 20 | 21 | class Wrapper < Java::HudsonSlaves::Cloud 22 | include Java::Ruby::SimpleGet 23 | include Java::Ruby::RubyDoDynamic 24 | 25 | def initialize(plugin, object) 26 | super(object.name) 27 | puts "Hudson::Plugin::Cloud::Wrapper.new(#{plugin}, #{object})" 28 | @plugin, @object = plugin, object 29 | end 30 | 31 | def canProvision(label) 32 | true if @object.can_provision?(@plugin.import(label)) 33 | end 34 | 35 | def provision(label, excess_workload) 36 | @plugin.export @object.provision(@plugin.import(label), excess_workload) 37 | end 38 | 39 | def getDescriptor 40 | puts "object.class: #{@object.class} -> #{@plugin.descriptors[@object.class]}" 41 | @plugin.descriptors[@object.class] 42 | end 43 | 44 | def unwrap 45 | @object 46 | end 47 | 48 | def get(name) 49 | @object.respond_to?(name) ? @object.send(name) : nil 50 | end 51 | 52 | def doDynamic(request, response) 53 | response.getWriter().println("Hello") 54 | end 55 | 56 | end 57 | 58 | module ClassMethods 59 | def new(properties) 60 | allocate.tap do |instance| 61 | for k,v in properties 62 | instance.instance_variable_set("@#{k}", v) 63 | end 64 | instance.send(:initialize) 65 | puts "new instance created: #{instance.inspect}" 66 | $stdout.flush() 67 | end 68 | end 69 | end 70 | end 71 | end 72 | end -------------------------------------------------------------------------------- /src/main/resources/ruby/support/hudson/plugin/controller.rb: -------------------------------------------------------------------------------- 1 | 2 | module Hudson 3 | module Plugin 4 | 5 | class Controller 6 | attr_reader :descriptors 7 | 8 | def initialize(java) 9 | @java = java 10 | @start = @stop = proc {} 11 | @descriptors = {} 12 | @wrappers = {} 13 | require 'bundled-gems.jar' 14 | require 'rubygems' 15 | script = 'support/hudson/plugin/models.rb' 16 | self.instance_eval @java.read(script), script 17 | DSL.new(self) do |dsl| 18 | script = @java.read("plugin.rb") 19 | dsl.instance_eval(script, "plugin.rb") 20 | end 21 | end 22 | 23 | def start 24 | @start.call() 25 | end 26 | 27 | def stop 28 | @stop.call() 29 | end 30 | 31 | def import(object) 32 | object.respond_to?(:unwrap) ? object.unwrap : object 33 | end 34 | 35 | def export(object) 36 | puts "export(#{object})" 37 | return @wrappers[object] if @wrappers[object] 38 | case object 39 | when Hudson::Plugin::Cloud 40 | puts "it's a cloud, I'm going to wrap it" 41 | wrapper = Hudson::Plugin::Cloud::Wrapper.new(self, object) 42 | puts "wrapper created: #{wrapper}" 43 | return wrapper 44 | when Hudson::Plugin::BuildWrapper 45 | puts "it's a build wrapper, I'm going to wrap it" 46 | wrapper = Hudson::Plugin::BuildWrapper::Wrapper.new(self, object) 47 | else object 48 | end 49 | end 50 | 51 | private 52 | 53 | class DSL 54 | def initialize(controller) 55 | @controller = controller 56 | yield self if block_given? 57 | end 58 | 59 | def start(&impl) 60 | @controller.instance_variable_set(:@start, impl) if impl 61 | end 62 | 63 | def stop(&impl) 64 | @controller.instance_variable_set(:@stop, impl) if impl 65 | end 66 | end 67 | 68 | end 69 | end 70 | end -------------------------------------------------------------------------------- /src/main/resources/ruby/support/hudson/plugin/descriptor.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'json' 3 | 4 | module Hudson 5 | module Plugin 6 | class Descriptor < Java::HudsonModel::Descriptor 7 | 8 | def initialize(name, impl, plugin, java_type) 9 | super(Java::OrgJruby::RubyObject.java_class) 10 | @name, @impl, @plugin, @java_type = name, impl, plugin, java_type 11 | end 12 | 13 | def getDisplayName 14 | @impl.display_name 15 | end 16 | 17 | def getT() 18 | @java_type 19 | end 20 | 21 | def getConfigPage 22 | "/ruby/plugin/views/#{@name}/config.jelly".tap do |path| 23 | puts "getConfigPage -> #{path}" 24 | end 25 | end 26 | 27 | def getGlobalConfigPage 28 | "/ruby/plugin/views/#{@name}/global.jelly".tap do |path| 29 | puts "getGlobalConfigPage -> #{path}" 30 | end 31 | end 32 | 33 | def newInstance(request, form) 34 | properties = JSON.parse(form.toString(2)) 35 | properties.delete("kind") 36 | properties.delete("stapler-class") 37 | instance = @plugin.export(@impl.new(properties)) 38 | puts "instance created: #{instance}" 39 | return instance 40 | end 41 | 42 | end 43 | 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /src/main/resources/ruby/support/hudson/plugin/models.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | Jenkins::Model::Descriptor.new("fog_cloud", FogCloud, self, Java::hudson.slaves.Cloud.java_class).tap do |d| 4 | @java.addExtension(d) 5 | descriptors[FogCloud] = d 6 | end 7 | 8 | Jenkins::Model::Descriptor.new("noop_wrapper", NoopWrapper, self, Java::hudson.tasks.BuildWrapper.java_class).tap do |d| 9 | @java.addExtension(d) 10 | descriptors[NoopWrapper] = d 11 | end 12 | 13 | 14 | Jenkins::Model::Descriptor.new("test_root_action", TestRootAction, self, Java::hudson.model.RootAction.java_class).tap do |d| 15 | @java.addExtension(d) 16 | descriptors[TestRootAction] = d 17 | end 18 | 19 | TestRootAction.new.tap do |action| 20 | @java.addExtension(@wrappers[action] = Hudson::Plugin::RootAction::Wrapper.new(action)) 21 | end 22 | -------------------------------------------------------------------------------- /src/main/resources/ruby/support/hudson/plugin/root_action.rb: -------------------------------------------------------------------------------- 1 | 2 | module Hudson 3 | module Plugin 4 | class RootAction 5 | 6 | class Wrapper 7 | include Java::HudsonModel::RootAction 8 | include Java::Ruby::SimpleGet 9 | include Java::OrgKohsukeStapler::StaplerProxy 10 | 11 | def initialize(object) 12 | puts "Hudson::Plugin::RootAction::Wrapper.new(#{object})" 13 | @object = object 14 | end 15 | 16 | def getIconFileName() 17 | @object.icon_file_name 18 | end 19 | 20 | def getDisplayName 21 | @object.display_name 22 | end 23 | 24 | def getUrlName 25 | @object.url_name 26 | end 27 | 28 | def unwrap 29 | @object 30 | end 31 | 32 | def get(name) 33 | @object.respond_to?(name) ? @object.send(name) : nil 34 | end 35 | 36 | def getTarget() 37 | @object 38 | end 39 | end 40 | 41 | end 42 | end 43 | end -------------------------------------------------------------------------------- /src/main/resources/test_root_action/call-tags.erb: -------------------------------------------------------------------------------- 1 | <% l = taglib("/lib/layout") %> 2 | <% l.layout(:title => "Hello from ERB") do %> 3 | <% l.main_panel do %> 4 |

Hello from ERB

5 |

6 | With proper Jenkins UI tags 7 | <%= it %> 8 |

9 | 12 | <% end %> 13 | <% end %> -------------------------------------------------------------------------------- /src/main/resources/test_root_action/index.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | I am <%= it.class %>!! 4 | <%= 1+1 %> 5 | 6 | -------------------------------------------------------------------------------- /src/main/webapp/help-globalConfig.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | This HTML fragment will be injected into the configuration screen 4 | when the user clicks the 'help' icon. See global.jelly for how the 5 | form decides which page to load. 6 | You can have any HTML fragment here. 7 |

8 |
9 | --------------------------------------------------------------------------------