ruby-bugzilla-nomagick 0.6.4.2
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.
- checksums.yaml +7 -0
- data/COPYING +165 -0
- data/README.md +55 -0
- data/bin/bzconsole +738 -0
- data/lib/bugzilla/api_tmpl.rb +50 -0
- data/lib/bugzilla/bug.rb +377 -0
- data/lib/bugzilla/bugzilla.rb +161 -0
- data/lib/bugzilla/classification.rb +77 -0
- data/lib/bugzilla/group.rb +48 -0
- data/lib/bugzilla/plugin.rb +80 -0
- data/lib/bugzilla/product.rb +191 -0
- data/lib/bugzilla/skeleton.rb +48 -0
- data/lib/bugzilla/user.rb +176 -0
- data/lib/bugzilla/version.rb +31 -0
- data/lib/bugzilla/xmlrpc.rb +103 -0
- data/lib/bugzilla.rb +24 -0
- data/lib/ruby-bugzilla/nvbugzilla.rb +55 -0
- data/lib/ruby-bugzilla/rhbugzilla.rb +222 -0
- metadata +105 -0
data/bin/bzconsole
ADDED
@@ -0,0 +1,738 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# bzconsole
|
4
|
+
# Copyright (C) 2010-2014 Red Hat, Inc.
|
5
|
+
#
|
6
|
+
# Authors:
|
7
|
+
# Akira TAGOH <tagoh@redhat.com>
|
8
|
+
#
|
9
|
+
# This library is free software: you can redistribute it and/or
|
10
|
+
# modify it under the terms of the GNU Lesser General Public
|
11
|
+
# License as published by the Free Software Foundation, either
|
12
|
+
# version 3 of the License, or (at your option) any later version.
|
13
|
+
#
|
14
|
+
# This library is distributed in the hope that it will be useful,
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
+
# GNU General Public License for more details.
|
18
|
+
#
|
19
|
+
# You should have received a copy of the GNU General Public License
|
20
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
21
|
+
|
22
|
+
require 'optparse'
|
23
|
+
require 'time'
|
24
|
+
require 'yaml'
|
25
|
+
require 'rubygems'
|
26
|
+
require 'pp'
|
27
|
+
require 'uri'
|
28
|
+
require 'highline/import'
|
29
|
+
|
30
|
+
begin
|
31
|
+
require 'bugzilla/xmlrpc'
|
32
|
+
require 'bugzilla/user'
|
33
|
+
require 'bugzilla/plugin'
|
34
|
+
require 'bugzilla/bug'
|
35
|
+
rescue LoadError
|
36
|
+
$:.push(File.join(File.dirname(__FILE__), '..', 'lib'))
|
37
|
+
require 'bugzilla/xmlrpc'
|
38
|
+
require 'bugzilla/user'
|
39
|
+
require 'bugzilla/plugin'
|
40
|
+
require 'bugzilla/bug'
|
41
|
+
end
|
42
|
+
|
43
|
+
module BzConsole
|
44
|
+
module Utils
|
45
|
+
def get_xmlrpc(conf = {},opts = {})
|
46
|
+
info = conf
|
47
|
+
uri = URI.parse(info[:URL])
|
48
|
+
host = uri.host
|
49
|
+
port = uri.port
|
50
|
+
path = uri.path.empty? ? nil : uri.path
|
51
|
+
proxy_host, proxy_port = get_proxy(info)
|
52
|
+
timeout = opts[:timeout].nil? ? 60 : opts[:timeout]
|
53
|
+
yield host if block_given? # if you want to run some pre hook
|
54
|
+
xmlrpc = Bugzilla::XMLRPC.new(host, port, path, proxy_host, proxy_port, timeout, uri.user, uri.password)
|
55
|
+
[xmlrpc,host]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module BzConsole
|
61
|
+
|
62
|
+
class CommandTemplate
|
63
|
+
|
64
|
+
include Utils
|
65
|
+
|
66
|
+
def initialize(plugin)
|
67
|
+
@n_args = 0
|
68
|
+
@defaultyamlfile = File.join(ENV['HOME'], ".bzconsole.yml")
|
69
|
+
@plugin = plugin
|
70
|
+
end # def initialize
|
71
|
+
|
72
|
+
attr_reader :n_args
|
73
|
+
|
74
|
+
def parse(parser, argv, opts)
|
75
|
+
raise RuntimeError, sprintf("No implementation for %s", self.class) if self.class =~ /CommandTemplate/
|
76
|
+
|
77
|
+
parser.on('-h', '--help', 'show this message') {|x| opts[:help] = true}
|
78
|
+
|
79
|
+
read_config(opts)
|
80
|
+
@plugin.run(:parser, nil, parser, argv, opts)
|
81
|
+
|
82
|
+
parser.order(argv)
|
83
|
+
end # def parse
|
84
|
+
|
85
|
+
def do(argv)
|
86
|
+
raise RuntimeError, sprintf("No implementation for %s", self.class)
|
87
|
+
end # def do
|
88
|
+
|
89
|
+
protected
|
90
|
+
|
91
|
+
def read_config(opts)
|
92
|
+
fname = opts[:config].nil? ? @defaultyamlfile : opts[:config]
|
93
|
+
begin
|
94
|
+
conf = YAML.load(File.open(fname).read)
|
95
|
+
rescue Errno::ENOENT
|
96
|
+
conf = {}
|
97
|
+
end
|
98
|
+
conf.each do |k, v|
|
99
|
+
if v.kind_of?(Hash) && v.include?(:Plugin) then
|
100
|
+
load(v[:Plugin])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
conf
|
104
|
+
end # def read_config
|
105
|
+
|
106
|
+
def save_config(opts, conf)
|
107
|
+
fname = opts[:config].nil? ? @defaultyamlfile : opts[:config]
|
108
|
+
if File.exist?(fname) then
|
109
|
+
st = File.lstat(fname)
|
110
|
+
if st.mode & 0600 != 0600 then
|
111
|
+
raise RuntimeError, sprintf("The permissions of %s has to be 0600", fname)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
File.open(fname, "w") {|f| f.chmod(0600); f.write(conf.to_yaml)}
|
115
|
+
end # def save_config
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def get_proxy(info)
|
120
|
+
uri = info[:Proxy] || ENV["http_proxy"]
|
121
|
+
proxy_uri = uri.nil? ? nil : URI.parse(uri)
|
122
|
+
proxy_host = proxy_uri.nil? ? nil : proxy_uri.host
|
123
|
+
proxy_port = proxy_uri.nil? ? nil : proxy_uri.port
|
124
|
+
return proxy_host, proxy_port
|
125
|
+
end
|
126
|
+
|
127
|
+
end # class CommandTemplate
|
128
|
+
|
129
|
+
class Setup < CommandTemplate
|
130
|
+
|
131
|
+
def initialize(plugin)
|
132
|
+
super
|
133
|
+
@n_args = 0
|
134
|
+
end # def initialize
|
135
|
+
|
136
|
+
def parse(parser, argv, opts)
|
137
|
+
parser.banner = sprintf("Usage: %s [global options] setup", File.basename(__FILE__))
|
138
|
+
parser.separator ""
|
139
|
+
parser.separator "Command options:"
|
140
|
+
|
141
|
+
super
|
142
|
+
end # def parse
|
143
|
+
|
144
|
+
def do(argv, opts)
|
145
|
+
template = {
|
146
|
+
"rhbz"=>{
|
147
|
+
:Name=>"Red Hat Bugzilla",
|
148
|
+
:URL=>"https://bugzilla.redhat.com",
|
149
|
+
:User=>"account@example.com",
|
150
|
+
:Password=>"blahblahblah",
|
151
|
+
:ProductAliases=>{
|
152
|
+
'RHEL3'=>'Red Hat Enterprise Linux 3',
|
153
|
+
'RHEL4'=>'Red Hat Enterprise Linux 4',
|
154
|
+
'RHEL5'=>'Red Hat Enterprise Linux 5',
|
155
|
+
'RHEL6'=>'Red Hat Enterprise Linux 6',
|
156
|
+
'Security'=>'Security Response'
|
157
|
+
},
|
158
|
+
:Plugin=>"ruby-bugzilla/rhbugzilla.rb"
|
159
|
+
},
|
160
|
+
"gnbz"=>{
|
161
|
+
:Name=>"GNOME Bugzilla",
|
162
|
+
:URL=>"https://bugzilla.gnome.org",
|
163
|
+
:User=>"account@example.com",
|
164
|
+
:Password=>"blahblahblah",
|
165
|
+
},
|
166
|
+
"fdobz"=>{
|
167
|
+
:Name=>"FreeDesktop Bugzilla",
|
168
|
+
:URL=>"https://bugs.freedesktop.org",
|
169
|
+
:User=>"account@example.com",
|
170
|
+
:Password=>"blahblahblah",
|
171
|
+
},
|
172
|
+
"mzbz"=>{
|
173
|
+
:Name=>"Mozilla Bugzilla",
|
174
|
+
:URL=>"https://bugzilla.mozilla.org",
|
175
|
+
:User=>"account@example.com",
|
176
|
+
:Password=>"blahblahblah",
|
177
|
+
},
|
178
|
+
"nvbz"=>{
|
179
|
+
:Name=>"Novell Bugzilla",
|
180
|
+
:URL=>"https://bugzilla.novell.com",
|
181
|
+
:User=>"account@example.com",
|
182
|
+
:Password=>"blahblahblah",
|
183
|
+
:ProductAliases=>{
|
184
|
+
'Security'=>'SUSE Security Incidents'
|
185
|
+
},
|
186
|
+
:Plugin=>"ruby-bugzilla/nvbugzilla.rb"
|
187
|
+
}
|
188
|
+
}
|
189
|
+
|
190
|
+
template.each do |k, v|
|
191
|
+
@plugin.run(:pre, v[:URL].sub(/\Ahttps?:\/\//, '').sub(/\/\Z/, ''), :setup, v)
|
192
|
+
end
|
193
|
+
|
194
|
+
fname = opts[:config].nil? ? @defaultyamlfile : opts[:config]
|
195
|
+
if File.exist?(fname) then
|
196
|
+
raise RuntimeError, ".bzconsole.yml already exists. please remove it first to proceed to the next step."
|
197
|
+
end
|
198
|
+
File.open(fname, File::CREAT|File::WRONLY, 0600) do |f|
|
199
|
+
f.write(template.to_yaml)
|
200
|
+
end
|
201
|
+
printf("%s has been created. please modify your account information before operating.\n", fname)
|
202
|
+
end # def do
|
203
|
+
|
204
|
+
end # class Setup
|
205
|
+
|
206
|
+
class Login < CommandTemplate
|
207
|
+
|
208
|
+
def initialize(plugin)
|
209
|
+
super
|
210
|
+
@n_args = 1
|
211
|
+
end # def initialize
|
212
|
+
|
213
|
+
def parse(parser, argv, opts)
|
214
|
+
opts[:output] = File.join(ENV['HOME'], '.ruby-bugzilla-cookie.yml')
|
215
|
+
parser.banner = sprintf("Usage: %s [global options] login [command options] <prefix> ...", File.basename(__FILE__))
|
216
|
+
parser.separator ""
|
217
|
+
parser.separator "Command options:"
|
218
|
+
parser.on('-o', '--output=FILE', 'FILE to store the cookie') {|v| opts[:output] = v}
|
219
|
+
|
220
|
+
super
|
221
|
+
end # def parse
|
222
|
+
|
223
|
+
def do(argv, opts)
|
224
|
+
conf = read_config(opts)
|
225
|
+
conf.freeze
|
226
|
+
cconf = read_config({:config=>opts[:command][:output]})
|
227
|
+
argv.each do |prefix|
|
228
|
+
if prefix.nil? then
|
229
|
+
raise ArgumentError, "No prefix to log in"
|
230
|
+
end
|
231
|
+
unless conf.include?(prefix) then
|
232
|
+
raise RuntimeError, sprintf("No host information for %s", prefix)
|
233
|
+
end
|
234
|
+
|
235
|
+
info = conf[prefix]
|
236
|
+
login = info[:User].nil? ? ask("Bugzilla ID: ") : info[:User]
|
237
|
+
pass = info[:Password].nil? ? ask("Bugzilla password: ") {|q| q.echo = false} : info[:Password]
|
238
|
+
|
239
|
+
xmlrpc,host = get_xmlrpc(conf[prefix],opts)
|
240
|
+
user = Bugzilla::User.new(xmlrpc)
|
241
|
+
|
242
|
+
begin
|
243
|
+
result = user.login({'login'=>login, 'password'=>pass, 'remember'=>true})
|
244
|
+
cconf[host] = xmlrpc.cookie
|
245
|
+
save_config({:config=>opts[:command][:output]}, cconf)
|
246
|
+
rescue XMLRPC::FaultException => e
|
247
|
+
printf("E: %s\n", e.message)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end # def do
|
251
|
+
|
252
|
+
end # class Login
|
253
|
+
|
254
|
+
class Getbug < CommandTemplate
|
255
|
+
|
256
|
+
def initialize(plugin)
|
257
|
+
super
|
258
|
+
|
259
|
+
@n_args = 1
|
260
|
+
end # def initialize
|
261
|
+
|
262
|
+
def parse(parser, argv, opts)
|
263
|
+
parser.banner = sprintf("Usage: %s [global options] getbug [command options] <prefix:bug number> ...", File.basename(__FILE__))
|
264
|
+
parser.separator ""
|
265
|
+
parser.separator "Command options:"
|
266
|
+
parser.on('-s', '--summary', 'Displays bugs summary only') {opts[:summary] = true}
|
267
|
+
parser.on('-d', '--details', 'Displays detailed bugs information') {opts[:details] = true}
|
268
|
+
parser.on('-a', '--all', 'Displays the whole data in bugs') {opts[:all] = true}
|
269
|
+
parser.on('-y', '--yaml', 'Displays bugs data as YAML') {opts[:yaml] = true}
|
270
|
+
parser.on('-c', '--cache=FILES', 'load results from FILES') { |v| opts[:cache] ||= []; opts[:cache].push(*v.split(','))}
|
271
|
+
parser.on('--anonymous', 'Access to Bugzilla anonymously') {opts[:anonymous] = true}
|
272
|
+
|
273
|
+
super
|
274
|
+
end # def parse
|
275
|
+
|
276
|
+
def do(argv, opts)
|
277
|
+
real_do(argv, opts) do |result|
|
278
|
+
if opts[:command][:summary] == true then
|
279
|
+
printf("Bug#%s, %s, %s[%s, %s] %s\n",
|
280
|
+
result['id'],
|
281
|
+
result['product'],
|
282
|
+
result['component'],
|
283
|
+
result['status'],
|
284
|
+
result['severity'],
|
285
|
+
result['summary'])
|
286
|
+
elsif opts[:command][:details] == true then
|
287
|
+
printf("Bug#%s, %s, %s, %s[%s, %s, %s, %s] %s\n",
|
288
|
+
result['id'],
|
289
|
+
result['product'],
|
290
|
+
result['assigned_to'],
|
291
|
+
result['component'],
|
292
|
+
result['status'],
|
293
|
+
result['resolution'],
|
294
|
+
result['priority'],
|
295
|
+
result['severity'],
|
296
|
+
result['summary'])
|
297
|
+
elsif opts[:command][:yaml] == true then
|
298
|
+
print(result.to_yaml)
|
299
|
+
else
|
300
|
+
printf("Bug#%s - %s\n", result['id'], result['summary'])
|
301
|
+
printf("Status:\t\t%s%s\n", result['status'], !result['resolution'].empty? ? sprintf("[%s]", result['resolution']) : "")
|
302
|
+
printf("Product:\t%s\n", result['product'])
|
303
|
+
printf("Version:\t%s\n", result['version'])
|
304
|
+
printf("Component:\t%s\n", result['component'])
|
305
|
+
printf("Assinged To:\t%s\n", result['assigned_to'])
|
306
|
+
printf("Priority:\t%s\n", result['priority'])
|
307
|
+
printf("Severity:\t%s\n", result['severity'])
|
308
|
+
result.keys.reject {|x| ['id','summary','status','resolution','product','version','component','assigned_to','priority','severity', 'comments'].include?(x)}.each do |x|
|
309
|
+
printf("%s:\t%s\n", x.capitalize, result[x].respond_to?(:to_time) ? result[x].to_time : result[x])
|
310
|
+
end
|
311
|
+
printf("Comments:\t%d\n\n", result['comments'].length)
|
312
|
+
i = 0
|
313
|
+
result['comments'].each do |c|
|
314
|
+
printf("Comment#%d%s %s %s\n", i, c['is_private'] == true ? "[private]" : "", c['creator'], c['creation_time'].to_time)
|
315
|
+
printf("\n %s\n\n", c['text'].split("\n").join("\n "))
|
316
|
+
i += 1
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end # def do
|
321
|
+
|
322
|
+
private
|
323
|
+
|
324
|
+
def real_do(argv, opts)
|
325
|
+
conf = read_config(opts)
|
326
|
+
conf.freeze
|
327
|
+
argv.each do |bugn|
|
328
|
+
bugn =~ /(.*):(.*)/
|
329
|
+
prefix = $1
|
330
|
+
nna = $2.split(',')
|
331
|
+
if prefix.nil? then
|
332
|
+
raise ArgumentError, sprintf("No prefix specified for Bug#%s", bugn)
|
333
|
+
end
|
334
|
+
unless conf.include?(prefix) then
|
335
|
+
raise RuntimeError, sprintf("No host information for %s", prefix)
|
336
|
+
end
|
337
|
+
|
338
|
+
cache = opts[:command][:cache]
|
339
|
+
resulth = {}
|
340
|
+
nna0 = nna.dup
|
341
|
+
if cache
|
342
|
+
cacheh = {}
|
343
|
+
cache.each do |cache_file|
|
344
|
+
File.open(cache_file) do |f|
|
345
|
+
YAML.load_stream(f) do |e|
|
346
|
+
cacheh[e["id"].to_s] = e
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
resulta,nna1x = nna.zip(cacheh.values_at(*nna)).partition { |k,v| v }
|
351
|
+
resulth = resulta.to_h
|
352
|
+
nna = nna1x.transpose[0]||[]
|
353
|
+
end
|
354
|
+
|
355
|
+
unless nna.empty?
|
356
|
+
info = conf[prefix]
|
357
|
+
if opts[:command][:anonymous] == true then
|
358
|
+
login = nil
|
359
|
+
pass = nil
|
360
|
+
else
|
361
|
+
login = info[:User].nil? ? ask("Bugzilla ID: ") : info[:User]
|
362
|
+
pass = info[:Password].nil? ? ask("Bugzilla password: ") {|q| q.echo = false} : info[:Password]
|
363
|
+
end
|
364
|
+
|
365
|
+
xmlrpc,host = get_xmlrpc(conf[prefix],opts) do |h|
|
366
|
+
@plugin.run(:pre, h, :getbug, opts)
|
367
|
+
end
|
368
|
+
|
369
|
+
user = Bugzilla::User.new(xmlrpc)
|
370
|
+
user.session(login, pass) do
|
371
|
+
bug = Bugzilla::Bug.new(xmlrpc)
|
372
|
+
|
373
|
+
result = nil
|
374
|
+
if opts[:command][:summary] == true then
|
375
|
+
result = bug.get_bugs(nna)
|
376
|
+
elsif opts[:command][:details] == true then
|
377
|
+
result = bug.get_bugs(nna, ::Bugzilla::Bug::FIELDS_DETAILS)
|
378
|
+
else
|
379
|
+
result = bug.get_bugs(nna, nil)
|
380
|
+
end
|
381
|
+
result.each do |r|
|
382
|
+
resulth[r["id"].to_s] = r
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
result = nna0.map { |nn| resulth[nn] }.compact
|
387
|
+
|
388
|
+
@plugin.run(:post, host, :getbug, result)
|
389
|
+
|
390
|
+
result.each do |r|
|
391
|
+
yield r
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end # def real_do
|
395
|
+
|
396
|
+
end # class Getbug
|
397
|
+
|
398
|
+
class Search < CommandTemplate
|
399
|
+
|
400
|
+
def initialize(plugin)
|
401
|
+
super
|
402
|
+
|
403
|
+
@n_args = 1
|
404
|
+
end # def initialize
|
405
|
+
|
406
|
+
def parse(parser, argv, opts)
|
407
|
+
opts[:query] ||= {}
|
408
|
+
parser.banner = sprintf("Usage: %s [global options] search [command options] <prefix> ...", File.basename(__FILE__))
|
409
|
+
parser.separator ""
|
410
|
+
parser.separator "Search options:"
|
411
|
+
parser.on('--alias=ALIASES', 'filter out the result by the alias') {|v| opts[:query][:alias] ||= []; opts[:query][:alias].push(*v.split(','))}
|
412
|
+
parser.on('-a', '--assignee=ASSIGNEES', 'filter out the result by the specific assignees') {|v| opts[:query][:assigned_to] ||= []; opts[:query][:assigned_to].push(*v.split(','))}
|
413
|
+
parser.on('--bug=BUGS', 'filter out the result by the specific bug number') {|v| opts[:query][:id] ||= []; opts[:query][:id].push(*v.split(','))}
|
414
|
+
parser.on('-c', '--component=COMPONENTS', 'filter out the result by the specific components') {|v| opts[:query][:component] ||= []; opts[:query][:component].push(*v.split(','))}
|
415
|
+
parser.on('--create-time=TIME', 'Searches for bugs that were created at this time or later') {|v| opts[:query][:creation_time] = Time.parse(v)}
|
416
|
+
parser.on('--creator=CREATER', 'filter out the result by the specific user who reported bugs') {|v| opts[:query][:creator] ||= []; opts[:query][:creator].push(*v.split(','))}
|
417
|
+
parser.on('--last-change-time=TIME', 'Searches for bugs that were modified at this time or later') {|v| opts[:query][:last_change_time] = Time.parse(v)}
|
418
|
+
parser.on('--op-sys=NAMES', 'filter out the result by the operating system') {|v| opts[:query][:op_sys] ||= []; opts[:query][:op_sys].push(*v.split(','))}
|
419
|
+
parser.on('--platform=PLATFORMS', 'filter out the result by the platform') {|v| opts[:query][:platform] ||= []; opts[:query][:platform].push(*v.split(','))}
|
420
|
+
parser.on('--priority=PRIORITY', 'filter out the result by the priority') {|v| opts[:query][:priority] ||= []; opts[:query][:priority].push(*v.split(','))}
|
421
|
+
parser.on('-p', '--product=PRODUCTS', 'filter out the result by the specific products') {|v| opts[:query][:product] ||= []; opts[:query][:product].push(*v.split(','))}
|
422
|
+
parser.on('--resolution=RESOLUTIONS', 'filter out the result by the resolutions') {|v| opts[:query][:resolution] ||= []; opts[:query][:resolution].push(*v.split(','))}
|
423
|
+
parser.on('--severity=SEVERITY', 'filter out the result by the severity') {|v| opts[:query][:severity] ||= []; opts[:query][:severity].push(*v.split(','))}
|
424
|
+
parser.on('-s', '--status=STATUSES', 'filter out the result by the status') {|v| opts[:query][:status] ||= []; opts[:query][:status].push(*v.split(','))}
|
425
|
+
parser.on('--summary=SUMMARY', 'filter out the result by the summary') {|v| opts[:query][:summary] ||= []; opts[:query][:summary] << v}
|
426
|
+
parser.on('--milestone=MILESTONE', 'filter out the result by the target milestone') {|v| opts[:query][:target_milestone] ||= []; opts[:query][:target_milestone].push(*v.split(','))}
|
427
|
+
parser.on('--whiteboard=STRING', 'filter out the result by the specific words in the status whiteboard') {|v| opts[:query][:whiteboard] ||= []; opts[:query][:whiteboard] << v}
|
428
|
+
parser.separator ""
|
429
|
+
parser.separator "Command options:"
|
430
|
+
parser.on('--short-list', 'Displays bugs summary only') {opts[:summary] = true}
|
431
|
+
parser.on('--detailed-list', 'Displays detailed bugs information') {opts[:details] = true}
|
432
|
+
parser.on('--anonymous', 'Access to Bugzilla anonymously') {opts[:anonymous] = true}
|
433
|
+
|
434
|
+
super
|
435
|
+
end # def parse
|
436
|
+
|
437
|
+
def do(argv, opts)
|
438
|
+
c = 0
|
439
|
+
real_do(argv, opts) do |result|
|
440
|
+
if opts[:command][:summary] == true then
|
441
|
+
printf("Bug#%s, %s, %s[%s, %s] %s\n",
|
442
|
+
result['id'] || result['bug_id'],
|
443
|
+
result['product'],
|
444
|
+
result['component'],
|
445
|
+
result['status'],
|
446
|
+
result['severity'],
|
447
|
+
result['summary'])
|
448
|
+
elsif opts[:command][:details] == true then
|
449
|
+
printf("Bug#%s, %s, %s, %s[%s, %s, %s, %s] %s\n",
|
450
|
+
result['id'],
|
451
|
+
result['product'],
|
452
|
+
result['assigned_to'],
|
453
|
+
result['component'],
|
454
|
+
result['status'],
|
455
|
+
result['resolution'],
|
456
|
+
result['priority'],
|
457
|
+
result['severity'],
|
458
|
+
result['summary'])
|
459
|
+
end
|
460
|
+
c += 1
|
461
|
+
end
|
462
|
+
printf("\n%d bug(s) found\n", c)
|
463
|
+
end # def do
|
464
|
+
|
465
|
+
private
|
466
|
+
|
467
|
+
def real_do(argv, opts)
|
468
|
+
conf = read_config(opts)
|
469
|
+
conf.freeze
|
470
|
+
argv.each do |prefix|
|
471
|
+
unless conf.include?(prefix) then
|
472
|
+
raise RuntimeError, sprintf("No host information for %s", prefix)
|
473
|
+
end
|
474
|
+
|
475
|
+
info = conf[prefix]
|
476
|
+
uri = URI.parse(info[:URL])
|
477
|
+
host = uri.host
|
478
|
+
port = uri.port
|
479
|
+
path = uri.path.empty? ? nil : uri.path
|
480
|
+
if opts[:command][:anonymous] == true then
|
481
|
+
login = nil
|
482
|
+
pass = nil
|
483
|
+
else
|
484
|
+
login = info[:User].nil? ? ask("Bugzilla ID: ") : info[:User]
|
485
|
+
pass = info[:Password].nil? ? ask("Bugzilla password: ") {|q| q.echo = false} : info[:Password]
|
486
|
+
end
|
487
|
+
proxy_host, proxy_port = get_proxy(info)
|
488
|
+
timeout = opts[:timeout].nil? ? 60 : opts[:timeout]
|
489
|
+
|
490
|
+
@plugin.run(:pre, host, :search, opts[:command][:query])
|
491
|
+
|
492
|
+
xmlrpc = Bugzilla::XMLRPC.new(host, port, path, proxy_host, proxy_port, timeout, uri.user, uri.password)
|
493
|
+
user = Bugzilla::User.new(xmlrpc)
|
494
|
+
user.session(login, pass) do
|
495
|
+
bug = Bugzilla::Bug.new(xmlrpc)
|
496
|
+
opts[:command][:query][:product].map! { |x| info.include?(:ProductAliases) &&
|
497
|
+
info[:ProductAliases].include?(x) ? info[:ProductAliases][x] : x } if opts[:command][:query].include?(:product)
|
498
|
+
|
499
|
+
result = bug.search(opts[:command][:query])
|
500
|
+
|
501
|
+
@plugin.run(:post, host, :search, result)
|
502
|
+
|
503
|
+
if result.include?('bugs') then
|
504
|
+
result['bugs'].each do |r|
|
505
|
+
yield r
|
506
|
+
end
|
507
|
+
end
|
508
|
+
end
|
509
|
+
end
|
510
|
+
end # def real_do
|
511
|
+
|
512
|
+
end # class Search
|
513
|
+
|
514
|
+
class Show < CommandTemplate
|
515
|
+
|
516
|
+
def initialize(plugin)
|
517
|
+
super
|
518
|
+
|
519
|
+
@n_args = 1
|
520
|
+
end # def initialize
|
521
|
+
|
522
|
+
def parse(parser, argv, opts)
|
523
|
+
opts[:show] ||= {}
|
524
|
+
opts[:show][:mode] = :component
|
525
|
+
opts[:show][:field] = []
|
526
|
+
parser.banner = sprintf("Usage: %s [global options] show [command options] <prefix> ...", File.basename(__FILE__))
|
527
|
+
parser.separator ""
|
528
|
+
parser.separator "Command options:"
|
529
|
+
parser.on('-f', '--field', 'Displays available field informations') {|v| opts[:show][:mode] = :field}
|
530
|
+
parser.on('--field-name=NAME', 'Displays NAME field information') {|v| opts[:show][:field] << v.split(',')}
|
531
|
+
parser.on('-p', '--product', 'Displays available product names') {|v| opts[:show][:mode] = :product}
|
532
|
+
parser.on('-c', '--component', 'Displays available component names (default)') {|v| opts[:show][:mode] = :component}
|
533
|
+
parser.on('--anonymous', 'Access to Bugzilla anonymously') {opts[:anonymous] = true}
|
534
|
+
|
535
|
+
super
|
536
|
+
end # def parse
|
537
|
+
|
538
|
+
def do(argv, opts)
|
539
|
+
real_do(argv, opts) do |*result|
|
540
|
+
if opts[:command][:show][:mode] == :product then
|
541
|
+
printf("%s\n", result[0])
|
542
|
+
elsif opts[:command][:show][:mode] == :component then
|
543
|
+
printf("%s:\n", result[0])
|
544
|
+
printf(" %s\n", result[1].join("\n "))
|
545
|
+
end
|
546
|
+
end
|
547
|
+
end # def do
|
548
|
+
|
549
|
+
private
|
550
|
+
|
551
|
+
def real_do(argv, opts)
|
552
|
+
conf = read_config(opts)
|
553
|
+
conf.freeze
|
554
|
+
argv.each do |prefix|
|
555
|
+
unless conf.include?(prefix) then
|
556
|
+
raise RuntimeError, sprintf("No host information for %s", prefix)
|
557
|
+
end
|
558
|
+
|
559
|
+
info = conf[prefix]
|
560
|
+
uri = URI.parse(info[:URL])
|
561
|
+
host = uri.host
|
562
|
+
port = uri.port
|
563
|
+
path = uri.path.empty? ? nil : uri.path
|
564
|
+
if opts[:command][:anonymous] == true then
|
565
|
+
login = nil
|
566
|
+
pass = nil
|
567
|
+
else
|
568
|
+
login = info[:User].nil? ? ask("Bugzilla ID: ") : info[:User]
|
569
|
+
pass = info[:Password].nil? ? ask("Bugzilla password: ") {|q| q.echo = false} : info[:Password]
|
570
|
+
end
|
571
|
+
proxy_host, proxy_port = get_proxy(info)
|
572
|
+
timeout = opts[:timeout].nil? ? 60 : opts[:timeout]
|
573
|
+
|
574
|
+
@plugin.run(:pre, host, :show, opts)
|
575
|
+
|
576
|
+
xmlrpc = Bugzilla::XMLRPC.new(host, port, path, proxy_host, proxy_port, timeout, uri.user, uri.password)
|
577
|
+
user = Bugzilla::User.new(xmlrpc)
|
578
|
+
user.session(login, pass) do
|
579
|
+
if opts[:command][:show][:mode] == :field then
|
580
|
+
bug = Bugzilla::Bug.new(xmlrpc)
|
581
|
+
|
582
|
+
result = bug.fields(opts[:command][:show][:field].flatten)
|
583
|
+
|
584
|
+
@plugin.run(:post, host, :show, result)
|
585
|
+
|
586
|
+
else
|
587
|
+
product = Bugzilla::Product.new(xmlrpc)
|
588
|
+
|
589
|
+
result = product.selectable_products
|
590
|
+
|
591
|
+
@plugin.run(:post, host, :show, result)
|
592
|
+
|
593
|
+
products = result.keys.sort
|
594
|
+
products.each do |p|
|
595
|
+
if opts[:command][:show][:mode] == :product then
|
596
|
+
yield p
|
597
|
+
elsif opts[:command][:show][:mode] == :component then
|
598
|
+
yield p, result[p][0].sort
|
599
|
+
end
|
600
|
+
end
|
601
|
+
end
|
602
|
+
end
|
603
|
+
end
|
604
|
+
end # def real_do
|
605
|
+
|
606
|
+
end # class Show
|
607
|
+
|
608
|
+
class Newbug < CommandTemplate
|
609
|
+
|
610
|
+
def initialize(plugin)
|
611
|
+
super
|
612
|
+
|
613
|
+
@n_args = 1
|
614
|
+
end # def initialize
|
615
|
+
|
616
|
+
def parse(parser, argv, opts)
|
617
|
+
opts[:newbug] = {}
|
618
|
+
|
619
|
+
parser.banner = sprintf("Usage: %s [global options] newbug [command options] <prefix>", File.basename(__FILE__))
|
620
|
+
parser.separator ""
|
621
|
+
parser.separator "Options:"
|
622
|
+
parser.on('-p', '--product=PRODUCT', 'The name of the product the bug is being filed against') {|v| opts[:newbug][:product] = v}
|
623
|
+
parser.on('-c', '--component=COMPONENT', 'The name of the component in PRODUCT') {|v| opts[:newbug][:component] = v}
|
624
|
+
parser.on('-s', '--summary=SUMMARY', 'A brief description of the bug being filed') {|v| opts[:newbug][:summary] = v}
|
625
|
+
parser.on('-v', '--version=VERSION', 'A version of PRODUCT that the bug was found in') {|v| opts[:newbug][:version] = v}
|
626
|
+
parser.on('-d', '--description=DESCRIPTION', 'The initial description for bug') {|v| opts[:newbug][:description] = v}
|
627
|
+
parser.on('--opsys=OPSYS', 'The operating system the bug was discovered on') {|v| opts[:newbug][:op_sys] = v}
|
628
|
+
parser.on('--platform=PLATFORM', 'What type of hardware the bug was experienced on') {|v| opts[:newbug][:platform] = v}
|
629
|
+
parser.on('--priority=PRIORITY', 'What order the bug will be fixed in by the developer') {|v| opts[:newbug][:priority] = v}
|
630
|
+
parser.on('--severity=SEVERITY', 'How severe the bug is') {|v| opts[:newbug][:severity] = v}
|
631
|
+
parser.on('--alias=ALIAS', 'A brief alias for the bug that can be used instead of a bug number') {|v| opts[:newbug][:alias] = v}
|
632
|
+
parser.on('--assigned_to=ASSGINEE', 'A user to assign the bug to') {|v| opts[:newbug][:assigned_to] = v}
|
633
|
+
parser.on('--comment_is_private', 'Make the description to private') {|v| opts[:newbug][:comment_is_private] = true}
|
634
|
+
parser.on('--groups=GROUPS', 'The list of group names to put this bug into') {|v| opts[:newbug][:groups] = v.split(/,/)}
|
635
|
+
parser.on('--qacontact=USER', 'The QA concact to assign the bug to') {|v| opts[:newbug][:qa_contact] = v}
|
636
|
+
parser.on('--status=STATUS', 'The status that the bug should start out as') {|v| opts[:newbug][:status] = v}
|
637
|
+
parser.on('--resolution=RESOLUTION', 'Set the resolution if filing a closed bug') {|v| opts[:newbug][:resolution] = v}
|
638
|
+
parser.on('--targetmilestone=MILESTONE', 'A valid target milestone for PRODUCT') {|v| opts[:newbug][:target_milestone] = v}
|
639
|
+
|
640
|
+
super
|
641
|
+
end # def parse
|
642
|
+
|
643
|
+
def do(argv, opts)
|
644
|
+
real_do(argv, opts) do |res|
|
645
|
+
if res.include?('id') then
|
646
|
+
printf("A bug has been filed as Bug#%s\n", res['id'])
|
647
|
+
else
|
648
|
+
p res
|
649
|
+
end
|
650
|
+
end
|
651
|
+
end # def do
|
652
|
+
|
653
|
+
private
|
654
|
+
|
655
|
+
def real_do(argv, opts)
|
656
|
+
conf = read_config(opts)
|
657
|
+
conf.freeze
|
658
|
+
# not supporting filing a bug to multiple bugzilla
|
659
|
+
prefix = argv[0]
|
660
|
+
unless conf.include?(prefix) then
|
661
|
+
raise RuntimeError, sprintf("No host information for %s", prefix)
|
662
|
+
end
|
663
|
+
|
664
|
+
info = conf[prefix]
|
665
|
+
uri = URI.parse(info[:URL])
|
666
|
+
host = uri.host
|
667
|
+
port = uri.port
|
668
|
+
path = uri.path.empty? ? nil : uri.path
|
669
|
+
login = info[:User].nil? ? ask("Bugzilla ID: ") : info[:User]
|
670
|
+
pass = info[:Password].nil? ? ask("Bugzilla password: ") {|q| q.echo = false} : info[:Password]
|
671
|
+
proxy_host, proxy_port = get_proxy(info)
|
672
|
+
timeout = opts[:timeout].nil? ? 60 : opts[:timeout]
|
673
|
+
|
674
|
+
@plugin.run(:pre, host, :newbug, opts)
|
675
|
+
|
676
|
+
xmlrpc = Bugzilla::XMLRPC.new(host, port, path, proxy_host, proxy_port, timeout, uri.user, uri.password)
|
677
|
+
user = Bugzilla::User.new(xmlrpc)
|
678
|
+
user.session(login, pass) do
|
679
|
+
bug = Bugzilla::Bug.new(xmlrpc)
|
680
|
+
|
681
|
+
result = bug.create(opts[:command][:newbug])
|
682
|
+
|
683
|
+
@plugin.run(:post, host, :newbug, result)
|
684
|
+
|
685
|
+
yield result
|
686
|
+
end
|
687
|
+
end # def real_do
|
688
|
+
|
689
|
+
end # class Newbug
|
690
|
+
|
691
|
+
|
692
|
+
end # module BzConsole
|
693
|
+
|
694
|
+
begin
|
695
|
+
opts = {}
|
696
|
+
opts[:command] = {}
|
697
|
+
subargv = []
|
698
|
+
|
699
|
+
o = ARGV.options do |opt|
|
700
|
+
opt.banner = sprintf("Usage: %s [global options] <command> ...", File.basename(__FILE__))
|
701
|
+
opt.separator ""
|
702
|
+
opt.separator "Global options:"
|
703
|
+
opt.on('-c', '--config=FILE', 'read FILE as the configuration file.') {|v| opts[:config] = v}
|
704
|
+
opt.on('-t', '--timeout=SEC', 'Set XMLRPC timeout in a second.') {|v| opts[:timeout] = v.to_i}
|
705
|
+
opt.on('-h', '--help', 'show this message') {|x| opts[:help] = true}
|
706
|
+
|
707
|
+
cmds = BzConsole.constants.sort.map {|x| (k = eval("BzConsole::#{x}")).class == Class && x != :CommandTemplate ? x.downcase.to_sym : nil}.compact
|
708
|
+
|
709
|
+
subargv = opt.order(ARGV);
|
710
|
+
|
711
|
+
command = subargv[0]
|
712
|
+
|
713
|
+
if subargv.length > 0 then
|
714
|
+
n = cmds.index(command.to_sym)
|
715
|
+
unless n.nil? then
|
716
|
+
opts[:instance] = eval("BzConsole::#{cmds[n].to_s.capitalize}.new(Bugzilla::Plugin::Template.new)")
|
717
|
+
subargv = opts[:instance].parse(opt, subargv[1..-1], opts[:command])
|
718
|
+
else
|
719
|
+
STDERR.printf("E: Unknown command: %s\n", subargv[0])
|
720
|
+
STDERR.printf(" Available commands: %s\n", cmds.join(' '))
|
721
|
+
exit 1
|
722
|
+
end
|
723
|
+
else
|
724
|
+
opt.separator ""
|
725
|
+
opt.separator "Available commands:"
|
726
|
+
opt.separator sprintf(" %s", cmds.join(' '))
|
727
|
+
end
|
728
|
+
|
729
|
+
if opts[:instance].nil? && subargv.length == 0 ||
|
730
|
+
opts[:help] == true ||
|
731
|
+
subargv.length < opts[:instance].n_args then
|
732
|
+
puts opt.help
|
733
|
+
exit
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
737
|
+
opts[:instance].do(subargv, opts)
|
738
|
+
end
|