summaryrefslogtreecommitdiff
blob: 9d29bdda7d55e123eb6c3f2935f56593312ed8cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
#!/usr/bin/python

import argparse
import ConfigParser
import locale
import os
import sys
import traceback

from bugz import __version__
from bugz.cli import BugzError, PrettyBugz
from bugz.config import config

def make_attach_parser(subparsers):
	attach_parser = subparsers.add_parser('attach',
		help = 'attach file to a bug')
	attach_parser.add_argument('bugid',
		help = 'the ID of the bug where the file should be attached')
	attach_parser.add_argument('filename',
		help = 'the name of the file to attach')
	attach_parser.add_argument('-c', '--content-type',
		default='text/plain',
		help = 'mimetype of the file (default: text/plain)')
	attach_parser.add_argument('-d', '--description',
		help = 'a description of the attachment.')
	attach_parser.add_argument('-p', '--patch',
		action='store_true',
	help = 'attachment is a patch')
	attach_parser.set_defaults(func = PrettyBugz.attach)

def make_attachment_parser(subparsers):
	attachment_parser = subparsers.add_parser('attachment',
		help = 'get an attachment from bugzilla')
	attachment_parser.add_argument('attachid',
		help = 'the ID of the attachment')
	attachment_parser.add_argument('-v', '--view',
		action="store_true",
		default = False,
		help = 'print attachment rather than save')
	attachment_parser.set_defaults(func = PrettyBugz.attachment)

def make_get_parser(subparsers):
	get_parser = subparsers.add_parser('get',
		help = 'get a bug from bugzilla')
	get_parser.add_argument('bugid',
		help = 'the ID of the bug to retrieve.')
	get_parser.add_argument("-a", "--no-attachments",
		action="store_false",
		default = True,
		help = 'do not show attachments',
		dest = 'attachments')
	get_parser.add_argument("-n", "--no-comments",
		action="store_false",
		default = True,
		help = 'do not show comments',
		dest = 'comments')
	get_parser.set_defaults(func = PrettyBugz.get)

def make_modify_parser(subparsers):
	modify_parser = subparsers.add_parser('modify',
		help = 'modify a bug (eg. post a comment)')
	modify_parser.add_argument('bugid',
		help = 'the ID of the bug to modify')
	modify_parser.add_argument('-a', '--assigned-to',
		help = 'change assignee for this bug')
	modify_parser.add_argument('-C', '--comment-editor',
		action='store_true',
		help = 'add comment via default editor')
	modify_parser.add_argument('-F', '--comment-from',
		help = 'add comment from file.  If -C is also specified, the editor will be opened with this file as its contents.')
	modify_parser.add_argument('-c', '--comment',
		help = 'add comment from command line')
	modify_parser.add_argument('-d', '--duplicate',
		type = int,
		default = 0,
		help = 'this bug is a duplicate')
	modify_parser.add_argument('-k', '--keywords',
		help = 'set bug keywords'),
	modify_parser.add_argument('--priority',
		choices=config.choices['priority'].values(),
		help = 'change the priority for this bug')
	modify_parser.add_argument('-r', '--resolution',
		choices=config.choices['resolution'].values(),
		help = 'set new resolution (only if status = RESOLVED)')
	modify_parser.add_argument('-s', '--status',
		choices=config.choices['status'].values(),
		help = 'set new status of bug (eg. RESOLVED)')
	modify_parser.add_argument('-S', '--severity',
		choices=config.choices['severity'],
		help = 'set severity for this bug')
	modify_parser.add_argument('-t', '--title',
		help = 'set title of bug')
	modify_parser.add_argument('-U', '--url',
		help = 'set URL field of bug')
	modify_parser.add_argument('-w', '--whiteboard',
		help = 'set Status whiteboard'),
	modify_parser.add_argument('--add-cc',
		action = 'append',
		help = 'add an email to the CC list')
	modify_parser.add_argument('--remove-cc',
		action = 'append',
		help = 'remove an email from the CC list')
	modify_parser.add_argument('--add-dependson',
		action = 'append',
		help = 'add a bug to the depends list')
	modify_parser.add_argument('--remove-dependson',
		action = 'append',
		help = 'remove a bug from the depends list')
	modify_parser.add_argument('--add-blocked',
		action = 'append',
		help = 'add a bug to the blocked list')
	modify_parser.add_argument('--remove-blocked',
		action = 'append',
		help = 'remove a bug from the blocked list')
	modify_parser.add_argument('--component',
		help = 'change the component for this bug')
	modify_parser.add_argument('--fixed',
		action='store_true',
		help = 'mark bug as RESOLVED, FIXED')
	modify_parser.add_argument('--invalid',
		action='store_true',
		help = 'mark bug as RESOLVED, INVALID')
	modify_parser.set_defaults(func = PrettyBugz.modify)

