jenkins-plugin-runtime 0.1.18 → 0.1.20

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 (40) hide show
  1. data/README.md +12 -0
  2. data/Rakefile +2 -1
  3. data/jenkins-plugin-runtime.gemspec +2 -1
  4. data/lib/core_ext/exception.rb +36 -0
  5. data/lib/jenkins/cli/command.rb +167 -0
  6. data/lib/jenkins/cli/command_proxy.rb +62 -0
  7. data/lib/jenkins/filepath.rb +1 -6
  8. data/lib/jenkins/launcher.rb +4 -14
  9. data/lib/jenkins/model.rb +8 -41
  10. data/lib/jenkins/model/action.rb +1 -9
  11. data/lib/jenkins/model/build.rb +4 -5
  12. data/lib/jenkins/model/describable.rb +16 -30
  13. data/lib/jenkins/model/root_action.rb +2 -2
  14. data/lib/jenkins/plugin.rb +61 -12
  15. data/lib/jenkins/plugin/behavior.rb +96 -0
  16. data/lib/jenkins/plugin/proxies.rb +4 -0
  17. data/lib/jenkins/plugin/proxies/action.rb +10 -6
  18. data/lib/jenkins/plugin/proxies/build_step.rb +1 -0
  19. data/lib/jenkins/plugin/proxies/build_wrapper.rb +2 -5
  20. data/lib/jenkins/plugin/proxies/builder.rb +1 -5
  21. data/lib/jenkins/plugin/proxies/describable.rb +6 -0
  22. data/lib/jenkins/plugin/proxies/publisher.rb +1 -5
  23. data/lib/jenkins/plugin/proxies/root_action.rb +2 -17
  24. data/lib/jenkins/plugin/proxy.rb +27 -6
  25. data/lib/jenkins/plugin/runtime.rb +3 -0
  26. data/lib/jenkins/plugin/runtime/version.rb +1 -1
  27. data/lib/jenkins/plugin/wrapper.rb +29 -0
  28. data/spec/jenkins/cli/command_proxy_spec.rb +5 -0
  29. data/spec/jenkins/cli/command_spec.rb +5 -0
  30. data/spec/jenkins/launcher_spec.rb +0 -4
  31. data/spec/jenkins/model/action_spec.rb +2 -1
  32. data/spec/jenkins/model/build_spec.rb +2 -3
  33. data/spec/jenkins/model/describable_spec.rb +22 -7
  34. data/spec/jenkins/model_spec.rb +0 -28
  35. data/spec/jenkins/plugin/behavior_spec.rb +109 -0
  36. data/spec/jenkins/plugin/proxy_spec.rb +14 -0
  37. data/spec/jenkins/plugin_spec.rb +20 -0
  38. data/spec/spec_helper.rb +11 -5
  39. metadata +34 -13
  40. data/spec/mockito-all-1.8.5.jar +0 -0
@@ -0,0 +1,12 @@
1
+
2
+ # Jenkins Plugin Runtime
3
+
4
+ This gem provides the glue between the native Jenkins runtime
5
+ which is implemented in Java, and an idiomatic API that allows
6
+ for development with a Ruby 'feel'
7
+
8
+ If you're interested in developing actual plugin, see the
9
+ [plugin development tools][1]
10
+
11
+ [1]:https://github.com/jenkinsci/jpi.rb
12
+
data/Rakefile CHANGED
@@ -3,6 +3,7 @@ Bundler::GemHelper.install_tasks
3
3
 
4
4
  require 'rspec/core/rake_task'
5
5
  RSpec::Core::RakeTask.new(:spec)
6
+ task :spec => :compile
6
7
 
7
8
  require 'jenkins/war'
8
9
  Jenkins::War.classpath
@@ -18,4 +19,4 @@ require 'rake/clean'
18
19
  directory "target"
19
20
  CLEAN.include("target")
20
21
 
21
- task :default => [:compile, :spec]
22
+ task :default => [:compile, :spec]
@@ -20,9 +20,10 @@ Gem::Specification.new do |s|
20
20
  s.require_paths = ["lib"]
21
21
 
22
22
  s.add_dependency "json"
23
+ s.add_dependency "slop", "~> 3.0.2"
23
24
 
24
25
  s.add_development_dependency "rake", "0.8.7"
25
- s.add_development_dependency "rspec", "~> 2.0"
26
+ s.add_development_dependency "rspec", "~> 2.7.0" # something in rspec 2.8 breaks rspec-spies
26
27
  s.add_development_dependency "rspec-spies"
