jenkins_api_client 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.gitignore +3 -0
  2. data/.jenkins.yml +9 -0
  3. data/.travis.yml +11 -15
  4. data/CHANGELOG.md +15 -0
  5. data/Gemfile +2 -2
  6. data/README.md +7 -9
  7. data/Rakefile +27 -14
  8. data/lib/jenkins_api_client.rb +36 -6
  9. data/lib/jenkins_api_client/build_queue.rb +213 -0
  10. data/lib/jenkins_api_client/cli/base.rb +10 -6
  11. data/lib/jenkins_api_client/cli/helper.rb +13 -4
  12. data/lib/jenkins_api_client/cli/job.rb +6 -9
  13. data/lib/jenkins_api_client/cli/node.rb +6 -4
  14. data/lib/jenkins_api_client/cli/system.rb +2 -1
  15. data/lib/jenkins_api_client/client.rb +31 -25
  16. data/lib/jenkins_api_client/job.rb +248 -95
  17. data/lib/jenkins_api_client/node.rb +128 -10
  18. data/lib/jenkins_api_client/system.rb +4 -2
  19. data/lib/jenkins_api_client/version.rb +2 -2
  20. data/lib/jenkins_api_client/view.rb +17 -4
  21. data/scripts/login_with_irb.rb +4 -3
  22. data/spec/func_tests/client_spec.rb +90 -0
  23. data/spec/func_tests/job_spec.rb +348 -0
  24. data/spec/func_tests/node_spec.rb +174 -0
  25. data/spec/{spec_helper.rb → func_tests/spec_helper.rb} +2 -2
  26. data/spec/func_tests/system_spec.rb +55 -0
  27. data/spec/func_tests/view_spec.rb +53 -0
  28. data/spec/unit_tests/client_spec.rb +211 -0
  29. data/spec/unit_tests/fixtures/files/computer_sample.xml +17 -0
  30. data/spec/unit_tests/fixtures/files/job_sample.xml +16 -0
  31. data/spec/unit_tests/job_spec.rb +355 -0
  32. data/spec/unit_tests/node_spec.rb +192 -0
  33. data/spec/unit_tests/spec_helper.rb +8 -0
  34. data/spec/unit_tests/system_spec.rb +54 -0
  35. data/spec/unit_tests/view_spec.rb +127 -0
  36. metadata +34 -23
  37. data/spec/client_spec.rb +0 -52
  38. data/spec/job_spec.rb +0 -158
  39. data/spec/node_spec.rb +0 -48
  40. data/spec/system_spec.rb +0 -46
@@ -32,12 +32,16 @@ module JenkinsApi
32
32
 
33
33
  class Base < Thor
34
34
 
35
- class_option :username, :aliases => "-u", :desc => "Name of Jenkins user"
36
- class_option :password, :aliases => "-p", :desc => "Password of Jenkins user"
37
- class_option :password_base64, :aliases => "-b", :desc => "Base 64 encoded password of Jenkins user"
38
- class_option :server_ip, :aliases => "-s", :desc => "Jenkins server IP address"
39
- class_option :server_port, :aliases => "-o", :desc => "Jenkins server port"
40
- class_option :creds_file, :aliases => "-c", :desc => "Credentials file for communicating with Jenkins server"
35
+ class_option :username, :aliases => "-u", :desc => "Name of Jenkins user"
36
+ class_option :password, :aliases => "-p",
37
+ :desc => "Password of Jenkins user"
38
+ class_option :password_base64, :aliases => "-b",
39
+ :desc => "Base 64 encoded password of Jenkins user"
40
+ class_option :server_ip, :aliases => "-s",
41
+ :desc => "Jenkins server IP address"
42
+ class_option :server_port, :aliases => "-o", :desc => "Jenkins port"
43
+ class_option :creds_file, :aliases => "-c",
44
+ :desc => "Credentials file for communicating with Jenkins server"
41
45
 
42
46
 
43
47
  map "-v" => :version
@@ -26,14 +26,23 @@ module JenkinsApi
26
26
  module CLI
27
27
  class Helper
28
28
  def self.setup(options)
29
- if options[:username] && options[:server_ip] && (options[:password] || options[:password_base64])
29
+ if options[:username] && options[:server_ip] && \
30
+ (options[:password] || options[:password_base64])
30
31
  creds = options
31
32
  elsif options[:creds_file]
32
- creds = YAML.load_file(File.expand_path(options[:creds_file], __FILE__))
33
+ creds = YAML.load_file(
34
+ File.expand_path(options[:creds_file], __FILE__)
35
+ )
33
36
  elsif File.exist?("#{ENV['HOME']}/.jenkins_api_client/login.yml")
34
- creds = YAML.load_file(File.expand_path("#{ENV['HOME']}/.jenkins_api_client/login.yml", __FILE__))
37
+ creds = YAML.load_file(
38
+ File.expand_path(
39
+ "#{ENV['HOME']}/.jenkins_api_client/login.yml", __FILE__
40
+ )
41
+ )
35
42
  else
36
- puts "Credentials are not set. Please pass them as parameters or set them in the default credentials file"
43
+ msg = "Credentials are not set. Please pass them as parameters or"
44
+ msg << " set them in the default credentials file"
45
+ puts msg
37
46
  exit 1
38
47
  end
39
48
  JenkinsApi::Client.new(creds)
@@ -31,7 +31,8 @@ module JenkinsApi
31
31
 
32
32
  desc "list", "List jobs"
33
33
  method_option :status, :aliases => "-t", :desc => "Status to filter"
34
- method_option :filter, :aliases => "-f", :desc => "Regular expression to filter jobs"
34
+ method_option :filter, :aliases => "-f",
35
+ :desc => "Regular expression to filter jobs"
35
36
  def list
36
37
  @client = Helper.setup(parent_options)
37
38
  if options[:filter] && options[:status]
@@ -64,12 +65,6 @@ module JenkinsApi
64
65
  puts @client.job.get_current_build_status(job)
