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.
- data/.gitignore +3 -0
- data/.jenkins.yml +9 -0
- data/.travis.yml +11 -15
- data/CHANGELOG.md +15 -0
- data/Gemfile +2 -2
- data/README.md +7 -9
- data/Rakefile +27 -14
- data/lib/jenkins_api_client.rb +36 -6
- data/lib/jenkins_api_client/build_queue.rb +213 -0
- data/lib/jenkins_api_client/cli/base.rb +10 -6
- data/lib/jenkins_api_client/cli/helper.rb +13 -4
- data/lib/jenkins_api_client/cli/job.rb +6 -9
- data/lib/jenkins_api_client/cli/node.rb +6 -4
- data/lib/jenkins_api_client/cli/system.rb +2 -1
- data/lib/jenkins_api_client/client.rb +31 -25
- data/lib/jenkins_api_client/job.rb +248 -95
- data/lib/jenkins_api_client/node.rb +128 -10
- data/lib/jenkins_api_client/system.rb +4 -2
- data/lib/jenkins_api_client/version.rb +2 -2
- data/lib/jenkins_api_client/view.rb +17 -4
- data/scripts/login_with_irb.rb +4 -3
- data/spec/func_tests/client_spec.rb +90 -0
- data/spec/func_tests/job_spec.rb +348 -0
- data/spec/func_tests/node_spec.rb +174 -0
- data/spec/{spec_helper.rb → func_tests/spec_helper.rb} +2 -2
- data/spec/func_tests/system_spec.rb +55 -0
- data/spec/func_tests/view_spec.rb +53 -0
- data/spec/unit_tests/client_spec.rb +211 -0
- data/spec/unit_tests/fixtures/files/computer_sample.xml +17 -0
- data/spec/unit_tests/fixtures/files/job_sample.xml +16 -0
- data/spec/unit_tests/job_spec.rb +355 -0
- data/spec/unit_tests/node_spec.rb +192 -0
- data/spec/unit_tests/spec_helper.rb +8 -0
- data/spec/unit_tests/system_spec.rb +54 -0
- data/spec/unit_tests/view_spec.rb +127 -0
- metadata +34 -23
- data/spec/client_spec.rb +0 -52
- data/spec/job_spec.rb +0 -158
- data/spec/node_spec.rb +0 -48
- 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,
|
36
|
-
class_option :password,
|
37
|
-
|
38
|
-
class_option :
|
39
|
-
|
40
|
-
class_option :
|
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] &&
|
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(
|
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(
|
37
|
+
creds = YAML.load_file(
|
38
|
+
File.expand_path(
|
39
|
+
"#{ENV['HOME']}/.jenkins_api_client/login.yml", __FILE__
|
40
|
+
)
|
41
|
+
)
|
35
42
|
else
|
36
|
-
|
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",
|
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",
|
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
|
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",
|
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 =
|
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 =
|
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 =
|
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",
|
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
|
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
|
49
|
-
# * the +:username+ param is the username used for connecting to the
|
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
|
46
|
+
args.each do |key, value|
|
54
47
|
instance_variable_set("@#{key}", value) if value
|
55
|
-
|
56
|
-
raise "Server IP is required to connect to Jenkins
|
57
|
-
|
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
|
61
|
-
# to remove newline characters. I hope nobody uses newline
|
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(
|
133
|
+
raise Exceptions::UnautherizedException.new(msg)
|
131
134
|
when 404
|
132
|
-
raise Exceptions::NotFoundException.new(
|
135
|
+
raise Exceptions::NotFoundException.new(msg)
|
133
136
|
when 500
|
134
|
-
raise Exceptions::InternelServerErrorException.new(
|
137
|
+
raise Exceptions::InternelServerErrorException.new(msg)
|
135
138
|
else
|
136
|
-
raise Exceptions::ApiException.new(
|
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(
|
161
|
+
raise Exceptions::NotFoundException.new(msg)
|
156
162
|
when 500
|
157
|
-
raise Exceptions::InternelServerErrorException.new(
|
163
|
+
raise Exceptions::InternelServerErrorException.new(msg)
|
158
164
|
else
|
159
|
-
raise Exceptions::ApiException.new(
|
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
|
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:
|
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.
|
66
|
+
# * +:child_threshold+ threshold for child projects.
|
67
|
+
# success, failure, or unstable. Default: failure.
|
64
68
|
#
|
65
69
|
def create_freestyle(params)
|
66
|
-
#
|
67
|
-
|
70
|
+
# Supported SCM providers
|
71
|
+
supported_scm = ["git", "subversion", "cvs"]
|
68
72
|
|
69
|
-
# Set default values for params that are not specified
|
73
|
+
# Set default values for params that are not specified.
|
70
74
|
raise 'Job name must be specified' unless params[:name]
|
71
|
-
|
72
|
-
|
73
|
-
|
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.
|
77
|
-
unless
|
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
|
-
|
81
|
-
|
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
|
-
|
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",
|
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 =>
|
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] ==
|
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 =>
|
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
|
-
|
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
|
153
|
-
|
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
|
-
|
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
|
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(
|
204
|
-
|
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
|
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
|
228
|
-
#
|
229
|
-
#
|
230
|
-
|
231
|
-
|
232
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
393
|
-
|
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
|
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",
|
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
|
-
|
541
|
-
|
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
|
-
|
548
|
-
|
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
|
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
|
560
|
-
|
561
|
-
param_hash[:
|
562
|
-
|
563
|
-
param_hash[:
|
564
|
-
|
565
|
-
param_hash[:
|
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
|
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
|
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
|
-
|
589
|
-
|
590
|
-
|
703
|
+
end
|
704
|
+
end
|
705
|
+
params_array
|
591
706
|
end
|
592
707
|
|
593
|
-
# Obtains the threshold params used by jenkins in the XML file
|
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,
|
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
|
-
|
624
|
-
|
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
|
-
|
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(
|
637
|
-
|
638
|
-
|
639
|
-
|
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
|
-
|
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
|
702
|
-
|
703
|
-
@client.
|
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
|
711
|
-
# @param [Array] criteria criteria which should be applied for
|
712
|
-
#
|
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
|
-
|
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?(
|
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
|
-
|
729
|
-
|
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
|
|