27
28
  s.add_development_dependency "jenkins-war", "~> 1.445"
28
29
 
@@ -0,0 +1,36 @@
1
+ # From utilrb's full_message.rb, http://utilrb.rubyforge.org/
2
+ # http://gitorious.org/+rock-core-maintainers/orocos-toolchain/rock-utilrb
3
+ #
4
+ # Copyright (c) 2006-2008
5
+ # Sylvain Joyeux <sylvain.joyeux@m4x.org>
6
+ # LAAS/CNRS <openrobots@laas.fr>
7
+ #
8
+ # Released under the BSD license,
9
+ # http://gitorious.org/+rock-core-maintainers/orocos-toolchain/rock-utilrb/blobs/master/License.txt
10
+
11
+ class Exception
12
+ def full_message(options = {}, &block)
13
+ since_matches, until_matches = options[:since], options[:until]
14
+
15
+ trace = backtrace
16
+ if since_matches || until_matches
17
+ found_beginning, found_end = !since_matches, false
18
+ trace = trace.find_all do |line|
19
+ found_beginning ||= (line =~ since_matches)
20
+ found_end ||= (line =~ until_matches) if until_matches
21
+ found_beginning && !found_end
22
+ end
23
+ end
24
+
25
+ first, *remaining = if block_given? then trace.find_all(&block)
26
+ else trace
27
+ end
28
+
29
+ msg = "#{first}: #{message} (#{self.class})"
30
+ unless remaining.empty?
31
+ msg << "\n\tfrom " + remaining.join("\n\tfrom ")
32
+ end
33
+
34
+ msg
35
+ end
36
+ end
@@ -0,0 +1,167 @@
1
+ require 'slop'
2
+
3
+ module Jenkins::CLI
4
+ # Extend the Jenkins CLI
5
+ #
6
+ # Jenkins ships with a CLI which can be used to iteract with it
7
+ # from a terminal, or from a program. It also exposes an API with
8
+ # which developers can extend the CLI with their own commands.
9
+ #
10
+ # The Ruby API allows you define a command as a Ruby class, which
11
+ # is then instantiated once for each invocation of the command via
12
+ # the CLI.
13
+ #
14
+ # Argument parsing is flexible, and a parsing scheme (Slop) is
15
+ # provided by default, but this is optional behavior which can
16
+ # be overridden.
17
+ #
18
+ # Include this to define a new CLI / SSHD command. Example:
19
+ #
20
+ # class HelloWorldCommand
21
+ # include Jenkins::CLI::Command
22
+ #
23
+ # description "Hello world sample command"
24
+ #
25
+ # arguments do
26
+ # on :v, :verbose, 'Print verbosely'
27
+ # on :n, :name=, 'Set your name, if not Joe', :default => 'Joe'
28
+ # end
29
+ #
30
+ # run do
31
+ # puts "Hello #{options[:name]}!"
32
+ # if options.verbose?
33
+ # puts "It's currently #{Time.now}"
34
+ # puts "Very glad to see you! How are you doing today?"
35
+ # end
36
+ # end
37
+ # end
38
+ #
39
+ # This will create a Jenkins CLI command called `hello-world`
40
+ # Which can be used as
41
+ # jenkins-cli hello-world --n cowboyd -v
42
+ #
43
+ # @see {https://github.com/injekt/slop Slop}
44
+ # @see {https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+CLI Running Jenkins CLI}
45
+ module Command
46
+ extend Jenkins::Plugin::Behavior
47
+
48
+ implemented do |cls|
49
+ cls.instance_eval do
50
+ @slop_block = Proc.new {}
51
+ @run_block = Proc.new {}
52
+ @description = 'No description'
53
+ command_name default_command_name
54
+ end
55
+ Jenkins.plugin.register_extension CommandProxy.new(Jenkins.plugin, cls.new)
56
+ end
57
+
58
+ module ClassMethods
59
+ attr_reader :slop_block, :run_block
60
+
61
+ # Set (or get) the description shown by this command in the 'help' CLI
62
+ # command. Also used in the default implementation of Slop's banner -
63
+ # meaning the my-command --help output.
64
+ #
65
+ # Example: description "Cool command that'll rock your world!"
66
+ # @param [String] description the description of the command
67
+ # @return [String] the description of the command
68
+ def description(desc = nil)
69
+ desc ? @description = desc : @description
70
+ end
71
+
72
+ # Set up the block passed to Slop.parse. See the Slop README for
73
+ # information on the way this block works:
74
+ # https://github.com/injekt/slop/blob/master/README.md
75
+ #
76
+ # Example: arguments { on :v, :verbose, "Be verbose" }
77
+ #
78
+ # @yield a declaration of arguments and options of this command
79
+ def arguments(&slop_block)
80
+ @slop_block = slop_block
81
+ end
82
+
83
+ # Define the actual implementation of the command
84
+ #
85
+ # This block specified with {#run} will be invoked in the
86
+ # scope of a fresh instance for each command invocation.
87
+ #
88
+ # Example: run { puts "Hello world" }
89
+ #
90
+ # @yield the command body
91
+ def run(&run_block)
92
+ @run_block = run_block
93
+ end
94
+
95
+ # Get/set the name by which the command will be invoked.
96
+ #
97
+ # This is what the user has to call the command as. The default value is
98
+ # the class name, with any 'Command' suffix removed, and the 'CamelCase'
99
+ # words separated using hypen, into 'camel-case'. For example this turns
100
+ # `HelloWorldCommand` into `hello-world`.
101
+ #
102
+ # To use a custom name, just invoke it with that name. E.g.
103
+ #
104
+ # command_name "my-cooler-name"
105
+ #
106
+ # @param [String] the name with which the command will be invoked
107
+ # @return [String] the command name
108
+ def command_name(command_name = nil)
109
+ command_name ? @command_name = command_name : @command_name
110
+ end
111
+
112
+ # We use this to initialize the default value of @command_name. You can
113
+ # override this using the above `command_name`.
114
+ def default_command_name
115
+ command = name
116
+
117
+ # Replace any 'CLICommand' or 'Command' suffix on class name.
118
+ command = command.sub(/(CLI)?Command$/, '')
119
+
120
+ # Then convert "FooBarZot" into "Foo-Bar-Zot"
121
+ command = command.gsub(/([a-z0-9])([A-Z])/, '\1-\2')
122
+
123
+ # Then lower-case it.
124
+ command.downcase
125
+ end
126
+ end
127
+
128
+ module InstanceMethods
129
+ attr_reader :options
130
+
131
+ # Set up the instance with command-line arguments.
132
+ #
133
+ # Any arguments from the terminal will be passed to {#parse} as
134
+ # a whitespace separated list. The default implementation uses
135
+ # Slop to parse this list with the block passed to {.arguments}
136
+ #
137
+ # Note: If this method returns a falsy value, then the command will
138
+ # *not* be run.
139
+ #
140
+ # You can override this instance method if you don't want to use Slop, or
141
+ # if you want to do some preprocessing before Slop is called.
142
+ # param [Array] args the list of whitespace separated command line arguments
143
+ # return [Object] a truthy value if the parse succeeded and the command can be run.
144
+ def parse(args)
145
+ default_banner = "#{self.class.command_name} [options] - #{self.class.description}"
146
+ @options = Slop.new(:help => true, :strict => true,
147
+ :banner => default_banner, &self.class.slop_block)
148
+ @options.parse(args)
149
+ rescue Slop::Error => e
150
+ $stderr.puts e.message
151
+ $stderr.puts @options.help
152
+ end
153
+
154
+ # Run the command.
155
+ #
156
+ # This method is invoked immediately after {#parse} and
157
+ # implements the "meat" of the command. By default, it invokes
158
+ # body specified with {.run}.
159
+ #
160
+ # There should generally be no reason to override this, but hey
161
+ # it's your world! :-)
162
+ def run
163
+ self.instance_eval(&self.class.run_block)
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,62 @@
1
+ require 'core_ext/exception'
2
+
3
+ module Jenkins::CLI
4
+ # The native Java CLICommand class has a static initializer
5
+ # that prevents the class from being loaded without a running
6
+ # Jenkins instance. This disgusting hack detects if we're in
7
+ # RSpec mode and just creates the proxy class without descending
8
+ # from the Java class.
9
+ if Jenkins.rspec_ewwww_gross_hack?
10
+ class CommandProxy; end
11
+ else
12
+ class CommandProxy < Java.hudson.cli.CLICommand; end
13
+ end
14
+
15
+ class CommandProxy
16
+ include Jenkins::Plugin::Proxy
17
+
18
+ AbortException = Java.hudson.AbortException
19
+
20
+ def getShortDescription
21
+ @object.class.description
22
+ end
23
+
24
+ def createClone
25
+ self.dup.tap do |dup|
26
+ dup.instance_variable_set(:@object, @object.dup)
27
+ end
28
+ end
29
+
30
+ # This mirrors what Java.hudson.cli.CLICommand#getName does, but with the
31
+ # @object's class instead.
32
+ def getName
33
+ @object.class.command_name
34
+ end
35
+
36
+ def run
37
+ # We don't call run from within our main, but needs to be here to satisfy the Java Interface
38
+ raise RuntimeError, "This method should never be called."
39
+ end
40
+
41
+ # main(List<String> args, Locale locale, InputStream stdin, PrintStream stdout, PrintStream stderr)
42
+ def main(args, locale, stdin, stdout, stderr)
43
+ old_in, old_out, old_err = $stdin, $stdout, $stderr
44
+ begin
45
+ $stdin, $stdout, $stderr = stdin.to_io, stdout.to_io, stderr.to_io
46
+ if @object.parse(args.to_a)
47
+ @object.run
48
+ return 0
49
+ else
50
+ return -1
51
+ end
52
+ rescue AbortException
53
+ return -1
54
+ rescue => e
55
+ $stderr.puts e.full_message
56
+ return -1
57
+ ensure
58
+ $stdin, $stdout, $stderr = old_in, old_out, old_err
59
+ end
60
+ end
61
+ end
62
+ end
@@ -2,14 +2,9 @@ require 'pathname'
2
2
 