65
66
  end
66
67
 
67
- desc "listrunning", "List running jobs"
68
- def listrunning
69
- @client = Helper.setup(parent_options)
70
- puts @client.job.list_running
71
- end
72
-
73
68
  desc "delete JOB", "Delete the job"
74
69
  def delete(job)
75
70
  @client = Helper.setup(parent_options)
@@ -77,10 +72,12 @@ module JenkinsApi
77
72
  end
78
73
 
79
74
  desc "console JOB", "Print the progressive console output of a job"
80
- method_option :sleep, :aliases => "-z", :desc => "Time to wait between querying the API for console output"
75
+ method_option :sleep, :aliases => "-z",
76
+ :desc => "Time to wait between querying the API for console output"
81
77
  def console(job)
82
78
  @client = Helper.setup(parent_options)
83
- # If debug is enabled, disable it. It shouldn't interfere with console output.
79
+ # If debug is enabled, disable it. It shouldn't interfere
80
+ # with console output.
84
81
  debug_changed = false
85
82
  if @client.debug == true
86
83
  @client.debug = false
@@ -28,9 +28,11 @@ module JenkinsApi
28
28
  module CLI
29
29
  class Node < Thor
30
30
  include Thor::Actions
31
+ include Terminal
31
32
 
32
33
  desc "list", "List all nodes"
33
- method_option :filter, :aliases => "-f", :desc => "Regular expression to filter jobs"
34
+ method_option :filter, :aliases => "-f",
35
+ :desc => "Regular expression to filter jobs"
34
36
  def list
35
37
  @client = Helper.setup(parent_options)
36
38
  if options[:filter]
@@ -48,7 +50,7 @@ module JenkinsApi
48
50
  general_attributes.each do |attr|
49
51
  rows << [attr, @client.node.method("get_#{attr}").call]
50
52
  end
51
- table = Terminal::Table.new :headings => ['Attribute', 'Value'], :rows => rows
53
+ table = Table.new :headings => ['Attribute', 'Value'], :rows => rows
52
54
  puts table
53
55
  end
54
56
 
@@ -60,7 +62,7 @@ module JenkinsApi
60
62
  node_attributes.each do |attr|
61
63
  rows << [attr, @client.node.method("get_node_#{attr}").call(node)]
62
64
  end
63
- table = Terminal::Table.new :headings => ['Attribute', 'Value'], :rows => rows
65
+ table = Table.new :headings => ['Attribute', 'Value'], :rows => rows
64
66
  puts "Node: #{node}"
65
67
  puts table
66
68
  end
@@ -73,7 +75,7 @@ module JenkinsApi
73
75
  node_properties.each do |property|
74
76
  rows << [property, @client.node.method("is_#{property}?").call(node)]
75
77
  end
76
- table = Terminal::Table.new :headings => ['Property', 'Value'], :rows => rows
78
+ table = Table.new :headings => ['Property', 'Value'], :rows => rows
77
79
  puts "Node: #{node}"
78
80
  puts table
79
81
  end
@@ -41,7 +41,8 @@ module JenkinsApi
41
41
  end
42
42
 
43
43
  desc "restart", "Restarts the Jenkins server"
44
- method_option :force, :type => :boolean, :aliases => "-s", :desc => "Force restart"
44
+ method_option :force, :type => :boolean, :aliases => "-s",
45
+ :desc => "Force restart"
45
46
  def restart
46
47
  @client = Helper.setup(parent_options)
47
48
  force = options[:force] ? true : false
@@ -28,38 +28,34 @@ require 'active_support/core_ext'
28
28
  require 'active_support/builder'
29
29
  require 'base64'
30
30
 
31
- require File.expand_path('../version', __FILE__)
32
- require File.expand_path('../exceptions', __FILE__)
33
- require File.expand_path('../job', __FILE__)
34
- require File.expand_path('../system', __FILE__)
35
- require File.expand_path('../node', __FILE__)
36
-
37
31
  module JenkinsApi
38
32
  class Client
39
33
  attr_accessor :debug
40
- @debug = false
41
34
  DEFAULT_SERVER_PORT = 8080
42
35
  VALID_PARAMS = %w(server_ip server_port username password debug)
43
36
 
44
- # Initialize a Client object with Jenkins CI server information and credentials
37
+ # Initialize a Client object with Jenkins CI server credentials
45
38
  #
46
39
  # @param [Hash] args
47
40
  # * the +:server_ip+ param is the IP address of the Jenkins CI server
48
- # * the +:server_port+ param is the port on which the Jenkins server listens
49
- # * the +:username+ param is the username used for connecting to the CI server
41
+ # * the +:server_port+ param is the port on which the Jenkins listens
42
+ # * the +:username+ param is the username used for connecting to the server
50
43
  # * the +:password+ param is the password for connecting to the CI server
51
44
  #
52
45
  def initialize(args)
53
- args.each { |key, value|
46
+ args.each do |key, value|
54
47
  instance_variable_set("@#{key}", value) if value
55
- } if args.is_a? Hash
56
- raise "Server IP is required to connect to Jenkins Server" unless @server_ip
57
- raise "Credentials are required to connect to te Jenkins Server" unless @username && (@password || @password_base64)
48
+ end if args.is_a? Hash
49
+ raise "Server IP is required to connect to Jenkins" unless @server_ip
50
+ unless @username && (@password || @password_base64)
51
+ raise "Credentials are required to connect to te Jenkins Server"
52
+ end
58
53
  @server_port = DEFAULT_SERVER_PORT unless @server_port
54
+ @debug = false unless @debug
59
55
 
60
- # Base64 decode inserts a newline character at the end. As a workaround added chomp
61
- # to remove newline characters. I hope nobody uses newline characters at the end of
62
- # their passwords :)
56
+ # Base64 decode inserts a newline character at the end. As a workaround
57
+ # added chomp to remove newline characters. I hope nobody uses newline
58
+ # characters at the end of their passwords :)
63
59
  @password = Base64.decode64(@password_base64).chomp if @password_base64
