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.
- data/.gitignore +7 -0
- data/.travis.yml +2 -0
- data/Gemfile +4 -0
- data/Rakefile +21 -0
- data/jenkins-plugin-runtime.gemspec +30 -0
- data/lib/jenkins/launcher.rb +15 -0
- data/lib/jenkins/model.rb +91 -0
- data/lib/jenkins/model/action.rb +37 -0
- data/lib/jenkins/model/build.rb +21 -0
- data/lib/jenkins/model/describable.rb +75 -0
- data/lib/jenkins/model/descriptor.rb +40 -0
- data/lib/jenkins/model/listener.rb +47 -0
- data/lib/jenkins/plugin.rb +141 -0
- data/lib/jenkins/plugin/cli.rb +20 -0
- data/lib/jenkins/plugin/proxies.rb +131 -0
- data/lib/jenkins/plugin/proxies/build_wrapper.rb +50 -0
- data/lib/jenkins/plugin/proxies/builder.rb +22 -0
- data/lib/jenkins/plugin/proxy.rb +76 -0
- data/lib/jenkins/plugin/runtime.rb +13 -0
- data/lib/jenkins/plugin/runtime/version.rb +7 -0
- data/lib/jenkins/slaves/cloud.rb +9 -0
- data/lib/jenkins/tasks/build_wrapper.rb +39 -0
- data/lib/jenkins/tasks/builder.rb +33 -0
- data/spec/jenkins/launcher_spec.rb +8 -0
- data/spec/jenkins/model/action_spec.rb +36 -0
- data/spec/jenkins/model/build_spec.rb +8 -0
- data/spec/jenkins/model/describable_spec.rb +51 -0
- data/spec/jenkins/model/listener_spec.rb +31 -0
- data/spec/jenkins/model_spec.rb +100 -0
- data/spec/jenkins/plugin/proxies/build_wrapper_spec.rb +25 -0
- data/spec/jenkins/plugin/proxies/builder_spec.rb +24 -0
- data/spec/jenkins/plugin/proxies/proxy_helper.rb +24 -0
- data/spec/jenkins/plugin/proxies_spec.rb +148 -0
- data/spec/jenkins/plugin/proxy_spec.rb +43 -0
- data/spec/jenkins/plugin_spec.rb +7 -0
- data/spec/jenkins/tasks/build_wrapper_spec.rb +7 -0
- data/spec/jenkins/tasks/builder_spec.rb +8 -0
- data/spec/spec_helper.rb +11 -0
- data/src/jenkins/ruby/DoDynamic.java +18 -0
- data/src/jenkins/ruby/Get.java +18 -0
- metadata +175 -0
@@ -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,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,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
|