jenkins_api_client 0.6.2 → 0.7.0
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.
- 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
|
|