64
60
  end
65
61
 
@@ -93,6 +89,12 @@ module JenkinsApi
93
89
  JenkinsApi::Client::View.new(self)
94
90
  end
95
91
 
92
+ # Creates an instance to the BuildQueue by passing a reference to self
93
+ #
94
+ def queue
95
+ JenkinsApi::Client::BuildQueue.new(self)
96
+ end
97
+
96
98
  # Returns a string representing the class name
97
99
  #
98
100
  def to_s
@@ -119,6 +121,7 @@ module JenkinsApi
119
121
  request = Net::HTTP::Get.new("#{url_prefix}#{url_suffix}?#{tree}") if tree
120
122
  request.basic_auth @username, @password
121
123
  response = http.request(request)
124
+ msg = "HTTP Code: #{response.code}, Response Body: #{response.body}"
122
125
  case response.code.to_i
123
126
  when 200
124
127
  if url_suffix =~ /json/
@@ -127,36 +130,39 @@ module JenkinsApi
127
130
  return response
128
131
  end
129
132
  when 401
130
- raise Exceptions::UnautherizedException.new("HTTP Code: #{response.code.to_s}, Response Body: #{response.body}")
133
+ raise Exceptions::UnautherizedException.new(msg)
131
134
  when 404
132
- raise Exceptions::NotFoundException.new("HTTP Code: #{response.code.to_s}, Response Body: #{response.body}")
135
+ raise Exceptions::NotFoundException.new(msg)
133
136
  when 500
134
- raise Exceptions::InternelServerErrorException.new("HTTP Code: #{response.code.to_s}, Response Body: #{response.body}")
137
+ raise Exceptions::InternelServerErrorException.new(msg)
135
138
  else
136
- raise Exceptions::ApiException.new("HTTP Code: #{response.code.to_s}, Response body: #{response.body}")
139
+ raise Exceptions::ApiException.new(msg)
137
140
  end
138
141
  end
139
142
 
140
143
  # Sends a POST message to the Jenkins CI server with the specified URL
141
144
  #
142
145
  # @param [String] url_prefix
146
+ # @param [Hash] form_data form data to send with POST request
143
147
  #
144
- def api_post_request(url_prefix)
148
+ def api_post_request(url_prefix, form_data = nil)
145
149
  http = Net::HTTP.start(@server_ip, @server_port)
146
150
  request = Net::HTTP::Post.new("#{url_prefix}")
147
151
  puts "[INFO] PUT #{url_prefix}" if @debug
148
152
  request.basic_auth @username, @password
149
153
  request.content_type = 'application/json'
154
+ request.set_form_data(form_data) unless form_data.nil?
150
155
  response = http.request(request)
156
+ msg = "HTTP Code: #{response.code}, Response Body: #{response.body}"
151
157
  case response.code.to_i
152
158
  when 200, 302
153
159
  return response.code
154
160
  when 404
155
- raise Exceptions::NotFoundException.new("HTTP Code: #{response.code.to_s}, Response Body: #{response.body}")
161
+ raise Exceptions::NotFoundException.new(msg)
156
162
  when 500
157
- raise Exceptions::InternelServerErrorException.new("HTTP Code: #{response.code.to_s}, Response Body: #{response.body}")
163
+ raise Exceptions::InternelServerErrorException.new(msg)
158
164
  else
159
- raise Exceptions::ApiException.new("HTTP Code: #{response.code.to_s}, Response body: #{response.body}")
165
+ raise Exceptions::ApiException.new(msg)
160
166
  end
161
167
  end
162
168
 
@@ -46,8 +46,8 @@ module JenkinsApi
46
46
  end
47
47
 
48
48
  # Create a job with params given as a hash instead of the xml
49
- # This gives some flexibility for creating simple jobs so the user doesn't have to
50
- # learn about handling xml.
49
+ # This gives some flexibility for creating simple jobs so the user
50
+ # doesn't have to learn about handling xml.
51
51
  #
52
52
  # @param [Hash] params
53
53
  # * +:name+ name of the job
@@ -55,33 +55,61 @@ module JenkinsApi
55
55
  # * +:block_build_when_downstream_building+ true or false
56
56
  # * +:block_build_when_upstream_building+ true or false
57
57
  # * +:concurrent_build+ true or false
58
- # * +:scm_provider+ type of source control system. Supported: git, subversion
58
+ # * +:scm_provider+ type of source control system. Supported: Git, SVN
59
59
  # * +:scm_url+ remote url for scm
60
+ # * +:scm_module+ Module to download. Only for CVS.
60
61
  # * +:scm_branch+ branch to use in scm. Uses master by default
62
+ # * +:scm_tag+ tag to download from scm. Only for CVS.
63
+ # * +:scm_use_head_if_tag_not_found+ Only for CVS.
61
64
  # * +:shell_command+ command to execute in the shell
62
65
  # * +:child_projects+ projects to add as downstream projects
63
- # * +:child_threshold+ threshold for child projects. success, failure, or unstable. Default: failure.
66
+ # * +:child_threshold+ threshold for child projects.
67
+ # success, failure, or unstable. Default: failure.
64
68
  #
65
69
  def create_freestyle(params)
66
- # TODO: Add support for all SCM providers supported by Jenkins
67
- supported_scm_providers = ['git', 'subversion']
70
+ # Supported SCM providers
71
+ supported_scm = ["git", "subversion", "cvs"]
68
72
 
69
- # Set default values for params that are not specified and Error handling.
73
+ # Set default values for params that are not specified.
70
74
  raise 'Job name must be specified' unless params[:name]
