jenkins-plugin-runtime 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +2 -0
  3. data/Gemfile +4 -0
  4. data/Rakefile +21 -0
  5. data/jenkins-plugin-runtime.gemspec +30 -0
  6. data/lib/jenkins/launcher.rb +15 -0
  7. data/lib/jenkins/model.rb +91 -0
  8. data/lib/jenkins/model/action.rb +37 -0
  9. data/lib/jenkins/model/build.rb +21 -0
  10. data/lib/jenkins/model/describable.rb +75 -0
  11. data/lib/jenkins/model/descriptor.rb +40 -0
  12. data/lib/jenkins/model/listener.rb +47 -0
  13. data/lib/jenkins/plugin.rb +141 -0
  14. data/lib/jenkins/plugin/cli.rb +20 -0
  15. data/lib/jenkins/plugin/proxies.rb +131 -0
  16. data/lib/jenkins/plugin/proxies/build_wrapper.rb +50 -0
  17. data/lib/jenkins/plugin/proxies/builder.rb +22 -0
  18. data/lib/jenkins/plugin/proxy.rb +76 -0
  19. data/lib/jenkins/plugin/runtime.rb +13 -0
  20. data/lib/jenkins/plugin/runtime/version.rb +7 -0
  21. data/lib/jenkins/slaves/cloud.rb +9 -0
  22. data/lib/jenkins/tasks/build_wrapper.rb +39 -0
  23. data/lib/jenkins/tasks/builder.rb +33 -0
  24. data/spec/jenkins/launcher_spec.rb +8 -0
  25. data/spec/jenkins/model/action_spec.rb +36 -0
  26. data/spec/jenkins/model/build_spec.rb +8 -0
  27. data/spec/jenkins/model/describable_spec.rb +51 -0
  28. data/spec/jenkins/model/listener_spec.rb +31 -0
  29. data/spec/jenkins/model_spec.rb +100 -0
  30. data/spec/jenkins/plugin/proxies/build_wrapper_spec.rb +25 -0
  31. data/spec/jenkins/plugin/proxies/builder_spec.rb +24 -0
  32. data/spec/jenkins/plugin/proxies/proxy_helper.rb +24 -0
  33. data/spec/jenkins/plugin/proxies_spec.rb +148 -0
  34. data/spec/jenkins/plugin/proxy_spec.rb +43 -0
  35. data/spec/jenkins/plugin_spec.rb +7 -0
  36. data/spec/jenkins/tasks/build_wrapper_spec.rb +7 -0
  37. data/spec/jenkins/tasks/builder_spec.rb +8 -0
  38. data/spec/spec_helper.rb +11 -0
  39. data/src/jenkins/ruby/DoDynamic.java +18 -0
  40. data/src/jenkins/ruby/Get.java +18 -0
  41. metadata +175 -0
