jenkins-plugin-runtime 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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