def make_namedcmd_parser(subparsers):
	namedcmd_parser = subparsers.add_parser('namedcmd',
		help = 'run a stored search')
	namedcmd_parser.add_argument('command',
		help = 'the name of the stored search')
	namedcmd_parser.add_argument('--show-status',
		action = 'store_true',
		help = 'show status of bugs')
	namedcmd_parser.add_argument('--show-url',
		action = 'store_true',
		help = 'show bug id as a url')
	namedcmd_parser.set_defaults(func = PrettyBugz.namedcmd)

def make_post_parser(subparsers):
	post_parser = subparsers.add_parser('post',
		help = 'post a new bug into bugzilla')
	post_parser.add_argument('--product',
		help = 'product')
	post_parser.add_argument('--component',
		help = 'component')
	post_parser.add_argument('--prodversion',
		help = 'version of the product')
	post_parser.add_argument('-t', '--title',
		help = 'title of bug')
	post_parser.add_argument('-d', '--description',
		help = 'description of the bug')
	post_parser.add_argument('-F' , '--description-from',
		help = 'description from contents of file')
	post_parser.add_argument('--append-command',
		help = 'append the output of a command to the description')
	post_parser.add_argument('-a', '--assigned-to',
		help = 'assign bug to someone other than the default assignee')
	post_parser.add_argument('--cc',
		help = 'add a list of emails to CC list')
	post_parser.add_argument('-U', '--url',
		help = 'URL associated with the bug')
	post_parser.add_argument('--depends-on',
		help = 'add a list of bug dependencies',
		dest='dependson')
	post_parser.add_argument('--blocked',
		help = 'add a list of blocker bugs')
	post_parser.add_argument('-k', '--keywords',
		help = 'list of bugzilla keywords')
	post_parser.add_argument('--batch',
		action="store_true",
		help = 'do not prompt for any values')
	post_parser.add_argument('--default-confirm',
		choices = ['y','Y','n','N'],
		default = 'y',
		help = 'default answer to confirmation question')
	post_parser.add_argument('--priority',
		choices=config.choices['priority'].values(),
		help = 'set priority for the new bug')
	post_parser.add_argument('-S', '--severity',
		choices=config.choices['severity'],
		help = 'set the severity for the new bug')
	post_parser.set_defaults(func = PrettyBugz.post)

def make_search_parser(subparsers):
	search_parser = subparsers.add_parser('search',
		help = 'search for bugs in bugzilla')
	search_parser.add_argument('terms',
		nargs='*',
		help = 'strings to search for in title or body')
	search_parser.add_argument('-o', '--order',
		choices = config.choices['order'].keys(),
		default = 'number',
		help = 'display bugs in this order')
	search_parser.add_argument('-a', '--assigned-to',
		help = 'email the bug is assigned to')
	search_parser.add_argument('-r', '--reporter',
		help = 'email the bug was reported by')
	search_parser.add_argument('--cc',
		help = 'restrict by CC email address')
	search_parser.add_argument('--commenter',
		help = 'email that commented the bug')
	search_parser.add_argument('-s', '--status',
		action='append',
		help = 'restrict by status (one or more, use all for all statuses)')
	search_parser.add_argument('--severity',
		action='append',
		choices = config.choices['severity'],
		help = 'restrict by severity (one or more)')
	search_parser.add_argument('--priority',
		action='append',
		choices = config.choices['priority'].values(),
		help = 'restrict by priority (one or more)')
	search_parser.add_argument('-c', '--comments',
		action='store_true',
		default=None,
		help = 'search comments instead of title')
	search_parser.add_argument('--product',
		action='append',
		help = 'restrict by product (one or more)')
	search_parser.add_argument('-C', '--component',
		action='append',
		help = 'restrict by component (1 or more)')
	search_parser.add_argument('-k', '--keywords',
		help = 'restrict by keywords')
	search_parser.add_argument('-w', '--whiteboard',
		help = 'status whiteboard')
	search_parser.add_argument('--show-status',
		action = 'store_true',
		help='show status of bugs')
	search_parser.add_argument('--show-url',
		action = 'store_true',
		help='show bug id as a url.')
	search_parser.set_defaults(func = PrettyBugz.search)