71
- params[:keep_dependencies] = false if params[:keep_dependencies].nil?
72
- params[:block_build_when_downstream_building] = false if params[:block_build_when_downstream_building].nil?
73
- params[:block_build_when_upstream_building] = false if params[:block_build_when_upstream_building].nil?
75
+ if params[:keep_dependencies].nil?
76
+ params[:keep_dependencies] = false
77
+ end
78
+ if params[:block_build_when_downstream_building].nil?
79
+ params[:block_build_when_downstream_building] = false
80
+ end
81
+ if params[:block_build_when_upstream_building].nil?
82
+ params[:block_build_when_upstream_building] = false
83
+ end
74
84
  params[:concurrent_build] = false if params[:concurrent_build].nil?
85
+ if params[:notification_email]
86
+ if params[:notification_email_for_every_unstable_build].nil?
87
+ params[:notification_email_for_every_unstable] = false
88
+ end
89
+ if params[:notification_email_send_to_individuals].nil?
90
+ params[:notification_email_send_to_individuals] ||= false
91
+ end
92
+ end
75
93
 
76
- # SCM configurations and Error handling. Presently only Git plugin is supported.
77
- unless supported_scm_providers.include?(params[:scm_provider]) || params[:scm_provider].nil?
94
+ # SCM configurations and Error handling.
95
+ unless supported_scm.include?(params[:scm_provider]) ||
96
+ params[:scm_provider].nil?
78
97
  raise "SCM #{params[:scm_provider]} is currently not supported"
79
98
  end
80
- raise 'SCM URL must be specified' if params[:scm_url].nil? && !params[:scm_provider].nil?
81
- params[:scm_branch] = "master" if params[:scm_branch].nil? && !params[:scm_provider].nil?
99
+ if params[:scm_url].nil? && !params[:scm_provider].nil?
100
+ raise 'SCM URL must be specified'
101
+ end
102
+ if params[:scm_branch].nil? && !params[:scm_provider].nil?
103
+ params[:scm_branch] = "master"
104
+ end
105
+ if params[:scm_use_head_if_tag_not_found].nil?
106
+ params[:scm_use_head_if_tag_not_found] = false
107
+ end
82
108
 
83
109
  # Child projects configuration and Error handling
84
- params[:child_threshold] = 'failure' if params[:child_threshold].nil? && !params[:child_projects].nil?
110
+ if params[:child_threshold].nil? && !params[:child_projects].nil?
111
+ params[:child_threshold] = 'failure'
112
+ end
85
113
 
86
114
  # Build the Job xml file based on the parameters given
87
115
  builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') { |xml|
@@ -92,7 +120,8 @@ module JenkinsApi
92
120
  xml.properties
93
121
  # SCM related stuff
94
122
  if params[:scm_provider] == 'subversion'
