jenkins_api_client 0.14.1 → 1.0.0.alpha.1

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.
@@ -30,4 +30,4 @@ notifications:
30
30
  on_success: change
31
31
  on_failure: always
32
32
  channels:
33
- - "irc.freenode.org##jenins-api-client"
33
+ - "irc.freenode.org##jenkins-api-client"
@@ -11,3 +11,5 @@ Thank you very much to all who contributed to this project.
11
11
  * [rubytester](https://github.com/rubytester)
12
12
  * [dougforpres](https://github.com/dougforpres)
13
13
  * [brettporter](https://github.com/brettporter)
14
+ * [riywo](https://github.com/riywo)
15
+ * [bobbrez](https://github.com/bobbrez)
data/Gemfile CHANGED
@@ -11,5 +11,6 @@ group :development do
11
11
  gem "jeweler", ">= 1.6.4"
12
12
  gem "rspec", "~> 2.13.0"
13
13
  gem "simplecov"
14
+ gem "yard-thor"
14
15
  gem "yard"
15
16
  end
data/README.md CHANGED
@@ -102,7 +102,7 @@ puts @client.job.list_all
102
102
  ### Chaining and Building Jobs
103
103
 
104
104
  Sometimes we want certain jobs to be added as downstream projects and run them
105
- sequencially. The following example will explain how this could be done.
105
+ sequentially. The following example will explain how this could be done.
106
106
 
107
107
  ```ruby
108
108
  require 'jenkins_api_client'
@@ -40,6 +40,7 @@ Gem::Specification.new do |s|
40
40
  "lib/jenkins_api_client/exceptions.rb",
41
41
  "lib/jenkins_api_client/job.rb",
42
42
  "lib/jenkins_api_client/node.rb",
43
+ "lib/jenkins_api_client/plugin_manager.rb",
43
44
  "lib/jenkins_api_client/system.rb",
44
45
  "lib/jenkins_api_client/user.rb",
45
46
  "lib/jenkins_api_client/version.rb",
@@ -28,6 +28,7 @@ require 'jenkins_api_client/node'
28
28
  require 'jenkins_api_client/system'
29
29
  require 'jenkins_api_client/view'
30
30
  require 'jenkins_api_client/build_queue'
31
+ require 'jenkins_api_client/plugin_manager'
31
32
  require 'jenkins_api_client/user'
32
33
 
33
34
  require 'jenkins_api_client/cli/helper'
@@ -212,6 +212,15 @@ module JenkinsApi
212
212
  JenkinsApi::Client::BuildQueue.new(self)
213
213
  end
214
214
 
215
+ # Creates an instance to the PluginManager by passing a reference to self
216
+ #
217
+ # @return [JenkinsApi::Client::PluginManager] an object to PluginManager
218
+ # subclass
219
+ #
220
+ def plugin
221
+ JenkinsApi::Client::PluginManager.new(self)
222
+ end
223
+
215
224
  # Creates an instance of the User class by passing a reference to self
216
225
  #
217
226
  # @return [JenkinsApi::Client::User] An object of User subclass
@@ -288,8 +297,8 @@ module JenkinsApi
288
297
  # @return [Net::HTTP::Response] Response from Jenkins for "/"
289
298
  #
290
299
  def get_root
291
- @logger.info "GET /"
292
- request = Net::HTTP::Get.new("/")
300
+ @logger.info "GET #{@jenkins_path}/"
301
+ request = Net::HTTP::Get.new("#{@jenkins_path}/")
293
302
  make_http_request(request)
294
303
  end
295
304
 
@@ -633,7 +642,10 @@ module JenkinsApi
633
642
  when 404
634
643
  raise Exceptions::NotFound.new @logger
635
644
  when 500
636
- raise Exceptions::InternalServerError.new @logger
645
+ matched = response.body.match(/Exception: (.*)<br>/)
646
+ api_message = matched[1] unless matched.nil?
647
+ @logger.debug "API message: #{api_message}"
648
+ raise Exceptions::InternalServerError.new(@logger, api_message)
637
649
  when 503
638
650
  raise Exceptions::ServiceUnavailable.new @logger
639
651
  else
@@ -41,8 +41,8 @@ module JenkinsApi
41
41
  #
42
42
  class NothingSubmitted < ApiException
43
43
  def initialize(logger, message = "", log_level = Logger::ERROR)
44
- msg = "Nothing is submitted. #{message}"
45
- super(logger, msg)
44
+ message = "Nothing is submitted." if message.empty?
45
+ super(logger, message)
46
46
  end
47
47
  end
48
48
 
@@ -80,8 +80,8 @@ module JenkinsApi
80
80
  #
81
81
  class Unauthorized < ApiException
82
82
  def initialize(logger, message = "", log_level = Logger::ERROR)
83
- msg = "Invalid credentials are provided. #{message}"
84
- super(logger, msg, Logger::FATAL)
83
+ message = "Invalid credentials are provided." if message.empty?
84
+ super(logger, message, Logger::FATAL)
85
85
  end
86
86
  end
87
87
  # Support for backward compatibility
@@ -158,8 +158,8 @@ module JenkinsApi
158
158
  #
159
159
  class CrumbNotFound < NotFound
160
160
  def initialize(logger, message = "", log_level = Logger::ERROR)
161
- msg = "No crumb available on the server. #{message}"
162
- super(logger, msg)
161
+ message = "No crumb available on the server." if message.empty?
162
+ super(logger, message)
163
163
  end
164
164
  end
165
165
  # Support for backward compatibility
@@ -200,10 +200,10 @@ module JenkinsApi
200
200
  #
201
201
  class InternalServerError < ApiException
202
202
  def initialize(logger, message = "", log_level = Logger::ERROR)
203
- msg = "Internel Server Error. Perhaps the in-memory configuration of" +
204
- " Jenkins is different from the disk configuration." +
205
- " Please try to reload the configuration #{message}"
206
- super(logger, msg)
203
+ message = "Internel Server Error. Perhaps the in-memory configuration" +
204
+ " Jenkins is different from the disk configuration. Please try to" +
205
+ " reload the configuration" if message.empty?
206
+ super(logger, message)
207
207
  end
208
208
  end
209
209
  # Support for backward compatibility
@@ -214,10 +214,10 @@ module JenkinsApi
214
214
  #
215
215
  class ServiceUnavailable < ApiException
216
216
  def initialize(logger, message = "", log_level = Logger::ERROR)
217
- msg = "Jenkins is being reloaded or restarted. Please wait till" +
218
- " Jenkins is completely back online. This can be" +
219
- " programatically achieved by System#wait_for_ready #{message}"
220
- super(logger, msg)
217
+ message = "Jenkins is being reloaded or restarted. Please wait till" +
218
+ " Jenkins is completely back online. This can be" +
219
+ " programatically achieved by System#wait_for_ready" if message.empty?
220
+ super(logger, message)
221
221
  end
222
222
  end
223
223
  # Support for backward compatibility
@@ -227,11 +227,20 @@ module JenkinsApi
227
227
  #
228
228
  class CLIError < ApiException
229
229
  def initialize(logger, message = "", log_level = Logger::ERROR)
230
- msg = "Execute CLI Error. #{message}"
231
- super(logger, msg)
230
+ message = "Unable to execute the command." if message.empty?
231
+ super(logger, message)
232
232
  end
233
233
  end
234
234
  # Support for backward compatibility
235
235
  CLIException = CLIError
236
+
237
+ # Exception when a particular plugin is not found
238
+ #
239
+ class PluginNotFound < NotFound
240
+ def initialize(logger, message = "", log_level = Logger::ERROR)
241
+ message = "The specified plugin is not found" if message.empty?
242
+ super(logger, message)
243
+ end
244
+ end
236
245
  end
237
246
  end
@@ -197,7 +197,7 @@ module JenkinsApi
197
197
  # Update a job with params given as a hash instead of the xml. For the
198
198
  # parameter description see #create_or_update_freestyle
199
199
  #
200
- # @param params [Hash]
200
+ # @param params [Hash] parameters to update a freestyle project
201
201
  #
202
202
  # @see #create_or_update_freestyle
203
203
  # @see #update
@@ -334,7 +334,7 @@ module JenkinsApi
334
334
  # @option params [String] :name Name of the job
335
335
  # @option params [String] :notification_email Email address to send
336
336
  # @option params [Boolean] :notification_email_for_every_unstable
337
- # Send email notification email for every unstable build
337
+ # Send email notification email for every unstable build
338
338
  #
339
339
  def add_email_notification(params)
340
340
  raise "No job name specified" unless params[:name]
@@ -405,7 +405,9 @@ module JenkinsApi
405
405
 
406
406
  # Delete a job given the name
407
407
  #
408
- # @param [String] job_name
408
+ # @param job_name [String] the name of the job to delete
409
+ #
410
+ # @return [String] the response from the HTTP POST request
409
411
  #
410
412
  def delete(job_name)
411
413
  @logger.info "Deleting job '#{job_name}'"
@@ -424,7 +426,9 @@ module JenkinsApi
424
426
 
425
427
  # Wipe out the workspace for a job given the name
426
428
  #
427
- # @param [String] job_name
429
+ # @param job_name [String] the name of the job to wipe out the workspace
430
+ #
431
+ # @return [String] response from the HTTP POST request
428
432
  #
429
433
  def wipe_out_workspace(job_name)
430
434
  @logger.info "Wiping out the workspace of job '#{job_name}'"
@@ -436,8 +440,8 @@ module JenkinsApi
436
440
  # is specified. The build will be stopped only if it was
437
441
  # in 'running' state.
438
442
  #
439
- # @param [String] job_name
440
- # @param [Number] build_number
443
+ # @param job_name [String] the name of the job to stop the build
444
+ # @param build_number [Number] the build number to stop
441
445
  #
442
446
  def stop_build(job_name, build_number = 0)
443
447
  build_number = get_current_build_number(job_name) if build_number == 0
@@ -451,11 +455,15 @@ module JenkinsApi
451
455
  @client.api_post_request("/job/#{job_name}/#{build_number}/stop")
452
456
  end
453
457
  end
458
+ alias_method :stop, :stop_build
459
+ alias_method :abort, :stop_build
454
460
 
455
461
  # Re-create the same job
456
462
  # This is a hack to clear any existing builds
457
463
  #
458
- # @param [String] job_name
464
+ # @param job_name [String] the name of the job to recreate
465
+ #
466
+ # @return [String] the response from the HTTP POST request
459
467
  #
460
468
  def recreate(job_name)
461
469
  @logger.info "Recreating job '#{job_name}'"
@@ -466,8 +474,10 @@ module JenkinsApi
466
474
 
467
475
  # Copy a job
468
476
  #
469
- # @param [String] from_job_name
470
- # @param [String] to_job_name
477
+ # @param from_job_name [String] the name of the job to copy from
478
+ # @param to_job_name [String] the name of the job to copy to
479
+ #
480
+ # @return [String] the response from the HTTP POST request
471
481
  #
472
482
  def copy(from_job_name, to_job_name=nil)
473
483
  to_job_name = "copy_of_#{from_job_name}" if to_job_name.nil?
@@ -481,7 +491,7 @@ module JenkinsApi
481
491
  #
482
492
  # @param [String] job_name Name of the Jenkins job
483
493
  # @param [Number] build_num Specific build number to obtain the
484
- # console output from. Default is the recent build
494
+ # console output from. Default is the recent build
485
495
  # @param [Number] start start offset to get only a portion of the text
486
496
  # @param [String] mode Mode of text output. 'text' or 'html'
487
497
  #
@@ -520,6 +530,8 @@ module JenkinsApi
520
530
 
521
531
  # List all jobs on the Jenkins CI server
522
532
  #
533
+ # @return [Array<String>] the names of all jobs in jenkins
534
+ #
523
535
  def list_all
524
536
  response_json = @client.api_get_request("", "tree=jobs[name]")["jobs"]
525
537
  response_json.map { |job| job["name"] }.sort
@@ -527,7 +539,9 @@ module JenkinsApi
527
539
 
528
540
  # Checks if the given job exists in Jenkins
529
541
  #
530
- # @param [String] job_name
542
+ # @param job_name [String] the name of the job to check
543
+ #
544
+ # @return [Boolean] whether the job exists in jenkins or not
531
545
  #
532
546
  def exists?(job_name)
533
547
  list(job_name).include?(job_name)
@@ -536,8 +550,12 @@ module JenkinsApi
536
550
  # List all Jobs matching the given status
537
551
  # You can optionally pass in jobs list to filter the status from
538
552
  #
539
- # @param [String] status
540
- # @param [Array] jobs
553
+ # @param status [String] the job status to filter
554
+ # @param jobs [Array<String>] if specified this array will be used for
555
+ # filtering by the status otherwise the filtering will be done using
556
+ # all jobs available in jenkins
557
+ #
558
+ # @return [Array<String>] filtered jobs
541
559
  #
542
560
  def list_by_status(status, jobs = [])
543
561
  jobs = list_all if jobs.empty?
@@ -555,8 +573,10 @@ module JenkinsApi
555
573
 
556
574
  # List all jobs that match the given regex
557
575
  #
558
- # @param [String] filter - a regex
559
- # @param [Boolean] ignorecase
576
+ # @param filter [String] a regular expression or a string to filter jobs
577
+ # @param ignorecase [Boolean] whether to ignore case or not
578
+ #
579
+ # @return [Array<String>] jobs matching the given pattern
560
580
  #
561
581
  def list(filter, ignorecase = true)
562
582
  @logger.info "Obtaining jobs matching filter '#{filter}'"
@@ -574,6 +594,8 @@ module JenkinsApi
574
594
 
575
595
  # List all jobs on the Jenkins CI server along with their details
576
596
  #
597
+ # @return [Array<Hash>] the details of all jobs in jenkins
598
+ #
577
599
  def list_all_with_details
578
600
  @logger.info "Obtaining the details of all jobs"
579
601
  response_json = @client.api_get_request("")
@@ -582,7 +604,9 @@ module JenkinsApi
582
604
 
583
605
  # List details of a specific job
584
606
  #
585
- # @param [String] job_name
607
+ # @param job_name [String] the name of the job to obtain the details from
608
+ #
609
+ # @return [Hash] the details of the specified job
586
610
  #
587
611
  def list_details(job_name)
588
612
  @logger.info "Obtaining the details of '#{job_name}'"
@@ -591,7 +615,8 @@ module JenkinsApi
591
615
 
592
616
  # List upstream projects of a specific job
593
617
  #
594
- # @param [String] job_name
618
+ # @param job_name [String] the name of the job to obtain upstream
619
+ # projects for
595
620
  #
596
621
  def get_upstream_projects(job_name)
597
622
  @logger.info "Obtaining the upstream projects of '#{job_name}'"
@@ -601,7 +626,8 @@ module JenkinsApi
601
626
 
602
627
  # List downstream projects of a specific job
603
628
  #
604
- # @param [String] job_name
629
+ # @param job_name [String] the name of the job to obtain downstream
630
+ # projects for
605
631
  #
606
632
  def get_downstream_projects(job_name)
607
633
  @logger.info "Obtaining the down stream projects of '#{job_name}'"
@@ -660,6 +686,7 @@ module JenkinsApi
660
686
  response_json = @client.api_get_request("/job/#{job_name}")
661
687
  color_to_status(response_json["color"])
662
688
  end
689
+ alias_method :status, :get_current_build_status
663
690
 
664
691
  # Obtain the current build number of the given job
665
692
  # This function returns nil if there were no builds for the given job.
@@ -672,6 +699,7 @@ module JenkinsApi
672
699
  @logger.info "Obtaining the current build number of '#{job_name}'"
673
700
  @client.api_get_request("/job/#{job_name}")['nextBuildNumber'].to_i - 1
674
701
  end
702
+ alias_method :build_number, :get_current_build_number
675
703
 
676
704
  # Build a job given the name of the job
677
705
  # You can optionally pass in a list of params for Jenkins to use for
@@ -0,0 +1,460 @@
1
+ #
2
+ # Copyright (c) 2012-2013 Kannan Manickam <arangamani.kannan@gmail.com>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+
23
+ module JenkinsApi
24
+ class Client
25
+ # This classes communicates with the /pluginManager API for listing
26
+ # installed plugins, installing new plgins through hacks, and performing a
27
+ # lot of operations on installed plugins. It also gives the ability to
28
+ # obtain the details about available plugins in Jenkins update center by
29
+ # commmunicating with /updateCenter API.
30
+ #
31
+ class PluginManager
32
+
33
+ # Initializes a new PluginManager object.
34
+ #
35
+ # @param [Object] client a reference to Client
36
+ #
37
+ def initialize(client)
38
+ @client = client
39
+ @logger = @client.logger
40
+ end
41
+
42
+ # Returns a string representation of PluginManager class.
43
+ #
44
+ def to_s
45
+ "#<JenkinsApi::Client::PluginManager>"
46
+ end
47
+
48
+ # Defines a method to perform the given action on plugin(s)
49
+ #
50
+ # @param action [Symbol] the action to perform
51
+ # @param post_endpoint [Symbol] the endpoint in the POST request for the
52
+ # action
53
+ #
54
+ def self.plugin_action_method(action, post_endpoint)
55
+ define_method(action) do |plugins|
56
+ plugins = [plugins] unless plugins.is_a?(Array)
57
+ @logger.info "Performing '#{action}' on plugins: #{plugins.inspect}"
58
+ plugins.each do |plugin|
59
+ @client.api_post_request(
60
+ "/pluginManager/plugin/#{plugin}/#{post_endpoint}"
61
+ )
62
+ end
63
+ end
64
+ end
65
+
66
+ # Obtains the list of installed plugins from Jenkins along with their
67
+ # version numbers with optional filters
68
+ #
69
+ # @param filters [Hash] optional filters to apply. Use symbols for filter
70
+ # keys
71
+ #
72
+ # @option filters [Boolean] :active filter active/non-active plugins
73
+ # @option filters [Boolean] :bundled filter bundled/non-bundled plugins
74
+ # @option filters [Boolean] :deleted filter deleted/available plugins
75
+ # @option filters [Boolean] :downgradable filter downgradable plugins
76
+ # @option filters [Boolean] :enabled filter enabled/disabled plugins
77
+ # @option filters [Boolean] :hasUpdate filter plugins that has update
78
+ # available. Note that 'U' is capitalized in hasUpdate.
79
+ # @option filters [Boolean] :pinned filter pinned/un-pinned plugins
80
+ #
81
+ # @return [Hash<String, String>] installed plugins and their versions
82
+ # matching the filter provided. returns an empty hash if there are no
83
+ # plugins matched the filters or no plugins are installed in jenkins.
84
+ #
85
+ # @example Listing installed plugins from jenkins
86
+ # >> @client.plugin.list_installed
87
+ # => {
88
+ # "mailer" => "1.5",
89
+ # "external-monitor-job" => "1.1",
90
+ # "ldap" => "1.2"
91
+ # }
92
+ # >> @client.plugin.list_installed(true)
93
+ # => {}
94
+ #
95
+ # @example Listing installed plugins based on filters provided
96
+ # >> @client.plugin.list_installed(
97
+ # :active => true, :deleted => false, :bundled => false
98
+ # )
99
+ # => {
100
+ # "sourcemonitor" => "0.2",
101
+ # "sms-notification" => "1.0",
102
+ # "jquery" => "1.7.2-1",
103
+ # "simple-theme-plugin" => "0.3",
104
+ # "jquery-ui" => "1.0.2",
105
+ # "analysis-core" => "1.49"
106
+ # }
107
+ #
108
+ def list_installed(filters = {})
109
+ supported_filters = [
110
+ :active, :bundled, :deleted, :downgradable, :enabled, :hasUpdate,
111
+ :pinned
112
+ ]
113
+ unless filters.keys.all? { |filter| supported_filters.include?(filter) }
114
+ raise ArgumentError, "Unsupported filters specified." +
115
+ " Supported filters: #{supported_filters.inspect}"
116
+ end
117
+ tree_filters = filters.empty? ? "" : ",#{filters.keys.join(",")}"
118
+ plugins = @client.api_get_request(
119
+ "/pluginManager",
120
+ "tree=plugins[shortName,version#{tree_filters}]"
121
+ )["plugins"]
122
+ installed = Hash[plugins.map do |plugin|
123
+ if filters.keys.all? { |key| plugin[key.to_s] == filters[key] }
124
+ [plugin["shortName"], plugin["version"]]
125
+ end
126
+ end]
127
+ installed
128
+ end
129
+
130
+ # Obtains the details of a single installed plugin
131
+ #
132
+ # @param plugin [String] the plugin ID of the desired plugin
133
+ #
134
+ # @return [Hash] the details of the given installed plugin
135
+ #
136
+ # @example Obtain the information of an installed plugin
137
+ # >> @client.plugin.get_installed_info "ldap"
138
+ # => {
139
+ # "active"=>false,
140
+ # "backupVersion"=>"1.2",
141
+ # "bundled"=>true,
142
+ # "deleted"=>false,
143
+ # "dependencies"=>[],
144
+ # "downgradable"=>true,
145
+ # "enabled"=>false,
146
+ # "hasUpdate"=>false,
147
+ # "longName"=>"LDAP Plugin",
148
+ # "pinned"=>true,
149
+ # "shortName"=>"ldap",
150
+ # "supportsDynamicLoad"=>"MAYBE",
151
+ # "url"=>"http://wiki.jenkins-ci.org/display/JENKINS/LDAP+Plugin",
152
+ # "version"=>"1.5"
153
+ # }
154
+ #
155
+ def get_installed_info(plugin)
156
+ @logger.info "Obtaining the details of plugin: #{plugin}"
157
+ plugins = @client.api_get_request(
158
+ "/pluginManager",
159
+ "depth=1"
160
+ )["plugins"]
161
+ matched_plugin = plugins.select do |a_plugin|
162
+ a_plugin["shortName"] == plugin
163
+ end
164
+ if matched_plugin.empty?
165
+ raise Exceptions::PluginNotFound.new(
166
+ @logger,
167
+ "Plugin '#{plugin}' is not found"
168
+ )
169
+ else
170
+ matched_plugin.first
171
+ end
172
+ end
173
+
174
+ # List the available plugins from jenkins update center along with their
175
+ # version numbers
176
+ #
177
+ # @param filters [Hash] optional filters to filter available plugins.
178
+ #
179
+ # @option filters [Array] :category the category of the plugin to
180
+ # filter
181
+ # @option filters [Array] :dependency the dependency of the plugin to
182
+ # filter
183
+ #
184
+ # @return [Hash<String, String>] available plugins and their versions.
185
+ # returns an empty if no plugins are available.
186
+ #
187
+ # @example Listing available plugins from jenkins
188
+ # >> @client.plugin.list_available
189
+ # => {
190
+ # "accurev" => "0.6.18",
191
+ # "active-directory" => "1.33",
192
+ # "AdaptivePlugin" => "0.1",
193
+ # ...
194
+ # "zubhium" => "0.1.6"
195
+ # }
196
+ #
197
+ # @example Listing available plugins matching a particular category
198
+ # >> pp @client.plugin.list_available(:category => "ui")
199
+ # => {
200
+ # "all-changes"=>"1.3",
201
+ # "bruceschneier"=>"0.1",
202
+ # ...
203
+ # "xfpanel"=>"1.2.2"
204
+ # }
205
+ #
206
+ # @example Listing available plugins matching a particular dependency
207
+ # >> pp @client.plugin.list_available(:dependency => "git")
208
+ # => {
209
+ # "build-failure-analyzer"=>"1.5.0",
210
+ # "buildheroes"=>"0.2",
211
+ # ...
212
+ # "xpdev"=>"1.0"
213
+ # }
214
+ #
215
+ def list_available(filters = {})
216
+ supported_filters = [:category, :dependency]
217
+ filter_plural_map = {
218
+ :dependency => "dependencies",
219
+ :category => "categories"
220
+ }
221
+ unless filters.keys.all? { |filter| supported_filters.include?(filter) }
222
+ raise ArgumentError, "Unsupported filters specified." +
223
+ " Supported filters: #{supported_filters.inspect}"
224
+ end
225
+ # Compute the filters to be passed to the JSON tree parameter
226
+ tree_filters =
227
+ if filters.empty?
228
+ ""
229
+ else
230
+ ",#{filters.keys.map{ |key| filter_plural_map[key] }.join(",")}"
231
+ end
232
+
233
+ availables = @client.api_get_request(
234
+ "/updateCenter/coreSource",
235
+ "tree=availables[name,version#{tree_filters}]"
236
+ )["availables"]
237
+ Hash[availables.map do |plugin|
238
+ if filters.keys.all? do |key|
239
+ !plugin[filter_plural_map[key]].nil? &&
240
+ plugin[filter_plural_map[key]].include?(filters[key])
241
+ end
242
+ [plugin["name"], plugin["version"]]
243
+ end
244
+ end]
245
+ end
246
+
247
+ # Obtains the information about a plugin that is available in the Jenkins
248
+ # update center
249
+ #
250
+ # @param plugin [String] the plugin ID to obtain information for
251
+ #
252
+ # @return [Hash] the details of the given plugin
253
+ #
254
+ # @example Obtaining the details of a plugin available in jenkins
255
+ # >> @client.plugin.get_available_info "status-view"
256
+ # => {
257
+ # "name"=>"status-view",
258
+ # "sourceId"=>"default",
259
+ # "url"=>"http://updates.jenkins-ci.org/download/plugins/status-view/1.0/status-view.hpi",
260
+ # "version"=>"1.0",
261
+ # "categories"=>["ui"],
262
+ # "compatibleSinceVersion"=>nil,
263
+ # "compatibleWithInstalledVersion"=>true,
264
+ # "dependencies"=>{},
265
+ # "excerpt"=>"View type to show jobs filtered by the status of the last completed build.",
266
+ # "installed"=>nil, "neededDependencies"=>[],
267
+ # "requiredCore"=>"1.342",
268
+ # "title"=>"Status View Plugin",
269
+ # "wiki"=>"https://wiki.jenkins-ci.org/display/JENKINS/Status+View+Plugin"
270
+ # }
271
+ #
272
+ def get_available_info(plugin)
273
+ plugins = @client.api_get_request(
274
+ "/updateCenter/coreSource",
275
+ "depth=1"
276
+ )["availables"]
277
+ matched_plugin = plugins.select do |a_plugin|
278
+ a_plugin["name"] == plugin
279
+ end
280
+ if matched_plugin.empty?
281
+ raise Exceptions::PluginNotFound.new(
282
+ @logger,
283
+ "Plugin '#{plugin}' is not found"
284
+ )
285
+ else
286
+ matched_plugin.first
287
+ end
288
+ end
289
+
290
+ # List the available updates for plugins from jenkins update center
291
+ # along with their version numbers
292
+ #
293
+ # @return [Hash<String, String>] available plugin updates and their
294
+ # versions. returns an empty if no plugins are available.
295
+ #
296
+ # @example Listing available plugin updates from jenkins
297
+ # >> @client.plugin.list_updates
298
+ # => {
299
+ # "ldap" => "1.5",
300
+ # "ssh-slaves" => "0.27",
301
+ # "subversion" => "1".50
302
+ # }
303
+ #
304
+ def list_updates
305
+ updates = @client.api_get_request(
306
+ "/updateCenter/coreSource",
307
+ "tree=updates[name,version]"
308
+ )["updates"]
309
+ Hash[updates.map { |plugin| [plugin["name"], plugin["version"]] }]
310
+ end
311
+
312
+ # Installs a specific plugin or list of plugins. This method will install
313
+ # the latest available plugins that jenkins reports. The installation
314
+ # might not take place right away for some plugins and they might require
315
+ # restart of jenkins instances. This method makes a single POST request
316
+ # for the installation of multiple plugins. Updating plugins can be done
317
+ # the same way. When the install action is issued, it gets the latest
318
+ # version of the plugin if the plugin is outdated.
319
+ #
320
+ # @see Client#api_post_request
321
+ # @see #restart_required?
322
+ # @see System#restart
323
+ # @see #uninstall
324
+ #
325
+ # @param plugins [String, Array] a single plugin or a list of plugins to
326
+ # be installed
327
+ #
328
+ # @return [String] the HTTP code from the plugin install POST request
329
+ #
330
+ # @example Installing a plugin and restart jenkins if required
331
+ # >> @client.plugin.install "s3"
332
+ # => "302" # Response code from plugin installation POST
333
+ # >> @client.plugin.restart_required?
334
+ # => true # A restart is required for the installation completion
335
+ # >> @client.system.restart(true)
336
+ # => "302" # A force restart is performed
337
+ #
338
+ def install(plugins)
339
+ # Convert the input argument to an array if it is not already an array
340
+ plugins = [plugins] unless plugins.is_a?(Array)
341
+ @logger.info "Installing plugins: #{plugins.inspect}"
342
+
343
+ # Build the form data to post to jenkins
344
+ form_data = {}
345
+ plugins.each { |plugin| form_data["plugin.#{plugin}.default"] = "on" }
346
+ @client.api_post_request("/pluginManager/install", form_data)
347
+ end
348
+ alias_method :update, :install
349
+
350
+
351
+ # @!method uninstall(plugins)
352
+ #
353
+ # Uninstalls the specified plugin or list of plugins. Only the user
354
+ # installed plugins can be uninstalled. The plugins installed by default
355
+ # by jenkins (also known as bundled plugins) cannot be uninstalled. The
356
+ # call will succeed but the plugins wil still remain in jenkins installed.
357
+ # This method makes a POST request for every plugin requested - so it
358
+ # might lead to some delay if a big list is provided.
359
+ #
360
+ # @see Client#api_post_request
361
+ # @see #restart_required?
362
+ # @see System#restart
363
+ # @see #install
364
+ #
365
+ # @param plugins [String, Array] a single plugin or list of plugins to be
366
+ # uninstalled
367
+ #
368
+ plugin_action_method :uninstall, :doUninstall
369
+
370
+ # @!method downgrade(plugins)
371
+ #
372
+ # Downgrades the specified plugin or list of plugins. This method makes s
373
+ # POST request for every plugin specified - so it might lead to some
374
+ # delay if a big list is provided.
375
+ #
376
+ # @see Client#api_post_request
377
+ # @see #restart_required?
378
+ # @see System#restart
379
+ # @see #install
380
+ #
381
+ # @param [String, Array] a single plugin or list of plugins to be
382
+ # downgraded
383
+ #
384
+ plugin_action_method :downgrade, :downgrade
385
+
386
+ # @!method unpin(plugins)
387
+ #
388
+ # Unpins the specified plugin or list of plugins. This method makes a
389
+ # POST request for every plugin specified - so it might lead to some
390
+ # delay if a big list is provided.
391
+ #
392
+ # @see Client#api_post_request
393
+ # @see #restart_required?
394
+ # @see System#restart
395
+ #
396
+ # @param plugins [String, Array] a single plugin or list of plugins to be
397
+ # uninstalled
398
+ #
399
+ plugin_action_method :unpin, :unpin
400
+
401
+ # @!method enable(plugins)
402
+ #
403
+ # Enables the specified plugin or list of plugins. This method makes a
404
+ # POST request for every plugin specified - so it might lead to some
405
+ # delay if a big list is provided.
406
+ #
407
+ # @see Client#api_post_request
408
+ # @see #restart_required?
409
+ # @see System#restart
410
+ # @see #disable
411
+ #
412
+ # @param plugins [String, Array] a single plugin or list of plugins to be
413
+ # uninstalled
414
+ #
415
+ plugin_action_method :enable, :makeEnabled
416
+
417
+ # @!method disable(plugins)
418
+ #
419
+ # Disables the specified plugin or list of plugins. This method makes a
420
+ # POST request for every plugin specified - so it might lead to some
421
+ # delay if a big list is provided.
422
+ #
423
+ # @see Client#api_post_request
424
+ # @see #restart_required?
425
+ # @see System#restart
426
+ # @see #enable
427
+ #
428
+ # @param plugins [String, Array] a single plugin or list of plugins to be
429
+ # uninstalled
430
+ #
431
+ plugin_action_method :disable, :makeDisabled
432
+
433
+ # Requests the Jenkins plugin manager to check for updates by connecting
434
+ # to the update site.
435
+ #
436
+ # @see #list_updates
437
+ #
438
+ def check_for_updates
439
+ @client.api_post_request("/pluginManager/checkUpdates")
440
+ end
441
+
442
+ # Whether restart required for the completion of plugin
443
+ # installations/uninstallations
444
+ #
445
+ # @see Client#api_get_request
446
+ #
447
+ # @return [Boolean] whether restart is required for the completion for
448
+ # plugin installations/uninstallations.
449
+ #
450
+ def restart_required?
451
+ response = @client.api_get_request(
452
+ "/updateCenter",
453
+ "tree=restartRequiredForCompletion"
454
+ )
455
+ response["restartRequiredForCompletion"] ||
456
+ !list_installed(:deleted => true).empty?
457
+ end
458
+ end
459
+ end
460
+ end