def make_parser():
	parser = argparse.ArgumentParser(
		epilog = 'use -h after a sub-command for sub-command specific help')
	parser.add_argument('--config-file',
		help = 'read an alternate configuration file')
	parser.add_argument('--connection',
		help = 'use [connection] section of your configuration file')
	parser.add_argument('-b', '--base',
		help = 'base URL of Bugzilla')
	parser.add_argument('-u', '--user',
		help = 'username for commands requiring authentication')
	parser.add_argument('-p', '--password',
		help = 'password for commands requiring authentication')
	parser.add_argument('-H', '--httpuser',
		help = 'username for basic http auth')
	parser.add_argument('-P', '--httppassword',
		help = 'password for basic http auth')
	parser.add_argument('-f', '--forget',
		action='store_true',
		help = 'forget login after execution')
	parser.add_argument('-q', '--quiet',
		action='store_true',
		help = 'quiet mode')
	parser.add_argument('--columns', 
		type = int,
		help = 'maximum number of columns output should use')
	parser.add_argument('--encoding',
		help = 'output encoding (default: utf-8).')
	parser.add_argument('--skip-auth',
		action='store_true',
		help = 'skip Authentication.')
	parser.add_argument('--version',
		action='version',
		help='show program version and exit',
		version='%(prog)s ' + __version__)
	subparsers = parser.add_subparsers(help = 'help for sub-commands')
	make_attach_parser(subparsers)
	make_attachment_parser(subparsers)
	make_get_parser(subparsers)
	make_modify_parser(subparsers)
	make_namedcmd_parser(subparsers)
	make_post_parser(subparsers)
	make_search_parser(subparsers)
	return parser

def config_option(parser, get, section, option):
	if parser.has_option(section, option):
		try:
			if get(section, option) != '':
				return get(section, option)
			else:
				print " ! Error: "+option+" is not set"
				sys.exit(1)
		except ValueError as e:
			print " ! Error: option "+option+" is not in the right format: "+str(e)
			sys.exit(1)

def get_config(args, bugz):
	config_file = getattr(args, 'config_file')
	if config_file is None:
			config_file = '~/.bugzrc'
	section = getattr(args, 'connection')
	parser = ConfigParser.ConfigParser()
	config_file_name = os.path.expanduser(config_file)

	# try to open config file
	try:
		file = open(config_file_name)
	except IOError:
		if getattr(args, 'config_file') is not None:
			print " ! Error: Can't find user configuration file: "+config_file_name
			sys.exit(1)
		else:
			return bugz

	# try to parse config file
	try:
		parser.readfp(file)
		sections = parser.sections()
	except ConfigParser.ParsingError as e:
		print " ! Error: Can't parse user configuration file: "+str(e)
		sys.exit(1)

	# parse a specific section
	if section in sections:
		bugz['base'] = config_option(parser, parser.get, section, "base")
		bugz['user'] = config_option(parser, parser.get, section, "user")
		bugz['password'] = config_option(parser, parser.get, section, "password")
		bugz['httpuser'] = config_option(parser, parser.get, section, "httpuser")
		bugz['httppassword'] = config_option(parser, parser.get, section,
				"httppassword")
		bugz['forget'] = config_option(parser, parser.getboolean, section,
				"forget")
		bugz['columns'] = config_option(parser, parser.getint, section,
				"columns")
		bugz['encoding'] = config_option(parser, parser.get, section,
				"encoding")
		bugz['quiet'] = config_option(parser, parser.getboolean, section,
				"quiet")
	elif section is not None:
		print " ! Error: Can't find section ["+section+"] in configuration file"
		sys.exit(1)

	return bugz

def get_kwds(args, bugz, cmd):
	global_attrs = ['user', 'password', 'httpuser', 'httppassword', 'forget',
		'base', 'columns', 'encoding', 'quiet', 'skip_auth']
	skip_attrs = ['config_file', 'connection', 'func']
	for attr in dir(args):
		if attr[0] == '_' or attr in skip_attrs:
			continue
		elif attr in global_attrs:
			if attr not in bugz or getattr(args,attr):
				bugz[attr] = getattr(args,attr)
		else:
			cmd[attr] = getattr(args,attr)

def main():
	parser = make_parser()

	# parse options
	args = parser.parse_args()
	bugz_kwds = {}
	get_config(args, bugz_kwds)
	cmd_kwds = {}
	get_kwds(args, bugz_kwds, cmd_kwds)
	if bugz_kwds['base'] is None:
		bugz_kwds['base'] = 'https://bugs.gentoo.org'
	if bugz_kwds['columns'] is None:
		bugz_kwds['columns'] = 0

	try:
		bugz = PrettyBugz(**bugz_kwds)
		args.func(bugz, **cmd_kwds)

	except BugzError, e:
		print ' ! Error: %s' % e
		sys.exit(-1)

	except TypeError, e:
		print ' ! Error: Incorrect number of arguments supplied'
		print
		traceback.print_exc()
		sys.exit(-1)

	except RuntimeError, e:
		print ' ! Error: %s' % e
		sys.exit(-1)

	except KeyboardInterrupt:
		print
		print 'Stopped.'
		sys.exit(-1)

	except:
		raise

if __name__ == "__main__":
	main()