jenkins-plugin-runtime 0.1.18 → 0.1.20

Sign up to get free protection for your applications and to get access to all the features.
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