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.
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