jenkins-plugin-runtime 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
require 'jenkins/war'
|
8
|
+
Jenkins::War.classpath
|
9
|
+
ClassPath = FileList[File.join(ENV['HOME'], '.jenkins', 'wars', Jenkins::War::VERSION, "**/*.jar")].to_a.join(':')
|
10
|
+
|
11
|
+
desc "compile java source code"
|
12
|
+
task "compile" => "target" do
|
13
|
+
puts command = "javac -classpath #{ClassPath} #{FileList['src/**/*.java']} -d target"
|
14
|
+
system(command)
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'rake/clean'
|
18
|
+
directory "target"
|
19
|
+
CLEAN.include("target")
|
20
|
+
|
21
|
+
task :default => [:compile, :spec]
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "jenkins/plugin/runtime/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "jenkins-plugin-runtime"
|
7
|
+
s.version = Jenkins::Plugin::Runtime::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Charles Lowell"]
|
10
|
+
s.email = ["cowboyd@thefrontside.net"]
|
11
|
+
s.homepage = "http://github.com/cowboyd/jenkins-plugins.rb"
|
12
|
+
s.summary = %q{Runtime support libraries for Jenkins Ruby plugins}
|
13
|
+
s.description = %q{I'll think of a better description later, but if you're reading this, then I haven't}
|
14
|
+
|
15
|
+
s.rubyforge_project = "jenkins-plugin-runtime"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency "json"
|
23
|
+
|
24
|
+
s.add_development_dependency "rake"
|
25
|
+
s.add_development_dependency "rspec", "~> 2.0"
|
26
|
+
s.add_development_dependency "rspec-spies"
|
27
|
+
s.add_development_dependency "cucumber", "~> 1.0"
|
28
|
+
s.add_development_dependency "jenkins-war"
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
module Jenkins
|
3
|
+
|
4
|
+
# Launch processes on build slaves. No functionality is currently exposed
|
5
|
+
class Launcher
|
6
|
+
# the nantive hudson.Launcher object
|
7
|
+
attr_reader :native
|
8
|
+
|
9
|
+
def initialize(native = nil)
|
10
|
+
@native = native
|
11
|
+
end
|
12
|
+
|
13
|
+
Plugin::Proxies.register self, Java.hudson.Launcher
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
|
2
|
+
module Jenkins
|
3
|
+
module Model
|
4
|
+
|
5
|
+
module Included
|
6
|
+
def included(cls)
|
7
|
+
super(cls)
|
8
|
+
if cls.class == Module
|
9
|
+
cls.extend(Included)
|
10
|
+
else
|
11
|
+
cls.extend(Inherited)
|
12
|
+
cls.extend(ClassDisplayName)
|
13
|
+
cls.extend(Transience)
|
14
|
+
cls.send(:include, InstanceDisplayName)
|
15
|
+
end
|
16
|
+
Model.descendant(cls)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
extend Included
|
20
|
+
|
21
|
+
module Inherited
|
22
|
+
def inherited(cls)
|
23
|
+
super(cls)
|
24
|
+
Model.descendant(cls)
|
25
|
+
cls.extend(Inherited)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module InstanceDisplayName
|
30
|
+
# Get the display name of this Model. This value will be used as a default
|
31
|
+
# whenever this model needs to be shown in the UI. If no display name has
|
32
|
+
# been set, then it will use the Model's class name.
|
33
|
+
#
|
34
|
+
# @return [String] the display name
|
35
|
+
def display_name
|
36
|
+
self.class.display_name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module ClassDisplayName
|
41
|
+
|
42
|
+
# Set or get the display name of this Model Class.
|
43
|
+
#
|
44
|
+
# If `name` is not nil, then sets the display name.
|
45
|
+
# @return [String] the display name
|
46
|
+
def display_name(name = nil)
|
47
|
+
name.nil? ? @display_name || self.name : @display_name = name.to_s
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module Transience
|
52
|
+
|
53
|
+
# Mark a set of properties that should not be persisted as part of this Model's lifecycle.
|
54
|
+
#
|
55
|
+
# Jenkins supports transparent persistent
|
56
|
+
def transient(*properties)
|
57
|
+
properties.each do |p|
|
58
|
+
transients[p.to_sym] = true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def transient?(property)
|
63
|
+
transients.keys.member?(property.to_sym) || (superclass < Model && superclass.transient?(property))
|
64
|
+
end
|
65
|
+
|
66
|
+
def transients
|
67
|
+
@transients ||= {}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module Descendants
|
72
|
+
def descendant(mod)
|
73
|
+
@descendants ||= clear
|
74
|
+
@descendants[mod] = true
|
75
|
+
end
|
76
|
+
|
77
|
+
def descendants
|
78
|
+
@descendants.keys
|
79
|
+
end
|
80
|
+
|
81
|
+
def clear
|
82
|
+
@descendants = {}
|
83
|
+
end
|
84
|
+
|
85
|
+
def descendant?(cls)
|
86
|
+
@descendants[cls]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
extend Descendants
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
module Jenkins
|
3
|
+
module Model
|
4
|
+
module Action
|
5
|
+
include Model
|
6
|
+
|
7
|
+
module Included
|
8
|
+
def included(cls)
|
9
|
+
super(cls)
|
10
|
+
if cls.class == Module
|
11
|
+
cls.extend(Included)
|
12
|
+
else
|
13
|
+
cls.extend(ClassMethods)
|
14
|
+
cls.send(:include, InstanceMethods)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
extend Included
|
19
|
+
|
20
|
+
# def included(cls)
|
21
|
+
# super(cls)
|
22
|
+
# cls.send(:include)
|
23
|
+
# end
|
24
|
+
module InstanceMethods
|
25
|
+
def icon
|
26
|
+
self.class.icon
|
27
|
+
end
|
28
|
+
end
|
29
|
+
#
|
30
|
+
module ClassMethods
|
31
|
+
def icon(path = nil)
|
32
|
+
path.nil? ? @path : @path = path.to_s
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
module Jenkins
|
3
|
+
module Model
|
4
|
+
##
|
5
|
+
# Represents a single build. In general, you won't need this
|
6
|
+
#
|
7
|
+
class Build
|
8
|
+
|
9
|
+
##
|
10
|
+
# the Hudson::Model::AbstractBuild represented by this build
|
11
|
+
attr_reader :native
|
12
|
+
|
13
|
+
def initialize(native = nil)
|
14
|
+
@native = native
|
15
|
+
end
|
16
|
+
|
17
|
+
Jenkins::Plugin::Proxies.register self, Java.hudson.model.AbstractBuild
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
|
2
|
+
module Jenkins
|
3
|
+
module Model
|
4
|
+
|
5
|
+
# Register a Ruby class as a Jenkins extension point
|
6
|
+
|
7
|
+
# When Jenkins is searching for all the extension points of a particular
|
8
|
+
# type... let's say `Builder` for example. It first asks for all the `Descriptor`s
|
9
|
+
# registered to describe the `Builder` type. It will then use the descriptors
|
10
|
+
# it finds to do things like construct and validate the extension objects.
|
11
|
+
#
|
12
|
+
# It helps me to think about the Jenkins `Descriptor` as a Ruby class. It is a
|
13
|
+
# factory for instances, and contains data about what those instances can do.
|
14
|
+
#
|
15
|
+
# This module, when included, provides a single class method `describe_as`
|
16
|
+
# which tell Jenkins in effect "when you are looking for Java Classes of this type"
|
17
|
+
# you can use this ruby class too. e.g.
|
18
|
+
#
|
19
|
+
# class Builder
|
20
|
+
# include Jenkins::Model::Describeable
|
21
|
+
# describe_as Java.hudson.tasks.Builder
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# behind the scenes, this creates a `Descriptor` instance registered against the java type
|
25
|
+
# `Java.hudson.tasks.Builder`. Now, any time Jenkins asks about what kind of `hudson.tasks.Builder`s there
|
26
|
+
# are in the system, this class will come up.
|
27
|
+
#
|
28
|
+
# This class should generally not be needed by plugin authors since it is part of the
|
29
|
+
# glue layer and not the public runtime API.
|
30
|
+
module Describable
|
31
|
+
DescribableError = Class.new(StandardError)
|
32
|
+
|
33
|
+
module DescribeAs
|
34
|
+
def describe_as cls
|
35
|
+
if defined?(cls.java_class) && cls.is_a?(Class)
|
36
|
+
@describe_as_type = cls.java_class
|
37
|
+
else
|
38
|
+
fail DescribableError, "#{cls.class.inspect} is not an instance of java.lang.Class"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
def describe_as_type
|
42
|
+
@describe_as_type
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# When a Describable class is subclassed, make it also Describable
|
47
|
+
module Inherited
|
48
|
+
def inherited(cls)
|
49
|
+
super(cls)
|
50
|
+
cls.extend Inherited
|
51
|
+
describe_as_type = @describe_as_type
|
52
|
+
cls.class_eval do
|
53
|
+
@describe_as_type = describe_as_type
|
54
|
+
end
|
55
|
+
if Jenkins::Plugin.instance
|
56
|
+
Jenkins::Plugin.instance.register_describable(cls, describe_as_type)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
module Included
|
62
|
+
def included(mod)
|
63
|
+
super
|
64
|
+
if mod.is_a? Class
|
65
|
+
mod.extend DescribeAs
|
66
|
+
mod.extend Inherited
|
67
|
+
else
|
68
|
+
warn "tried to include Describable into a Module. Are you sure?"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
self.extend Included
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Jenkins
|
5
|
+
module Model
|
6
|
+
class Descriptor < Java.hudson.model.Descriptor
|
7
|
+
|
8
|
+
def initialize(impl, plugin, java_type)
|
9
|
+
super(Java.org.jruby.RubyObject.java_class)
|
10
|
+
@impl, @plugin, @java_type = 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 newInstance(request, form)
|
22
|
+
properties = JSON.parse(form.toString(2))
|
23
|
+
properties.delete("kind")
|
24
|
+
properties.delete("stapler-class")
|
25
|
+
instance = @plugin.export(construct(properties))
|
26
|
+
puts "instance created: #{instance}"
|
27
|
+
return instance
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def construct(attrs)
|
33
|
+
@impl.new(attrs)
|
34
|
+
rescue ArgumentError
|
35
|
+
@impl.new
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
module Jenkins
|
3
|
+
module Model
|
4
|
+
|
5
|
+
# Receive/Send events about a running task
|
6
|
+
class Listener
|
7
|
+
|
8
|
+
# the underlying hudson.model.TaskListener object
|
9
|
+
attr_reader :native
|
10
|
+
|
11
|
+
def initialize(native = nil)
|
12
|
+
@native = native
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Insert a clickable hyperlink into this tasks's output
|
17
|
+
# @param [String] url the link target
|
18
|
+
# @param [String] text the link content
|
19
|
+
def hyperlink(url, text)
|
20
|
+
@native.hyperlink(url, text)
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Append a message to the task output.
|
25
|
+
# @param [String] msg the message
|
26
|
+
def log(msg)
|
27
|
+
@native.getLogger().write(msg.to_s)
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Append an error message to the task output.
|
32
|
+
# @param [String] msg the error message
|
33
|
+
def error(msg)
|
34
|
+
@native.error(msg.to_s)
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Append a fatal error message to the task output
|
39
|
+
# @param [String] msg the fatal error message
|
40
|
+
def fatal(msg)
|
41
|
+
@native.fatalError(msg.to_s)
|
42
|
+
end
|
43
|
+
|
44
|
+
Jenkins::Plugin::Proxies.register self, Java.hudson.util.AbstractTaskListener
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module Jenkins
|
5
|
+
# Acts as the primary gateway between Ruby and Jenkins
|
6
|
+
# There is one instance of this object for the entire
|
7
|
+
# plugin
|
8
|
+
#
|
9
|
+
# On the Java side, it contains a reference to an instance
|
10
|
+
# of RubyPlugin. These two objects talk to each other to
|
11
|
+
# get things done.
|
12
|
+
#
|
13
|
+
# Each running ruby plugin has exactly one instance of
|
14
|
+
# `Jenkins::Plugin`
|
15
|
+
class Plugin
|
16
|
+
|
17
|
+
# A list of all the hudson.model.Descriptor objects
|
18
|
+
# of which this plugin is aware *indexed by Wrapper class*
|
19
|
+
#
|
20
|
+
# This is used so that wrappers can always have a single place
|
21
|
+
# to go when they are asked for a descriptor. That way, wrapper
|
22
|
+
# instances can always return the descriptor associated with
|
23
|
+
# their class.
|
24
|
+
#
|
25
|
+
# This may go away.
|
26
|
+
attr_reader :descriptors
|
27
|
+
|
28
|
+
# the instance of jenkins.ruby.RubyPlugin with which this Plugin is associated
|
29
|
+
attr_reader :peer
|
30
|
+
|
31
|
+
# Initializes this plugin by reading the models.rb
|
32
|
+
# file. This is a manual registration process
|
33
|
+
# Where ruby objects register themselves with the plugin
|
34
|
+
# In the future, this process will be automatic, but
|
35
|
+
# I haven't decided the best way to do this yet.
|
36
|
+
#
|
37
|
+
# @param [org.jenkinsci.ruby.RubyPlugin] java a native java RubyPlugin
|
38
|
+
def initialize(java)
|
39
|
+
@java = @peer = java
|
40
|
+
@start = @stop = proc {}
|
41
|
+
@descriptors = {}
|
42
|
+
@proxies = Proxies.new(self)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Initialize the singleton instance that will run for a
|
46
|
+
# ruby plugin. This method is designed to be called by the
|
47
|
+
# Java side when setting up the ruby plugin
|
48
|
+
# @return [Jenkins::Plugin] the singleton instance
|
49
|
+
def self.initialize(java)
|
50
|
+
#TODO: check for double initialization?!?
|
51
|
+
@instance = new(java)
|
52
|
+
@instance.load_models
|
53
|
+
return @instance
|
54
|
+
end
|
55
|
+
|
56
|
+
# Get the singleton instance associated with this plugin
|
57
|
+
#
|
58
|
+
# This is useful when code in the plugin needs to get a
|
59
|
+
# reference to the plugin in which it is running e.g.
|
60
|
+
#
|
61
|
+
# Jenkins::Plugin.instance #=> the running plugin
|
62
|
+
#
|
63
|
+
# @return [Jenkins::Plugin] the singleton instance
|
64
|
+
def self.instance
|
65
|
+
@instance
|
66
|
+
end
|
67
|
+
|
68
|
+
# Register a ruby class as a Jenkins extension point of
|
69
|
+
# a particular java type
|
70
|
+
#
|
71
|
+
# This method is invoked automatically as part of the auto-registration
|
72
|
+
# process, and should not need to be invoked by plugin code.
|
73
|
+
#
|
74
|
+
# @param [Class] ruby_class the class implementing the extension point
|
75
|
+
# @param [java.lang.Class] java_class that Jenkins will see this extention point as
|
76
|
+
def register_describable(ruby_class, java_class)
|
77
|
+
descriptor = Jenkins::Model::Descriptor.new(ruby_class, self, java_class)
|
78
|
+
@peer.addExtension(descriptor)
|
79
|
+
@descriptors[ruby_class] = descriptor
|
80
|
+
end
|
81
|
+
|
82
|
+
# unique identifier for this plugin in the Jenkins server
|
83
|
+
def name
|
84
|
+
@peer.getWrapper().getShortName()
|
85
|
+
end
|
86
|
+
|
87
|
+
# Called once when Jenkins first initializes this plugin
|
88
|
+
# currently does nothing, but plugin startup hooks would
|
89
|
+
# go here.
|
90
|
+
def start
|
91
|
+
@start.call()
|
92
|
+
end
|
93
|
+
|
94
|
+
# Called one by Jenkins (via RubyPlugin) when this plugin
|
95
|
+
# is shut down. Currently this does nothing, but plugin
|
96
|
+
# shutdown hooks would go here.
|
97
|
+
def stop
|
98
|
+
@stop.call()
|
99
|
+
end
|
100
|
+
|
101
|
+
# Reflect an Java object coming from Jenkins into the context of this plugin
|
102
|
+
# If the object is originally from the ruby plugin, and it was previously
|
103
|
+
# exported, then it will unwrap it. Otherwise, it will just use the object
|
104
|
+
# as a normal Java object.
|
105
|
+
#
|
106
|
+
# @param [Object] object the object to bring in from the outside
|
107
|
+
# @return the best representation of that object for this plugin
|
108
|
+
def import(object)
|
109
|
+
@proxies.import object
|
110
|
+
end
|
111
|
+
|
112
|
+
# Reflect a native Ruby object into its External Java form.
|
113
|
+
#
|
114
|
+
# Delegates to `Proxies` for the heavy lifting.
|
115
|
+
#
|
116
|
+
# @param [Object] object the object
|
117
|
+
# @returns [java.lang.Object] the Java proxy
|
118
|
+
def export(object)
|
119
|
+
@proxies.export object
|
120
|
+
end
|
121
|
+
|
122
|
+
# Link a plugin-local Ruby object to an external Java object.
|
123
|
+
#
|
124
|
+
# see 'Proxies#link`
|
125
|
+
#
|
126
|
+
# @param [Object] internal the object on the Ruby side of the link
|
127
|
+
# @param [java.lang.Object] external the object on the Java side of the link
|
128
|
+
def link(internal, external)
|
129
|
+
@proxies.link internal, external
|
130
|
+
end
|
131
|
+
|
132
|
+
def load_models
|
133
|
+
p = @java.getModelsPath().getPath()
|
134
|
+
puts "Trying to load models from #{p}"
|
135
|
+
for filename in Dir["#{p}/**/*.rb"]
|
136
|
+
puts "Loading "+filename
|
137
|
+
load filename
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|