95
- xml.scm(:class => "hudson.scm.SubversionSCM", :plugin => "subversion@1.39") {
123
+ xml.scm(:class => "hudson.scm.SubversionSCM",
124
+ :plugin => "subversion@1.39") {
96
125
  xml.locations {
97
126
  xml.send("hudson.scm.SubversionSCM_-ModuleLocation") {
98
127
  xml.remote "#{params[:scm_url]}"
@@ -104,9 +133,31 @@ module JenkinsApi
104
133
  xml.excludedUsers
105
134
  xml.excludedRevprop
106
135
  xml.excludedCommitMessages
107
- xml.workspaceUpdater(:class => "hudson.scm.subversion.UpdateUpdater")
136
+ xml.workspaceUpdater(:class =>
137
+ "hudson.scm.subversion.UpdateUpdater")
138
+ }
139
+ elsif params[:scm_provider] == "cvs"
140
+ xml.scm(:class => "hudson.scm.CVSSCM",
141
+ :plugin => "cvs@1.6") {
142
+ xml.cvsroot "#{params[:scm_url]}"
143
+ xml.module "#{params[:scm_module]}"
144
+ if params[:scm_branch]
145
+ xml.branch "#{params[:scm_branch]}"
146
+ else
147
+ xml.branch "#{params[:scm_tag]}"
148
+ end
149
+ xml.canUseUpdate true
150
+ xml.useHeadIfNotFound(
151
+ "#{params[:scm_use_head_if_tag_not_found]}")
152
+ xml.flatten true
153
+ if params[:scm_tag]
154
+ xml.isTag true
155
+ else
156
+ xml.isTag false
157
+ end
158
+ xml.excludedRegions
108
159
  }
109
- elsif params[:scm_provider] == 'git'
160
+ elsif params[:scm_provider] == "git"
110
161
  xml.scm(:class => "hudson.plugins.git.GitSCM") {
111
162
  xml.configVersion "2"
112
163
  xml.userRemoteConfigs {
@@ -131,7 +182,8 @@ module JenkinsApi
131
182
  xml.remotePoll "false"
132
183
  xml.ignoreNotifyCommit "false"
133
184
  xml.useShallowClone "false"
134
- xml.buildChooser(:class => "hudson.plugins.git.util.DefaultBuildChooser")
185
+ xml.buildChooser(:class =>
186
+ "hudson.plugins.git.util.DefaultBuildChooser")
135
187
  xml.gitTool "Default"
136
188
  xml.submoduleCfg(:class => "list")
137
189
  xml.relativeTargetDir
@@ -147,10 +199,18 @@ module JenkinsApi
147
199
  else
148
200
  xml.scm(:class => "hudson.scm.NullSCM")
149
201
  end
150
- xml.canRoam "true"
202
+ # Restrict job to run in a specified node
203
+ if params[:restricted_node]
204
+ xml.assignedNode "#{params[:restricted_node]}"
205
+ xml.canRoam "false"
206
+ else
207
+ xml.canRoam "true"
208
+ end
151
209
  xml.disabled "false"
152
- xml.blockBuildWhenDownstreamBuilding "#{params[:block_build_when_downstream_building]}"
153
- xml.blockBuildWhenUpstreamBuilding "#{params[:block_build_when_upstream_building]}"
210
+ xml.blockBuildWhenDownstreamBuilding(
211
+ "#{params[:block_build_when_downstream_building]}")
212
+ xml.blockBuildWhenUpstreamBuilding(
213
+ "#{params[:block_build_when_upstream_building]}")
154
214
  xml.triggers.vector
155
215
  xml.concurrentBuild "#{params[:concurrent_build]}"
156
216
  # Shell command stuff
@@ -165,8 +225,9 @@ module JenkinsApi
165
225
  xml.publishers {
166
226
  if params[:child_projects]
167
227
  xml.send("hudson.tasks.BuildTrigger") {
168
- xml.childProjects"#{params[:child_projects]}"
169
- name, ordinal, color = get_threshold_params(params[:child_threshold])
228
+ xml.childProjects "#{params[:child_projects]}"
229
+ threshold = params[:child_threshold]
230
+ name, ordinal, color = get_threshold_params(threshold)
170
231
  xml.threshold {
171
232
  xml.name "#{name}"
172
233
  xml.ordinal "#{ordinal}"
@@ -174,6 +235,15 @@ module JenkinsApi
174
235
  }
175
236
  }
176
237
  end
238
+ if params[:notification_email]
239
+ xml.send("hudson.tasks.Mailer") {
240
+ xml.recipients "#{params[:notification_email]}"
241
+ xml.dontNotifyEveryUnstableBuild(
242
+ "#{params[:notification_email_for_every_unstable]}")
243
+ xml.sendToIndividuals(
244
+ "#{params[:notification_email_send_to_individuals]}")
245
+ }
246
+ end
177
247
  }
178
248
  xml.buildWrappers
179
249
  }
@@ -191,7 +261,8 @@ module JenkinsApi
191
261
 
192
262
  # Stops a running build of a job
193
263
  # This method will stop the current/most recent build if no build number
194
- # is specified. The build will be stopped only if it was in 'running' state.
264
+ # is specified. The build will be stopped only if it was
265
+ # in 'running' state.
195
266
  #
196
267
  # @param [String] job_name
197
268
  # @param [Number] build_number
@@ -200,8 +271,12 @@ module JenkinsApi
200
271
  build_number = get_current_build_number(job_name) if build_number == 0
201
272
  raise "No builds for #{job_name}" unless build_number
202
273
  # Check and see if the build is running
203
- is_building = @client.api_get_request("/job/#{job_name}/#{build_number}")["building"]
204
- @client.api_post_request("/job/#{job_name}/#{build_number}/stop") if is_building
274
+ is_building = @client.api_get_request(
275
+ "/job/#{job_name}/#{build_number}"
276
+ )["building"]
277
+ if is_building
278
+ @client.api_post_request("/job/#{job_name}/#{build_number}/stop")
279
+ end
205
280
  end
206
281
 
207
282
  # Re-create the same job
@@ -218,18 +293,21 @@ module JenkinsApi
218
293
  # Get progressive console output from Jenkins server for a job
219
294
  #
220
295
  # @param [String] job_name Name of the Jenkins job
221
- # @param [Number] build_number Specific build number to obtain the console output from. Default is the recent build
296
+ # @param [Number] build_number Specific build number to obtain the
297
+ # console output from. Default is the recent build
222
298
  # @param [Number] start start offset to get only a portion of the text
223
299
  # @param [String] mode Mode of text output. 'text' or 'html'
224
300
  #
225
301
  # @return [Hash] response
226
302
  # * +output+ Console output of the job
227
- # * +size+ Size of the text. This can be used as 'start' for the next call to get progressive output
228
- # * +more+ More data available for the job. 'true' if available and nil otherwise
229
- #
230
- def get_console_output(job_name, build_number = 0, start = 0, mode = 'text')
231
- build_number = get_current_build_number(job_name) if build_number == 0
232
- if build_number == 0
303
+ # * +size+ Size of the text. This can be used as 'start' for the
304
+ # next call to get progressive output
305
+ # * +more+ More data available for the job. 'true' if available
306
+ # and nil otherwise
307
+ #
308
+ def get_console_output(job_name, build_num = 0, start = 0, mode = 'text')
309
+ build_num = get_current_build_number(job_name) if build_num == 0
310
+ if build_num == 0
233
311
  puts "No builds for this job '#{job_name}' yet."
234
312
  return nil
235
313
  end
@@ -240,7 +318,9 @@ module JenkinsApi
240
318
  else
241
319
  raise "Mode should either be 'text' or 'html'. You gave: #{mode}"
242
320
  end
243
- api_response = @client.api_get_request("/job/#{job_name}/#{build_number}/logText/progressive#{mode}?start=#{start}", nil, nil)
321
+ get_msg = "/job/#{job_name}/#{build_num}/logText/progressice#{mode}?"
322
+ get_msg << "start=#{start}"
323
+ api_response = @client.api_get_request(get_msg, nil, nil)
244
324
  #puts "Response: #{api_response.header['x-more-data']}"
245
325
  response = {}
246
326
  response['output'] = api_response.body
@@ -278,7 +358,10 @@ module JenkinsApi
278
358
  xml_response = @client.api_get_request("", "tree=jobs[name,color]")
279
359
  filtered_jobs = []
280
360
  xml_response["jobs"].each do |job|
281
- filtered_jobs << job["name"] if color_to_status(job["color"]) == status && jobs.include?(job["name"])
361
+ if color_to_status(job["color"]) == status &&
362
+ jobs.include?(job["name"])
363
+ filtered_jobs << job["name"]
364
+ end
282
365
  end
283
366
  filtered_jobs
284
367
  end
@@ -347,6 +430,8 @@ module JenkinsApi
347
430
  #
348
431
  # @param [String] color color given by the API for a job
349
432
  #
433
+ # @return [String] status status of the given job matching the color
434
+ #
350
435
  def color_to_status(color)
351
436
  case color
352
437
  when "blue"
@@ -372,37 +457,30 @@ module JenkinsApi
372
457
  #
373
458
  # @param [String] job_name
374
459
  #
460
+ # @return [String] status current status of the given job
461
+ #
375
462
  def get_current_build_status(job_name)
376
463
  response_json = @client.api_get_request("/job/#{job_name}")
377
464
  color_to_status(response_json["color"])
378
465
  end
379
466
 
380
467
  # Obtain the current build number of the given job
381
- # This function returns nil if there were no builds for the given job name.
468
+ # This function returns nil if there were no builds for the given job.
382
469
  #
383
470
  # @param [String] job_name
384
471
  #
385
- def get_current_build_number(job_name)
386
- @client.api_get_request("/job/#{job_name}")['nextBuildNumber'] - 1
387
- end
388
-
389
- # This functions lists all jobs that are currently running on the Jenkins CI server
390
- # This method is deprecated. Please use list_by_status instead.
472
+ # @return [Number] build_unumber current build number of the given job
391
473
  #
392
- def list_running
393
- puts "[WARN] list_running is deprecated. Please use list_by_status('running') instead."
394
- xml_response = @client.api_get_request("", "tree=jobs[name,color]")
395
- running_jobs = []
396
- xml_response["jobs"].each { |job|
397
- running_jobs << job["name"] if color_to_status(job["color"]) == "running"
398
- }
399
- running_jobs
474
+ def get_current_build_number(job_name)
475
+ @client.api_get_request("/job/#{job_name}")['nextBuildNumber'].to_i - 1
400
476
  end
401
477
 
402
478
  # Build a job given the name of the job
403
479
  #
404
480
  # @param [String] job_name
405
481
  #
482
+ # @return [String] response_code return code from HTTP POST
483
+ #
406
484
  def build(job_name)
407
485
  @client.api_post_request("/job/#{job_name}/build")
408
486
  end
@@ -411,6 +489,8 @@ module JenkinsApi
411
489
  #
412
490
  # @param [String] job_name
413
491
  #
492
+ # @return [String] XML Config.xml of the job
493
+ #
414
494
  def get_config(job_name)
415
495
  @client.get_config("/job/#{job_name}")
416
496
  end
@@ -420,6 +500,8 @@ module JenkinsApi
420
500
  # @param [String] job_name
421
501
  # @param [String] xml
422
502
  #
503
+ # @return [String] response_code return code from HTTP POST
504
+ #
423
505
  def post_config(job_name, xml)
424
506
  @client.post_config("/job/#{job_name}/config.xml", xml)
425
507
  end
@@ -429,6 +511,8 @@ module JenkinsApi
429
511
  # @param [String] job_name
430
512
  # @param [String] description
431
513
  #
514
+ # @return [String] response_code return code from HTTP POST
515
+ #
432
516
  def change_description(job_name, description)
433
517
  xml = get_config(job_name)
434
518
  n_xml = Nokogiri::XML(xml)
@@ -442,6 +526,8 @@ module JenkinsApi
442
526
  #
443
527
  # @param [String] job_name
444
528
  #
529
+ # @return [String] response_code return code from HTTP POST
530
+ #
445
531
  def block_build_when_downstream_building(job_name)
446
532
  xml = get_config(job_name)
447
533
  n_xml = Nokogiri::XML(xml)
@@ -457,6 +543,8 @@ module JenkinsApi
457
543
  #
458
544
  # @param [String] job_name
459
545
  #
546
+ # @return [String] response_code return code from HTTP POST
547
+ #
460
548
  def unblock_build_when_downstream_building(job_name)
461
549
  xml = get_config(job_name)
462
550
  n_xml = Nokogiri::XML(xml)
@@ -472,6 +560,8 @@ module JenkinsApi
472
560
  #
473
561
  # @param [String] job_name
474
562
  #
563
+ # @return [String] response_code return code from HTTP POST
564
+ #
475
565
  def block_build_when_upstream_building(job_name)
476
566
  xml = get_config(job_name)
477
567
  n_xml = Nokogiri::XML(xml)
@@ -487,6 +577,8 @@ module JenkinsApi
487
577
  #
488
578
  # @param [String] job_name
489
579
  #
580
+ # @return [String] response_code return code from HTTP POST
581
+ #
490
582
  def unblock_build_when_upstream_building(job_name)
491
583
  xml = get_config(job_name)
492
584
  n_xml = Nokogiri::XML(xml)
@@ -498,11 +590,13 @@ module JenkinsApi
498
590
  end
499
591
  end
500
592
 
501
- # Allow to either execute concurrent builds or disable concurrent execution
593
+ # Allow or disable concurrent build execution
502
594
  #
503
595
  # @param [String] job_name
504
596
  # @param [Bool] option true or false
505
597
  #
598
+ # @return [String] response_code return code from HTTP POST
599
+ #
506
600
  def execute_concurrent_builds(job_name, option)
507
601
  xml = get_config(job_name)
508
602
  n_xml = Nokogiri::XML(xml)
@@ -519,7 +613,7 @@ module JenkinsApi
519
613
  #
520
614
  # @param [String] job_name
521
615
  #
522
- # @return [Array] params_array
616
+ # @return [Array] params_array Array of parameters for the given job
523
617
  #
524
618
  def get_build_params(job_name)
525
619
  xml = get_config(job_name)
@@ -530,39 +624,58 @@ module JenkinsApi
530
624
  params.children.each do |param|
531
625
  param_hash = {}
532
626
  case param.name
533
- when "hudson.model.StringParameterDefinition", "hudson.model.BooleanParameterDefinition", "hudson.model.TextParameterDefinition", "hudson.model.PasswordParameterDefinition"
627
+ when "hudson.model.StringParameterDefinition",
628
+ "hudson.model.BooleanParameterDefinition",
629
+ "hudson.model.TextParameterDefinition",
630
+ "hudson.model.PasswordParameterDefinition"
534
631
  param_hash[:type] = 'string' if param.name =~ /string/i
535
632
  param_hash[:type] = 'boolean' if param.name =~ /boolean/i
536
633
  param_hash[:type] = 'text' if param.name =~ /text/i
537
634
  param_hash[:type] = 'password' if param.name =~ /password/i
538
635
  param.children.each do |value|
539
636
  param_hash[:name] = value.content if value.name == "name"
540
- param_hash[:description] = value.content if value.name == "description"
541
- param_hash[:default] = value.content if value.name == "defaultValue"
637
+ if value.name == "description"
638
+ param_hash[:description] = value.content
639
+ end
640
+ if value.name == "defaultValue"
641
+ param_hash[:default] = value.content
642
+ end
542
643
  end
543
644
  when "hudson.model.RunParameterDefinition"
544
645
  param_hash[:type] = 'run'
545
646
  param.children.each do |value|
546
647
  param_hash[:name] = value.content if value.name == "name"
547
- param_hash[:description] = value.content if value.name == "description"
548
- param_hash[:project] = value.content if value.name == "projectName"
648
+ if value.name == "description"
649
+ param_hash[:description] = value.content
650
+ end
651
+ if value.name == "projectName"
652
+ param_hash[:project] = value.content
653
+ end
549
654
  end
550
655
  when "hudson.model.FileParameterDefinition"
551
656
  param_hash[:type] = 'file'
552
657
  param.children.each do |value|
553
658
  param_hash[:name] = value.content if value.name == "name"
554
- param_hash[:description] = value.content if value.name == "description"
659
+ param_hash[:description] = value.content \
660
+ if value.name == "description"
555
661
  end
556
662
  when "hudson.scm.listtagsparameter.ListSubversionTagsParameterDefinition"
557
663
  param_hash[:type] = 'list_tags'
558
664
  param.children.each do |value|
559
- param_hash[:name] = value.content if value.name == "name"
560
- param_hash[:description] = value.content if value.name == "description"
561
- param_hash[:tags_dir] = value.content if value.name == "tagsDir"
562
- param_hash[:tags_filter] = value.content if value.name == "tagsFilter"
563
- param_hash[:reverse_by_date] = value.content if value.name == "reverseByDate"
564
- param_hash[:reverse_by_name] = value.content if value.name == "reverseByName"
565
- param_hash[:default] = value.content if value.name == "defaultValue"
665
+ param_hash[:name] = value.content \
666
+ if value.name == "name"
667
+ param_hash[:description] = value.content \
668
+ if value.name == "description"
669
+ param_hash[:tags_dir] = value.content \
670
+ if value.name == "tagsDir"
671
+ param_hash[:tags_filter] = value.content \
672
+ if value.name == "tagsFilter"
673
+ param_hash[:reverse_by_date] = value.content \
674
+ if value.name == "reverseByDate"
675
+ param_hash[:reverse_by_name] = value.content \
676
+ if value.name == "reverseByName"
677
+ param_hash[:default] = value.content \
678
+ if value.name == "defaultValue"
566
679
  param_hash[:max_tags] = value.content if value.name == "maxTags"
567
680
  param_hash[:uuid] = value.content if value.name == "uuid"
568
681
  end
@@ -570,13 +683,15 @@ module JenkinsApi
570
683
  param_hash[:type] = 'choice'
571
684
  param.children.each do |value|
572
685
  param_hash[:name] = value.content if value.name == "name"
573
- param_hash[:description] = value.content if value.name == "description"
686
+ param_hash[:description] = value.content \
687
+ if value.name == "description"
574
688
  choices = []
575
689
  if value.name == "choices"
576
690
  value.children.each do |value_child|
577
691
  if value_child.name == "a"
578
692
  value_child.children.each do |choice_child|
579
- choices << choice_child.content.strip unless choice_child.content.strip.empty?
693
+ choices << choice_child.content.strip \
694
+ unless choice_child.content.strip.empty?
580
695
  end
581
696
  end
582
697
  end
@@ -585,15 +700,18 @@ module JenkinsApi
585
700
  end
586
701
  end
587
702
  params_array << param_hash unless param_hash.empty?
588
- end
589
- end
590
- params_array
703
+ end
704
+ end
705
+ params_array
591
706
  end
592
707
 
593
- # Obtains the threshold params used by jenkins in the XML file given the threshold
708
+ # Obtains the threshold params used by jenkins in the XML file
709
+ # given the threshold
594
710
  #
595
711
  # @param [String] threshold success, failure, or unstable
596
712
  #
713
+ # @return [String] status readable status matching the color
714
+ #
597
715
  def get_threshold_params(threshold)
598
716
  case threshold
599
717
  when 'success'
@@ -612,16 +730,20 @@ module JenkinsApi
612
730
  return name, ordinal, color
613
731
  end
614
732
 
615
- # Add downstream projects to a specific job given the job name, projects to be
616
- # added as downstream projects, and the threshold
733
+ # Add downstream projects to a specific job given the job name,
734
+ # projects to be added as downstream projects, and the threshold
617
735
  #
618
736
  # @param [String] job_name
619
737
  # @param [String] downstream_projects
620
738
  # @param [String] threshold - failure, success, or unstable
621
739
  # @param [Bool] overwrite - true or false
622
740
  #
623
- def add_downstream_projects(job_name, downstream_projects, threshold, overwrite = false)
624
- name, ordinal, color = get_threshold_params(threshold)
741
+ # @return [String] response_code return code from HTTP POST
742
+ #
743
+ def add_downstream_projects(job_name,
744
+ downstream_projects,
745
+ threshold, overwrite = false)
746
+ name, ord, col = get_threshold_params(threshold)
625
747
  xml = get_config(job_name)
626
748
  n_xml = Nokogiri::XML(xml)
627
749
  child_projects_node = n_xml.xpath("//childProjects").first
@@ -629,14 +751,24 @@ module JenkinsApi
629
751
  if overwrite
630
752
  child_projects_node.content = "#{downstream_projects}"
631
753
  else
632
- child_projects_node.content = child_projects_node.content + ", #{downstream_projects}"
754
+ to_replace = child_projects_node.content +
755
+ ", #{downstream_projects}"
756
+ child_projects_node.content = to_replace
633
757
  end
634
758
  else
635
759
  publisher_node = n_xml.xpath("//publishers").first
636
- build_trigger_node = publisher_node.add_child("<hudson.tasks.BuildTrigger/>")
637
- child_project_node = build_trigger_node.first.add_child("<childProjects>#{downstream_projects}</childProjects>")
638
- threshold_node = child_project_node.first.add_next_sibling("<threshold/>")
639
- threshold_node.first.add_child("<name>#{name}</name><ordinal>#{ordinal}</ordinal><color>#{color}</color>")
760
+ build_trigger_node = publisher_node.add_child(
761
+ "<hudson.tasks.BuildTrigger/>"
762
+ )
763
+ child_project_node = build_trigger_node.first.add_child(
764
+ "<childProjects>#{downstream_projects}</childProjects>"
765
+ )
766
+ threshold_node = child_project_node.first.add_next_sibling(
767
+ "<threshold/>"
768
+ )
769
+ threshold_node.first.add_child(
770
+ "<name>#{name}</name><ordinal>#{ord}</ordinal><color>#{col}</color>"
771
+ )
640
772
  end
641
773
  xml_modified = n_xml.to_xml
642
774
  post_config(job_name, xml_modified)
@@ -646,6 +778,8 @@ module JenkinsApi
646
778
  #
647
779
  # @param [String] job_name
648
780
  #
781
+ # @return [String] response_code return code from HTTP POST
782
+ #
649
783
  def remove_downstream_projects(job_name)
650
784
  xml = get_config(job_name)
651
785
  n_xml = Nokogiri::XML(xml)
@@ -678,6 +812,8 @@ module JenkinsApi
678
812
  # @param [String] job_name
679
813
  # @param [String] node_name
680
814
  #
815
+ # @return [String] response_code return code from HTTP POST
816
+ #
681
817
  def restrict_to_node(job_name, node_name)
682
818
  xml = get_config(job_name)
683
819
  n_xml = Nokogiri::XML(xml)
@@ -685,7 +821,7 @@ module JenkinsApi
685
821
  node.content = node_name
686
822
  else
687
823
  project = n_xml.xpath("//scm").first
688
- child_node = project.add_next_sibling("<assignedNode>#{node_name}</assignedNode>")
824
+ project.add_next_sibling("<assignedNode>#{node_name}</assignedNode>")
689
825
  roam_node = n_xml.xpath("//canRoam").first
690
826
  roam_node.content = "false"
691
827
  end
@@ -698,37 +834,54 @@ module JenkinsApi
698
834
  # @param [Array] job_names Array of job names to be unchained
699
835
  #
700
836
  def unchain(job_names)
701
- job_names.each { |job|
702
- puts "[INFO] Removing downstream projects for <#{job}>" if @client.debug
703
- @client.job.remove_downstream_projects(job)
704
- }
837
+ job_names.each do |job|
838
+ log_msg = "[INFO] Removing downstream projects for <#{job}>"
839
+ puts log_msg if @client.debug
840
+ remove_downstream_projects(job)
841
+ end
705
842
  end
706
843
 
707
844
  # Chain the jobs given based on specified criteria
708
845
  #
709
846
  # @param [Array] job_names Array of job names to be chained
710
- # @param [String] threshold what should be the threshold for running the next job
711
- # @param [Array] criteria criteria which should be applied for picking the jobs for the chain
712
- # @param [Integer] parallel Number of jobs that should be considered for parallel run
713
- #
847
+ # @param [String] threshold threshold for running the next job
848
+ # @param [Array] criteria criteria which should be applied for
849
+ # picking the jobs for the chain
850
+ # @param [Integer] parallel Number of jobs that should be considered
851
+ # for parallel run
852
+ #
853
+ # @return [Array] job_names Names of jobs that are in the top of the
854
+ # chain
714
855
  def chain(job_names, threshold, criteria, parallel = 1)
715
856
  raise "Parallel jobs should be at least 1" if parallel < 1
716
857
  unchain(job_names)
858
+
717
859
  filtered_job_names = []
718
860
  if criteria.include?("all") || criteria.empty?
719
861
  filtered_job_names = job_names
720
862
  else
721
- puts "[INFO] Criteria is specified. Filtering jobs..." if @client.debug
863
+ log_msg = "[INFO] Criteria is specified. Filtering jobs..."
864
+ puts log_msg if @client.debug
722
865
  job_names.each do |job|
723
- filtered_job_names << job if criteria.include?(@client.job.get_current_build_status(job))
866
+ filtered_job_names << job if criteria.include?(
867
+ @client.job.get_current_build_status(job)
868
+ )
724
869
  end
725
870
  end
871
+
726
872
  filtered_job_names.each_with_index do |job_name, index|
727
873
  break if index >= (filtered_job_names.length - parallel)
728
- puts "[INFO] Adding <#{filtered_job_names[index+1]}> as a downstream project to <#{job_name}> with <#{threshold}> as the threshold" if @client.debug
729
- @client.job.add_downstream_projects(job_name, filtered_job_names[index + parallel], threshold, true)
874
+ msg = "[INFO] Adding <#{filtered_job_names[index+1]}> as a"
875
+ msg << " downstream project to <#{job_name}> with <#{threshold}> as"
876
+ msg << " the threshold"
877
+ puts msg if @client.debug
878
+ @client.job.add_downstream_projects(
879
+ job_name, filtered_job_names[index + parallel], threshold, true
880
+ )
881
+ end
882
+ if parallel > filtered_job_names.length
883
+ parallel = filtered_job_names.length
730
884
  end
731
- parallel = filtered_job_names.length if parallel > filtered_job_names.length
732
885
  filtered_job_names[0..parallel-1]
733
886
  end
734
887