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
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ .rvmrc
2
+ *.gem
3
+ .bundle
4
+ .idea
5
+ Gemfile.lock
6
+ pkg/*
7
+ target/
data/.travis.yml ADDED
@@ -0,0 +1,2 @@
1
+ rvm:
2
+ - jruby
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in jenkins-plugins.gemspec
4
+ gemspec
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