@@ -0,0 +1,20 @@
1
+
2
+ require 'thor'
3
+
4
+ module Jenkins
5
+ module Plugins
6
+ class CLI < Thor
7
+
8
+
9
+ desc "init", "Create a new Jenkins plugin"
10
+ def init
11
+
12
+ end
13
+
14
+ desc "gen", "generate extension boilerplate"
15
+ def gen
16
+
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,131 @@
1
+
2
+
3
+ module Jenkins
4
+ class Plugin
5
+
6
+ ExportError = Class.new(StandardError)
7
+ ImportError = Class.new(StandardError)
8
+
9
+ # Maps JRuby objects part of the idomatic Ruby API
10
+ # to a plain Java object representation and vice-versa.
11
+ #
12
+ # One of the pillars of Jenkins Ruby plugins is that writing
13
+ # plugins must "feel" like native Ruby, and not merely like
14
+ # scripting Java with Ruby. To this end, jenkins-plugins provides
15
+ # a idiomatic Ruby API that sits on top of the native Jenkins
16
+ # Java API with which plugin developers can interact.
17
+ #
18
+ # This has two consequences. Native Ruby objects authored as part
19
+ # of the plugin must have a foreign (Java) representation with which
20
+ # they can wander about the Jenkins universe and possibly interact
21
+ # with other foreign objects and APIs. Also, Foreign objects
22
+ # coming in from Jenkins at large should be wrapped, where possible
23
+ # to present an idomatic interface..
24
+ #
25
+ # Finally, Native plugin that had been wrapped and are comping home
26
+ # must be unwrapped from their external form.
27
+ #
28
+ # For all cases, we want to maintain referential integrety so that
29
+ # the same object always uses the same external form, etc... so
30
+ # there is one instance of the `Proxies` class per plugin which will
31
+ # reuse mappings where possible.
32
+ class Proxies
33
+
34
+ def initialize(plugin)
35
+ @plugin = plugin
36
+ @int2ext = java.util.WeakHashMap.new
37
+ @ext2int = java.util.WeakHashMap.new
38
+ end
39
+
40
+ # Reflect a foreign Java object into the context of this plugin.
41
+ #
42
+ # If the object is a native plugin object that had been previously
43
+ # exported, then it will unwrapped.
44
+ #
45
+ # Otherwise, we try to choose the best idiomatic API object for
46
+ # this foreign object
47
+ #
48
+ # @param [Object] object the object to bring in from the outside
49
+ # @return the best representation of that object for this plugin
50
+ def import(object)
51
+ if ref = @ext2int[object]
52
+ return ref.get() if ref.get()
53
+ end
54
+ cls = object.class
55
+ while cls do
56
+ if internal_class = @@extcls2intcls[cls]
57
+ internal = internal_class.new(object)
58
+ link(internal, object)
59
+ return internal
60
+ end
61
+ cls = cls.superclass
62
+ end
63
+ raise ImportError, "unable to find suitable representation for #{object.inspect}"
64
+ end
65
+
66
+ # Reflect a native Ruby object into its External Java form.
67
+ #
68
+ # Try to find a suitable form for this object and if one is found then decorate it.
69
+ # @param [Object] object the ruby object that is being exported to Java
70
+ # @return [java.lang.Object] the Java wrapper that provides an interface to `object`
71
+ # @throw [ExportError] if no suitable Java representation can be found
72
+ def export(object)
73
+ if ref = @int2ext[object]
74
+ return ref.get() if ref.get()
75
+ end
76
+
77
+ cls = object.class
78
+ while cls do
79
+ if proxy_class = @@intcls2extcls[cls]
80
+ proxy = proxy_class.new(@plugin, object)
81
+ link(object, proxy)
82
+ return proxy
83
+ end
84
+ cls = cls.superclass
85
+ end
86
+ raise ExportError, "unable to find suitable Java Proxy for #{object.inspect}"
87
+ end
88
+
89
+ ##
90
+ # Link a plugin-local Ruby object to an external Java object such that they will
91
+ # be subsituted for one another when passing values back and forth between Jenkins
92
+ # and this plugin. An example of this is associating the Ruby Jenkins::Launcher object
93
+ # with an equivalent
94
+ #
95
+ # @param [Object] internal the object on the Ruby side of the link
96
+ # @param [java.lang.Object] external the object on the Java side of the link
97
+ def link(internal, external)
98
+ @int2ext.put(internal, java.lang.ref.WeakReference.new(external))
99
+ @ext2int.put(external, java.lang.ref.WeakReference.new(internal))
100
+ end
101
+
102
+ ##
103
+ # Associated the the Ruby class `internal_class` with the Java class `external_class`.
104
+ #
105
+ # Whenever a plugin is importing or exporting an object to the other side, it will first
106
+ # see if there is an instance already linked. If not, it will try to create the other side
107
+ # of the link by constructing it via reflection. `register` links two classes together so
108
+ # that links can be built automatically.
109
+ #
110
+ # @param [Class] internal_class the Ruby class
111
+ # @param [java.lang.Class] external_class the Java class on the othe side of this link.
112
+ def self.register(internal_class, external_class)
113
+ @@intcls2extcls[internal_class] = external_class
114
+ @@extcls2intcls[external_class] = internal_class
115
+ end
116
+
117
+ ##
118
+ # Remove all class linkages. This is mainly for testing purposes.
119
+ def self.clear
120
+ @@intcls2extcls = {}
121
+ @@extcls2intcls = {}
122
+ end
123
+ clear
124
+ end
125
+ end
126
+ end
127
+
128
+ require 'jenkins/model/describable'
129
+ ["build_wrapper", "builder"].each do |proxy|
130
+ require "jenkins/plugin/proxies/#{proxy}"
131
+ end
@@ -0,0 +1,50 @@
1
+
2
+ require 'jenkins/tasks/build_wrapper'
3
+
4
+ module Jenkins
5
+ class Plugin
6
+ class Proxies
7
+
8
+ ##
9
+ # Binds the Java hudson.tasks.BuildWrapper API to the idomatic
10
+ # Ruby API Jenkins::Tasks::BuildWrapper
11
+
12
+ class BuildWrapper < Java.hudson.tasks.BuildWrapper
13
+ include Java.jenkins.ruby.Get
14
+ include Jenkins::Plugin::Proxy
15
+
16
+ def setUp(build, launcher, listener)
17
+ env = {}
18
+ @object.setup(import(build), import(launcher), import(listener), env)
19
+ EnvironmentWrapper.new(self, @plugin, @object, env)
20
+ end
21
+
22
+ def getDescriptor
23
+ @plugin.descriptors[@object.class]
24
+ end
25
+
26
+ def get(name)
27
+ @object.respond_to?(name) ? @object.send(name) : nil
28
+ end
29
+
30
+ end
31
+
32
+
33
+ class EnvironmentWrapper < Java.hudson.tasks.BuildWrapper::Environment
34
+
35
+ def initialize(build_wrapper, plugin, impl, env)
36
+ super(build_wrapper)
37
+ @plugin = plugin
38
+ @impl = impl
39
+ @env = env
40
+ end
41
+
42
+ def tearDown(build, listener)
43
+ @impl.teardown(@plugin.import(build), @plugin.import(listener), @env)
44
+ end
45
+ end
46
+
47
+ register Jenkins::Tasks::BuildWrapper, BuildWrapper
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,22 @@
1
+
2
+ require 'jenkins/tasks/builder'
3
+
4
+ module Jenkins
5
+ class Plugin
6
+ class Proxies
7
+ class Builder < Java.hudson.tasks.Builder
8
+ include Jenkins::Plugin::Proxy
9
+
10
+ def prebuild(build, launcher, listener)
11
+ @object.prebuild(import(build), import(launcher), import(listener)) ? true : false
12
+ end
13
+
14
+ def perform(build, launcher, listener)
15
+ @object.perform(import(build), import(launcher), import(listener)) ? true : false
16
+ end
17
+
18
+ end
19
+ register Jenkins::Tasks::Builder, Builder
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,76 @@
1
+
2
+ module Jenkins
3
+ class Plugin
4
+
5
+ ##
6
+ # The Jenkins Ruby API uses "proxies" which are Java subclasses of the native Jenkins
7
+ # Java API. These proxies provide the mapping between the Java API and the idomatic
8
+ # Ruby API. Sometimes these mappings can appear convoluted, but it is only so in order to make
9
+ # the Ruby side as simple and clean as possible.
10
+ #
11
+ # This module provides common functionality for all proxies.
12
+ module Proxy
13
+ def self.included(mod)
14
+ super
15
+ mod.extend(Marshal)
16
+ mod.send(:include, Unmarshal)
17
+ mod.send(:include, Customs)
18
+ end
19
+
20
+ # Every Proxy object has a reference to the plugin to which it belongs, as well as the
21
+ # native Ruby object which it represents.
22
+ #
23
+ # @param [Jenkins::Plugin] plugin the plugin from whence this proxy object came
24
+ # @param [Object] object the implementation to which this proxy will delegate.
25
+ def initialize(plugin, object)
26
+ super() if defined? super
27
+ @plugin, @object = plugin, object
28
+ @pluginid = @plugin.name
29
+ end
30
+
31
+ # Make sure that proxy classes do not try to persist the plugin parameter.
32
+ # when serializing this proxy to XStream. It will be reconstructed with
33
+ # [Unmarshal#read_completed]
34
+ module Marshal
35
+
36
+ # Tell XStream that we never want to persist the @plugin field
37
+ # @param [String] field name of the field which xstream is enquiring about
38
+ # @return [Boolean] true if this is the plugin field, otherwise delegate
39
+ def transient?(field)
40
+ field.to_s == "plugin" or (super if defined? super)
41
+ end
42
+ end
43
+
44
+ # Reanimates Proxy objects after their values have been unserialized from XStream
45
+ module Unmarshal
46
+
47
+ # Once the proxy has been unmarshalled from XStream, re-find the plugin
48
+ # that it is associated with, and use it to populate the @plugin field.
49
+ # Also, make sure to associate this proxy with the object it represents
50
+ # so that they remain referentially equivalent.
51
+ def read_completed
52
+ @plugin = Java.jenkins.model.Jenkins.getInstance().getPlugin(@pluginid).getNativeRubyPlugin()
53
+ @plugin.link @object, self
54
+ end
55
+
56
+ end
57
+
58
+ ##
59
+ # Convenience methods for converting from Ruby API to Java API objects and back
60
+ module Customs
61
+
62
+ ##
63
+ # convert an external Java object into a Ruby friendly object
64
+ def import(object)
65
+ @plugin.import(object)
66
+ end
67
+
68
+ ##
69
+ # convert an internal Ruby object into a Java proxy that is free to roam about Jenkins-land
70
+ def export(object)
71
+ @plugin.export(object)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,13 @@
1
+ require 'jenkins/plugin'
2
+ require 'jenkins/plugin/runtime/version'
3
+ require 'jenkins/plugin/proxy'
4
+ require 'jenkins/plugin/proxies'
5
+ require 'jenkins/model'
6
+ require 'jenkins/model/action'
7
+ require 'jenkins/model/build'
8
+ require 'jenkins/model/descriptor'
9
+ require 'jenkins/model/listener'
10
+ require 'jenkins/slaves/cloud'
11
+ require 'jenkins/tasks/builder'
12
+ require 'jenkins/tasks/build_wrapper'
13
+ require 'jenkins/launcher'
@@ -0,0 +1,7 @@
1
+ module Jenkins
2
+ class Plugin
3
+ module Runtime
4
+ VERSION = "0.1.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+
2
+ module Jenkins
3
+ module Slaves
4
+ class Cloud
5
+ include Jenkins::Model
6
+
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,39 @@
1
+
2
+ require 'jenkins/model'
3
+
4
+ module Jenkins
5
+ module Tasks
6
+
7
+ # Decorate a build with pre and post hooks.
8
+ # {http://javadoc.jenkins-ci.org/hudson/tasks/BuildWrapper.html}
9
+ class BuildWrapper
10
+ include Jenkins::Model
11
+ include Jenkins::Model::Describable
12
+
13
+ describe_as Java.hudson.tasks.BuildWrapper
14
+
15
+ # Perform setup for a build
16
+ #
17
+ # invoked after checkout, but before any `Builder`s have been run
18
+ # @param [Jenkins::Model::Build] build the build about to run
19
+ # @param [Jenkins::Launcher] launcher a launcher for the orderly starting/stopping of processes.
20
+ # @param [Jenkins::Model::Listener] listener channel for interacting with build output console
21
+ # @param [Hash] env a place to store information needed by #teardown
22
+ def setup(build, launcher, listener, env)
23
+
24
+ end
25
+
26
+ # Optionally perform optional teardown for a build
27
+ #
28
+ # invoked after a build has run for better or for worse. It's ok if subclasses
29
+ # don't override this.
30
+ #
31
+ # @param [Jenkins::Model::Build] the build which has completed
32
+ # @param [Jenkins::Model::Listener] listener channel for interacting with build output console
33
+ # @param [Hash] env contains anything that #setup needs to tell #teardown about
34
+ def teardown(build, listener, env)
35
+
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+
2
+ module Jenkins
3
+ module Tasks
4
+ ##
5
+ # A single step in the entire build process
6
+ class Builder
7
+
8
+ ##
9
+ # Runs before the build begins
10
+ #
11
+ # @param [Jenkins::Model::Build] build the build which will begin
12
+ # @param [Jenkins::Launcher] launcher the launcher that can run code on the node running this build
13
+ # @param [Jenkins::Model::Listener] listener the listener for this build.
14
+ # @return `true` if this build can continue or `false` if there was an error
15
+ # and the build needs to be aborted
16
+ def prebuild(build, launcher, listener)
17
+
18
+ end
19
+
20
+ ##
21
+ # Runs the step over the given build and reports the progress to the listener.
22
+ #
23
+ # @param [Jenkins::Model::Build] build on which to run this step
24
+ # @param [Jenkins::Launcher] launcher the launcher that can run code on the node running this build
25
+ # @param [Jenkins::Model::Listener] listener the listener for this build.
26
+ # return `true if this build can continue or `false` if there was an error
27
+ # and the build needs to be aborted
28
+ def perform(build, launcher, listener)
29
+
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Jenkins::Launcher do
4
+
5
+ it "can be instantiated" do
6
+ Jenkins::Launcher.new
7
+ end
8
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+ require 'jenkins/model/action'
3
+ describe Jenkins::Model::Action do
4
+
5
+ it "has the same display_name semantics as Model" do
6
+ a = new_action do
7
+ display_name "CoolAction"
8
+ end
9
+ a.display_name.should eql "CoolAction"
10
+ end
11
+
12
+ describe "its icon_file" do
13
+ it "is nil by default" do
14
+ new_action.icon.should be_nil
15
+ end
16
+ it "can be configured" do
17
+ action = new_action do
18
+ icon "foo.png"
19
+ end
20
+ action.new.icon.should == "foo.png"
21
+ end
22
+ end
23
+
24
+ describe "url_name" do
25
+ it "can be configured"
26
+ end
27
+
28
+ private
29
+
30
+ def new_action(&body)
31
+ action = Class.new
32
+ action.send(:include, Jenkins::Model::Action)
33
+ action.class_eval(&body) if block_given?
34
+ return action
35
+ end
36
+ end