3
3
  module Jenkins
4
4
  class FilePath
5
+ include Jenkins::Plugin::Wrapper
5
6
  Stat = Struct.new(:size, :mode, :mtime)
6
7
 
7
- attr_reader :native
8
-
9
- def initialize(native)
10
- @native = native
11
- end
12
-
13
8
  # Ruby's Pathname internace
14
9
 
15
10
  def +(name)
@@ -2,12 +2,11 @@ module Jenkins
2
2
 
3
3
  # Launch processes on build slaves. No functionality is currently exposed
4
4
  class Launcher
5
- class Proc
6
- attr_reader :native
5
+ include Jenkins::Plugin::Wrapper
6
+ wrapper_for Java.hudson.Launcher
7
7
 
8
- def initialize(native)
9
- @native = native
10
- end
8
+ class Proc
9
+ include Jenkins::Plugin::Wrapper
11
10
 
12
11
  def alive?
13
12
  @native.isAlive()
@@ -35,13 +34,6 @@ module Jenkins
35
34
  end
36
35
  end
37
36
 
38
- # the native hudson.Launcher object
39
- attr_reader :native
40
-
41
- def initialize(native = nil)
42
- @native = native
43
- end
44
-
45
37
  # execute([env,] command... [,options]) -> fixnum
46
38
  def execute(*args)
