ruby_bugzilla 0.4.2 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/ruby_bugzilla.rb +109 -10
- data/lib/ruby_bugzilla/version.rb +1 -1
- data/spec/ruby_bugzilla_spec.rb +143 -0
- metadata +3 -9
data/lib/ruby_bugzilla.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'linux_admin'
|
2
|
+
require "xmlrpc/client"
|
2
3
|
|
3
4
|
class RubyBugzilla
|
5
|
+
CLONE_FIELDS = [:assigned_to, :cc, :cf_devel_whiteboard, :cf_internal_whiteboard, :component,
|
6
|
+
:groups, :keywords, :op_sys, :platform, :priority, :product, :qa_contact, :severity,
|
7
|
+
:summary, :target_release, :url, :version, :whiteboard, :comments, :description,]
|
4
8
|
CMD = `which bugzilla`.chomp
|
5
9
|
COOKIES_FILE = File.expand_path('~/.bugzillacookies')
|
6
10
|
|
@@ -16,11 +20,12 @@ class RubyBugzilla
|
|
16
20
|
File.delete(COOKIES_FILE) if File.exists?(COOKIES_FILE)
|
17
21
|
end
|
18
22
|
|
19
|
-
attr_accessor :bugzilla_uri, :username, :password, :last_command
|
20
|
-
attr_reader :bugzilla_request_uri
|
23
|
+
attr_accessor :bugzilla_uri, :username, :password, :last_command, :xmlrpc
|
24
|
+
attr_reader :bugzilla_request_uri, :bugzilla_request_hostname
|
21
25
|
|
22
26
|
def bugzilla_uri=(value)
|
23
27
|
@bugzilla_request_uri = URI.join(value, "xmlrpc.cgi").to_s
|
28
|
+
@bugzilla_request_hostname = URI(value).hostname
|
24
29
|
@bugzilla_uri = value
|
25
30
|
end
|
26
31
|
|
@@ -31,6 +36,8 @@ class RubyBugzilla
|
|
31
36
|
self.bugzilla_uri = bugzilla_uri
|
32
37
|
self.username = username
|
33
38
|
self.password = password
|
39
|
+
self.xmlrpc = ::XMLRPC::Client.new(bugzilla_request_hostname, '/xmlrpc.cgi', 443, nil,
|
40
|
+
nil, username, password, true, 60)
|
34
41
|
end
|
35
42
|
|
36
43
|
def inspect
|
@@ -60,7 +67,7 @@ class RubyBugzilla
|
|
60
67
|
params["login"] = [username, password]
|
61
68
|
|
62
69
|
begin
|
63
|
-
|
70
|
+
execute_shell(params)
|
64
71
|
rescue
|
65
72
|
clear_login! # A failed login attempt could result in a corrupt COOKIES_FILE
|
66
73
|
raise
|
@@ -93,7 +100,7 @@ class RubyBugzilla
|
|
93
100
|
params["query"] = nil
|
94
101
|
set_params_options(params, options)
|
95
102
|
|
96
|
-
|
103
|
+
execute_shell(params)
|
97
104
|
end
|
98
105
|
|
99
106
|
# Modify an existing bug or set of bugs
|
@@ -123,16 +130,83 @@ class RubyBugzilla
|
|
123
130
|
params["modify"] = bug_ids
|
124
131
|
set_params_options(params, options)
|
125
132
|
|
126
|
-
|
133
|
+
execute_shell(params)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Clone of an existing bug
|
137
|
+
#
|
138
|
+
# Example:
|
139
|
+
# # Perform a clone of an existing bug, and return the new bug ID.
|
140
|
+
# bz.clone(948970)
|
141
|
+
#
|
142
|
+
# @param bug_id [String, Fixnum] A single bug id to process.
|
143
|
+
# @param overrides [Hash] The properties to change from the source bug. Some properties include
|
144
|
+
# * <tt>:target_release</tt> - The target release for the new cloned bug.
|
145
|
+
# * <tt>:assigned_to</tt> - The person to assign the new cloned bug to.
|
146
|
+
# @return [Fixnum] The bug id to the new, cloned, bug.
|
147
|
+
def clone(bug_id, overrides={})
|
148
|
+
raise ArgumentError, "bug_id must be numeric" unless bug_id.to_s =~ /^\d+$/
|
149
|
+
|
150
|
+
existing_bz = xmlrpc_bug_query(bug_id)
|
151
|
+
|
152
|
+
clone_description, clone_comment_is_private = assemble_clone_description(existing_bz)
|
153
|
+
|
154
|
+
params = {}
|
155
|
+
CLONE_FIELDS.each do |field|
|
156
|
+
next if field == :comments
|
157
|
+
params[field] = existing_bz[field.to_s]
|
158
|
+
end
|
159
|
+
|
160
|
+
# Apply overrides
|
161
|
+
overrides.each do |param, value|
|
162
|
+
params[param] = value
|
163
|
+
end
|
164
|
+
|
165
|
+
# Apply base clone fields
|
166
|
+
params[:cf_clone_of] = bug_id
|
167
|
+
params[:description] = clone_description
|
168
|
+
params[:comment_is_private] = clone_comment_is_private
|
169
|
+
|
170
|
+
execute_xmlrpc('create', params)[:id.to_s]
|
171
|
+
end
|
172
|
+
|
173
|
+
# XMLRPC Bug Query of an existing bug
|
174
|
+
#
|
175
|
+
# Example:
|
176
|
+
# # Perform an xmlrpc query for a single bug.
|
177
|
+
# bz.xmlrpc_bug_query(948970)
|
178
|
+
#
|
179
|
+
# @param bug_id [String, Fixnum] A single bug id to process.
|
180
|
+
# @return [Fixnum] The bug id to the new, cloned, bug.
|
181
|
+
def xmlrpc_bug_query(bug_id)
|
182
|
+
raise ArgumentError, "bug_id must be numeric" unless bug_id.to_s =~ /^\d+$/
|
183
|
+
|
184
|
+
params = {}
|
185
|
+
params[:Bugzilla_login] = username
|
186
|
+
params[:Bugzilla_password] = password
|
187
|
+
params[:ids] = bug_id
|
188
|
+
params[:include_fields] = CLONE_FIELDS
|
189
|
+
|
190
|
+
execute_xmlrpc('get', params)['bugs'].last
|
127
191
|
end
|
128
192
|
|
129
193
|
private
|
194
|
+
def assemble_clone_description(existing_bz)
|
195
|
+
clone_description = " +++ This bug was initially created as a clone of Bug ##{existing_bz[:id.to_s]} +++ \n"
|
196
|
+
clone_description << existing_bz[:description.to_s]
|
130
197
|
|
131
|
-
|
132
|
-
|
198
|
+
clone_comment_is_private = false
|
199
|
+
existing_bz[:comments.to_s].each do |comment|
|
200
|
+
clone_description << "\n\n"
|
201
|
+
clone_description << "*" * 70
|
202
|
+
clone_description << "\nFollowing comment by %s on %s\n\n" %
|
203
|
+
[comment['author'], comment['creation_time'].to_time]
|
204
|
+
clone_description << "\n\n"
|
205
|
+
clone_description << comment['text']
|
206
|
+
clone_comment_is_private = true if comment['is_private']
|
207
|
+
end
|
133
208
|
|
134
|
-
|
135
|
-
LinuxAdmin.run!(CMD, :params => params).output
|
209
|
+
[clone_description, clone_comment_is_private]
|
136
210
|
end
|
137
211
|
|
138
212
|
def set_params_options(params, options)
|
@@ -141,7 +215,24 @@ class RubyBugzilla
|
|
141
215
|
end
|
142
216
|
end
|
143
217
|
|
144
|
-
|
218
|
+
# Execute the command using LinuxAdmin to execute python-bugzilla shell commands.
|
219
|
+
def execute_shell(params)
|
220
|
+
params = {"--bugzilla=" => bugzilla_request_uri}.merge(params)
|
221
|
+
|
222
|
+
self.last_command = shell_command_string(CMD, params, password)
|
223
|
+
LinuxAdmin.run!(CMD, :params => params).output
|
224
|
+
end
|
225
|
+
|
226
|
+
# Bypass python-bugzilla and use the xmlrpc API directly.
|
227
|
+
def execute_xmlrpc(action, params)
|
228
|
+
cmd = "Bug.#{action}"
|
229
|
+
|
230
|
+
self.last_command = xmlrpc_command_string(cmd, params)
|
231
|
+
xmlrpc.call(cmd, params)
|
232
|
+
end
|
233
|
+
|
234
|
+
# Build a printable representation of the python-bugzilla command executed.
|
235
|
+
def shell_command_string(cmd, params = {}, password=nil)
|
145
236
|
scrubbed_str = str = ""
|
146
237
|
str << cmd
|
147
238
|
params.each do |param, value|
|
@@ -158,4 +249,12 @@ class RubyBugzilla
|
|
158
249
|
scrubbed_str = str.sub(password, "********") unless password.nil?
|
159
250
|
scrubbed_str
|
160
251
|
end
|
252
|
+
|
253
|
+
# Build a printable representation of the xmlrcp command executed.
|
254
|
+
def xmlrpc_command_string(cmd, params = {})
|
255
|
+
clean_params = Hash[params]
|
256
|
+
clean_params[:Bugzilla_password] = "********"
|
257
|
+
"xmlrpc.call(#{cmd}, #{clean_params})"
|
258
|
+
end
|
259
|
+
|
161
260
|
end
|
data/spec/ruby_bugzilla_spec.rb
CHANGED
@@ -124,4 +124,147 @@ describe RubyBugzilla do
|
|
124
124
|
bz.last_command.should include("948972")
|
125
125
|
end
|
126
126
|
end
|
127
|
+
|
128
|
+
context "#xmlrpc_bug_query" do
|
129
|
+
it "when no argument is specified" do
|
130
|
+
expect { bz.xmlrpc_bug_query }.to raise_error(ArgumentError)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "when an invalid argument is specified" do
|
134
|
+
expect { bz.xmlrpc_bug_query("not a Fixnum") }.to raise_error(ArgumentError)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "when the specified bug does not exist" do
|
138
|
+
output = {}
|
139
|
+
|
140
|
+
allow(::XMLRPC::Client).to receive(:new).and_return(double('xmlrpc_client', :call => output))
|
141
|
+
expect { bz.xmlrpc_bug_query(94897099) }.to raise_error
|
142
|
+
end
|
143
|
+
|
144
|
+
it "when producing valid output" do
|
145
|
+
output = {
|
146
|
+
'bugs' => [
|
147
|
+
{
|
148
|
+
"priority" => "unspecified",
|
149
|
+
"keywords" => ["ZStream"],
|
150
|
+
"cc" => ["calvin@redhat.com", "hobbes@RedHat.com"],
|
151
|
+
},
|
152
|
+
]
|
153
|
+
}
|
154
|
+
|
155
|
+
allow(::XMLRPC::Client).to receive(:new).and_return(double('xmlrpc_client', :call => output))
|
156
|
+
existing_bz = bz.xmlrpc_bug_query("948972")
|
157
|
+
|
158
|
+
|
159
|
+
bz.last_command.should include("Bug.get")
|
160
|
+
|
161
|
+
existing_bz["priority"].should == "unspecified"
|
162
|
+
existing_bz["keywords"].should == ["ZStream"]
|
163
|
+
existing_bz["cc"].should == ["calvin@redhat.com", "hobbes@RedHat.com"]
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context "#clone" do
|
169
|
+
it "when no argument is specified" do
|
170
|
+
expect { bz.clone }.to raise_error(ArgumentError)
|
171
|
+
end
|
172
|
+
|
173
|
+
it "when an invalid argument is specified" do
|
174
|
+
expect { bz.clone("not a Fixnum") }.to raise_error(ArgumentError)
|
175
|
+
end
|
176
|
+
|
177
|
+
it "when the specified bug to clone does not exist" do
|
178
|
+
output = {}
|
179
|
+
|
180
|
+
allow(::XMLRPC::Client).to receive(:new).and_return(double('xmlrpc_client', :call => output))
|
181
|
+
expect { bz.clone(94897099) }.to raise_error
|
182
|
+
end
|
183
|
+
|
184
|
+
it "when producing valid output" do
|
185
|
+
output = {"id" => 948992}
|
186
|
+
existing_bz = {
|
187
|
+
"description" => "Description of problem:\n\nIt's Broken",
|
188
|
+
"priority" => "unspecified",
|
189
|
+
"assigned_to" => "calvin@redhat.com",
|
190
|
+
"target_release" => ["---"],
|
191
|
+
"keywords" => ["ZStream"],
|
192
|
+
"cc" => ["calvin@redhat.com", "hobbes@RedHat.com"],
|
193
|
+
"comments" => [
|
194
|
+
{
|
195
|
+
"is_private" => false,
|
196
|
+
"count" => 0,
|
197
|
+
"time" => XMLRPC::DateTime.new(1969, 7, 20, 16, 18, 30),
|
198
|
+
"bug_id" => 948970,
|
199
|
+
"author" => "Calvin@redhat.com",
|
200
|
+
"text" => "It's Broken and impossible to reproduce",
|
201
|
+
"creation_time" => XMLRPC::DateTime.new(1969, 7, 20, 16, 18, 30),
|
202
|
+
"id" => 5777871,
|
203
|
+
"creator_id" => 349490
|
204
|
+
},
|
205
|
+
{
|
206
|
+
"is_private" => false,
|
207
|
+
"count" => 1,
|
208
|
+
"time" => XMLRPC::DateTime.new(1970, 11, 10, 16, 18, 30),
|
209
|
+
"bug_id" => 948970,
|
210
|
+
"author" => "Hobbes@redhat.com",
|
211
|
+
"text" => "Fix Me Now!",
|
212
|
+
"creation_time" => XMLRPC::DateTime.new(1972, 2, 14, 0, 0, 0),
|
213
|
+
"id" => 5782170,
|
214
|
+
"creator_id" => 349490
|
215
|
+
},]
|
216
|
+
}
|
217
|
+
|
218
|
+
RubyBugzilla.any_instance.stub(:xmlrpc_bug_query).and_return(existing_bz)
|
219
|
+
allow(::XMLRPC::Client).to receive(:new).and_return(double('xmlrpc_create', :call => output))
|
220
|
+
new_bz_id = bz.clone("948972")
|
221
|
+
|
222
|
+
bz.last_command.should include("Bug.create")
|
223
|
+
|
224
|
+
new_bz_id.should == output["id"]
|
225
|
+
end
|
226
|
+
|
227
|
+
it "when providing override values" do
|
228
|
+
output = {"id" => 948992}
|
229
|
+
existing_bz = {
|
230
|
+
"description" => "Description of problem:\n\nIt's Broken",
|
231
|
+
"priority" => "unspecified",
|
232
|
+
"assigned_to" => "calvin@redhat.com",
|
233
|
+
"target_release" => ["---"],
|
234
|
+
"keywords" => ["ZStream"],
|
235
|
+
"cc" => ["calvin@redhat.com", "hobbes@RedHat.com"],
|
236
|
+
"comments" => [
|
237
|
+
{
|
238
|
+
"is_private" => false,
|
239
|
+
"count" => 0,
|
240
|
+
"time" => XMLRPC::DateTime.new(1969, 7, 20, 16, 18, 30),
|
241
|
+
"bug_id" => 948970,
|
242
|
+
"author" => "Buzz.Aldrin@redhat.com",
|
243
|
+
"text" => "It's Broken and impossible to reproduce",
|
244
|
+
"creation_time" => XMLRPC::DateTime.new(1969, 7, 20, 16, 18, 30),
|
245
|
+
"id" => 5777871,
|
246
|
+
"creator_id" => 349490
|
247
|
+
},
|
248
|
+
{
|
249
|
+
"is_private" => false,
|
250
|
+
"count" => 1,
|
251
|
+
"time" => XMLRPC::DateTime.new(1970, 11, 10, 16, 18, 30),
|
252
|
+
"bug_id" => 948970,
|
253
|
+
"author" => "Neil.Armstrong@redhat.com",
|
254
|
+
"text" => "Fix Me Now!",
|
255
|
+
"creation_time" => XMLRPC::DateTime.new(1972, 2, 14, 0, 0, 0),
|
256
|
+
"id" => 5782170,
|
257
|
+
"creator_id" => 349490
|
258
|
+
},]
|
259
|
+
}
|
260
|
+
|
261
|
+
RubyBugzilla.any_instance.stub(:xmlrpc_bug_query).and_return(existing_bz)
|
262
|
+
allow(::XMLRPC::Client).to receive(:new).and_return(double('xmlrpc_create', :call => output))
|
263
|
+
new_bz_id = bz.clone("948972", {"assigned_to" => "Ham@NASA.gov", "target_release" => ["2.2.0"],} )
|
264
|
+
|
265
|
+
bz.last_command.should include("Bug.create")
|
266
|
+
|
267
|
+
new_bz_id.should == output["id"]
|
268
|
+
end
|
269
|
+
end
|
127
270
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_bugzilla
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2014-01-08 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -125,21 +125,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
125
125
|
- - ! '>='
|
126
126
|
- !ruby/object:Gem::Version
|
127
127
|
version: '0'
|
128
|
-
segments:
|
129
|
-
- 0
|
130
|
-
hash: 3744538861231322525
|
131
128
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
129
|
none: false
|
133
130
|
requirements:
|
134
131
|
- - ! '>='
|
135
132
|
- !ruby/object:Gem::Version
|
136
133
|
version: '0'
|
137
|
-
segments:
|
138
|
-
- 0
|
139
|
-
hash: 3744538861231322525
|
140
134
|
requirements: []
|
141
135
|
rubyforge_project:
|
142
|
-
rubygems_version: 1.8.
|
136
|
+
rubygems_version: 1.8.25
|
143
137
|
signing_key:
|
144
138
|
specification_version: 3
|
145
139
|
summary: RubyBugzilla is a Ruby wrapper around the python-bugzilla CLI for easy access
|