47
39
  spawn(*args).join
@@ -113,7 +105,5 @@ module Jenkins
113
105
  end
114
106
  [env || {}, cmd, opt || {}]
115
107
  end
116
-
117
- Plugin::Proxies.register self, Java.hudson.Launcher
118
108
  end
119
109
  end
@@ -1,30 +1,7 @@
1
1
 
2
2
  module Jenkins
3
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
4
+ extend Plugin::Behavior
28
5
 
29
6
  module InstanceDisplayName
30
7
  # Get the display name of this Model. This value will be used as a default
@@ -66,26 +43,16 @@ module Jenkins
66
43
  def transients
67
44
  @transients ||= {}
68
45
  end
69
- end
70
46
 
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
47
+ end
80
48
 
81
- def clear
82
- @descendants = {}
83
- end
49
+ module ClassMethods
50
+ include ClassDisplayName
51
+ include Transience
52
+ end
84
53
 
85
- def descendant?(cls)
86
- @descendants[cls]
87
- end
54
+ module InstanceMethods
55
+ include InstanceDisplayName
88
56
  end
89
- extend Descendants
90
57
  end
91
58
  end
@@ -1,13 +1,8 @@
1
-
2
1
  require 'jenkins/model'
3
2
 
4
3
  module Jenkins
5
4
  module Model
6
-
7
- # TODO: I turned Action into Class from Module but it should be a bad idea.
8
- # The change may be reverted. I used class Action for implementing a model
9
- # as a describable but I really should do is implementing describable.
10
- class Action
5
+ module Action
11
6
  include Model
12
7
 
13
8
  module InstanceMethods
@@ -29,9 +24,6 @@ module Jenkins
29
24
  path.nil? ? @url_path : @url_path = path.to_s
30
25
  end
31
26
  end
32
-
33
- include InstanceMethods
34
- extend ClassMethods
35
27
  end
36
28
  end
37
29
  end