|
Michal Koutný |
ccf7f1 |
#!/usr/bin/env python3
|
|
Jeff Mahoney |
3dff52 |
#
|
|
Jeff Mahoney |
3dff52 |
# bugzilla - a commandline frontend for the python bugzilla module
|
|
Jeff Mahoney |
3dff52 |
#
|
|
Jeff Mahoney |
3dff52 |
# Copyright (C) 2007-2017 Red Hat Inc.
|
|
Jeff Mahoney |
3dff52 |
# Author: Will Woods <wwoods@redhat.com>
|
|
Jeff Mahoney |
3dff52 |
# Author: Cole Robinson <crobinso@redhat.com>
|
|
Jeff Mahoney |
3dff52 |
#
|
|
Michal Koutný |
ccf7f1 |
# This work is licensed under the GNU GPLv2 or later.
|
|
Michal Koutný |
ccf7f1 |
# See the COPYING file in the top-level directory.
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
import argparse
|
|
Michal Koutný |
ccf7f1 |
import base64
|
|
Michal Koutný |
ccf7f1 |
import datetime
|
|
Michal Koutný |
ccf7f1 |
import errno
|
|
Michal Koutný |
ccf7f1 |
import json
|
|
Jeff Mahoney |
3dff52 |
import locale
|
|
Jeff Mahoney |
3dff52 |
from logging import getLogger, DEBUG, INFO, WARN, StreamHandler, Formatter
|
|
Jeff Mahoney |
3dff52 |
import os
|
|
Jeff Mahoney |
3dff52 |
import re
|
|
Jeff Mahoney |
3dff52 |
import socket
|
|
Jeff Mahoney |
3dff52 |
import sys
|
|
Jeff Mahoney |
3dff52 |
import tempfile
|
|
Michal Koutný |
ccf7f1 |
import urllib.parse
|
|
Michal Koutný |
ccf7f1 |
import xmlrpc.client
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
import requests.exceptions
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
import bugzilla
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
4e69d7 |
DEFAULT_BZ = 'https://apibugzilla.suse.com'
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
format_field_re = re.compile("%{([a-z0-9_]+)(?::([^}]*))?}")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
log = getLogger(bugzilla.__name__)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
################
|
|
Jeff Mahoney |
3dff52 |
# Util helpers #
|
|
Jeff Mahoney |
3dff52 |
################
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _is_unittest_debug():
|
|
Jeff Mahoney |
3dff52 |
return bool(os.getenv("__BUGZILLA_UNITTEST_DEBUG"))
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def open_without_clobber(name, *args):
|
|
Michal Koutný |
ccf7f1 |
"""
|
|
Michal Koutný |
ccf7f1 |
Try to open the given file with the given mode; if that filename exists,
|
|
Michal Koutný |
ccf7f1 |
try "name.1", "name.2", etc. until we find an unused filename.
|
|
Michal Koutný |
ccf7f1 |
"""
|
|
Jeff Mahoney |
3dff52 |
fd = None
|
|
Jeff Mahoney |
3dff52 |
count = 1
|
|
Jeff Mahoney |
3dff52 |
orig_name = name
|
|
Jeff Mahoney |
3dff52 |
while fd is None:
|
|
Jeff Mahoney |
3dff52 |
try:
|
|
Jeff Mahoney |
3dff52 |
fd = os.open(name, os.O_CREAT | os.O_EXCL, 0o666)
|
|
Jeff Mahoney |
3dff52 |
except OSError as err:
|
|
Michal Koutný |
ccf7f1 |
if err.errno == errno.EEXIST:
|
|
Jeff Mahoney |
3dff52 |
name = "%s.%i" % (orig_name, count)
|
|
Jeff Mahoney |
3dff52 |
count += 1
|
|
Michal Koutný |
ccf7f1 |
else: # pragma: no cover
|
|
Michal Koutný |
ccf7f1 |
raise IOError(err.errno, err.strerror, err.filename) from None
|
|
Jeff Mahoney |
3dff52 |
fobj = open(name, *args)
|
|
Jeff Mahoney |
3dff52 |
if fd != fobj.fileno():
|
|
Jeff Mahoney |
3dff52 |
os.close(fd)
|
|
Jeff Mahoney |
3dff52 |
return fobj
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def setup_logging(debug, verbose):
|
|
Jeff Mahoney |
3dff52 |
handler = StreamHandler(sys.stderr)
|
|
Jeff Mahoney |
3dff52 |
handler.setFormatter(Formatter(
|
|
Jeff Mahoney |
3dff52 |
"[%(asctime)s] %(levelname)s (%(module)s:%(lineno)d) %(message)s",
|
|
Jeff Mahoney |
3dff52 |
"%H:%M:%S"))
|
|
Jeff Mahoney |
3dff52 |
log.addHandler(handler)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if debug:
|
|
Jeff Mahoney |
3dff52 |
log.setLevel(DEBUG)
|
|
Jeff Mahoney |
3dff52 |
elif verbose:
|
|
Jeff Mahoney |
3dff52 |
log.setLevel(INFO)
|
|
Jeff Mahoney |
3dff52 |
else:
|
|
Jeff Mahoney |
3dff52 |
log.setLevel(WARN)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if _is_unittest_debug():
|
|
Michal Koutný |
ccf7f1 |
log.setLevel(DEBUG) # pragma: no cover
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
##################
|
|
Jeff Mahoney |
3dff52 |
# Option parsing #
|
|
Jeff Mahoney |
3dff52 |
##################
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _setup_root_parser():
|
|
Jeff Mahoney |
3dff52 |
epilog = 'Try "bugzilla COMMAND --help" for command-specific help.'
|
|
Jeff Mahoney |
3dff52 |
p = argparse.ArgumentParser(epilog=epilog)
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
default_url = bugzilla.Bugzilla.get_rcfile_default_url()
|
|
Michal Koutný |
ccf7f1 |
if not default_url:
|
|
Michal Koutný |
ccf7f1 |
default_url = DEFAULT_BZ
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
# General bugzilla connection options
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--bugzilla', default=default_url,
|
|
Michal Koutný |
ccf7f1 |
help="bugzilla URI. default: %s" % default_url)
|
|
Jeff Mahoney |
3dff52 |
p.add_argument("--nosslverify", dest="sslverify",
|
|
Jeff Mahoney |
3dff52 |
action="store_false", default=True,
|
|
Jeff Mahoney |
3dff52 |
help="Don't error on invalid bugzilla SSL certificate")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--cert',
|
|
Jeff Mahoney |
3dff52 |
help="client side certificate file needed by the webserver")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--login', action="store_true",
|
|
Jeff Mahoney |
3dff52 |
help='Run interactive "login" before performing the '
|
|
Jeff Mahoney |
3dff52 |
'specified command.')
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--username', help="Log in with this username")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--password', help="Log in with this password")
|
|
Michal Koutný |
ccf7f1 |
p.add_argument('--restrict-login', action="store_true",
|
|
Michal Koutný |
ccf7f1 |
help="The session (login token) will be restricted to "
|
|
Michal Koutný |
ccf7f1 |
"the current IP address.")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--ensure-logged-in', action="store_true",
|
|
Jeff Mahoney |
3dff52 |
help="Raise an error if we aren't logged in to bugzilla. "
|
|
Jeff Mahoney |
3dff52 |
"Consider using this if you are depending on "
|
|
Jeff Mahoney |
3dff52 |
"cached credentials, to ensure that when they expire the "
|
|
Jeff Mahoney |
3dff52 |
"tool errors, rather than subtly change output.")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--no-cache-credentials',
|
|
Jeff Mahoney |
3dff52 |
action='store_false', default=True, dest='cache_credentials',
|
|
Jeff Mahoney |
3dff52 |
help="Don't save any bugzilla cookies or tokens to disk, and "
|
|
Jeff Mahoney |
3dff52 |
"don't use any pre-existing credentials.")
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
p.add_argument('--cookiefile', default=None, help=argparse.SUPPRESS)
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--tokenfile', default=None,
|
|
Jeff Mahoney |
3dff52 |
help="token file to use for bugzilla authentication")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--verbose', action='store_true',
|
|
Jeff Mahoney |
3dff52 |
help="give more info about what's going on")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--debug', action='store_true',
|
|
Jeff Mahoney |
3dff52 |
help="output bunches of debugging info")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--version', action='version',
|
|
Jeff Mahoney |
3dff52 |
version=bugzilla.__version__)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
# Allow user to specify BZClass to initialize. Kinda weird for the
|
|
Jeff Mahoney |
3dff52 |
# CLI, I'd rather people file bugs about this so we can fix our detection.
|
|
Jeff Mahoney |
3dff52 |
# So hide it from the help output but keep it for back compat
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--bztype', default='auto', help=argparse.SUPPRESS)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
return p
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _parser_add_output_options(p):
|
|
Jeff Mahoney |
3dff52 |
outg = p.add_argument_group("Output format options")
|
|
Jeff Mahoney |
3dff52 |
outg.add_argument('--full', action='store_const', dest='output',
|
|
Jeff Mahoney |
3dff52 |
const='full', default='normal',
|
|
Jeff Mahoney |
3dff52 |
help="output detailed bug info")
|
|
Jeff Mahoney |
3dff52 |
outg.add_argument('-i', '--ids', action='store_const', dest='output',
|
|
Jeff Mahoney |
3dff52 |
const='ids', help="output only bug IDs")
|
|
Jeff Mahoney |
3dff52 |
outg.add_argument('-e', '--extra', action='store_const',
|
|
Jeff Mahoney |
3dff52 |
dest='output', const='extra',
|
|
Jeff Mahoney |
3dff52 |
help="output additional bug information "
|
|
Jeff Mahoney |
3dff52 |
"(keywords, Whiteboards, etc.)")
|
|
Jeff Mahoney |
3dff52 |
outg.add_argument('--oneline', action='store_const', dest='output',
|
|
Jeff Mahoney |
3dff52 |
const='oneline',
|
|
Jeff Mahoney |
3dff52 |
help="one line summary of the bug (useful for scripts)")
|
|
Michal Koutný |
ccf7f1 |
outg.add_argument('--json', action='store_const', dest='output',
|
|
Michal Koutný |
ccf7f1 |
const='json', help="output contents in json format")
|
|
Michal Koutný |
ccf7f1 |
outg.add_argument("--includefield", action="append",
|
|
Michal Koutný |
ccf7f1 |
help="Pass the field name to bugzilla include_fields list. "
|
|
Michal Koutný |
ccf7f1 |
"Only the fields passed to include_fields are returned "
|
|
Michal Koutný |
ccf7f1 |
"by the bugzilla server. "
|
|
Michal Koutný |
ccf7f1 |
"This can be specified multiple times.")
|
|
Michal Koutný |
ccf7f1 |
outg.add_argument("--extrafield", action="append",
|
|
Michal Koutný |
ccf7f1 |
help="Pass the field name to bugzilla extra_fields list. "
|
|
Michal Koutný |
ccf7f1 |
"When used with --json this can be used to request "
|
|
Michal Koutný |
ccf7f1 |
"bugzilla to return values for non-default fields. "
|
|
Michal Koutný |
ccf7f1 |
"This can be specified multiple times.")
|
|
Michal Koutný |
ccf7f1 |
outg.add_argument("--excludefield", action="append",
|
|
Michal Koutný |
ccf7f1 |
help="Pass the field name to bugzilla exclude_fields list. "
|
|
Michal Koutný |
ccf7f1 |
"When used with --json this can be used to request "
|
|
Michal Koutný |
ccf7f1 |
"bugzilla to not return values for a field. "
|
|
Michal Koutný |
ccf7f1 |
"This can be specified multiple times.")
|
|
Jeff Mahoney |
3dff52 |
outg.add_argument('--raw', action='store_const', dest='output',
|
|
Michal Koutný |
ccf7f1 |
const='raw', help="raw output of the bugzilla contents. This "
|
|
Michal Koutný |
ccf7f1 |
"format is unstable and difficult to parse. Use --json instead.")
|
|
Jeff Mahoney |
3dff52 |
outg.add_argument('--outputformat',
|
|
Jeff Mahoney |
3dff52 |
help="Print output in the form given. "
|
|
Jeff Mahoney |
3dff52 |
"You can use RPM-style tags that match bug "
|
|
Jeff Mahoney |
3dff52 |
"fields, e.g.: '%%{id}: %%{summary}'. See the man page "
|
|
Jeff Mahoney |
3dff52 |
"section 'Output options' for more details.")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _parser_add_bz_fields(rootp, command):
|
|
Jeff Mahoney |
3dff52 |
cmd_new = (command == "new")
|
|
Jeff Mahoney |
3dff52 |
cmd_query = (command == "query")
|
|
Jeff Mahoney |
3dff52 |
cmd_modify = (command == "modify")
|
|
Jeff Mahoney |
3dff52 |
if cmd_new:
|
|
Jeff Mahoney |
3dff52 |
comment_help = "Set initial bug comment/description"
|
|
Jeff Mahoney |
3dff52 |
elif cmd_query:
|
|
Jeff Mahoney |
3dff52 |
comment_help = "Search all bug comments"
|
|
Jeff Mahoney |
3dff52 |
else:
|
|
Jeff Mahoney |
3dff52 |
comment_help = "Add new bug comment"
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
p = rootp.add_argument_group("Standard bugzilla options")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-p', '--product', help="Product name")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-v', '--version', help="Product version")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-c', '--component', help="Component name")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-t', '--summary', '--short_desc', help="Bug summary")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-l', '--comment', '--long_desc', help=comment_help)
|
|
Jeff Mahoney |
3dff52 |
if not cmd_query:
|
|
Jeff Mahoney |
3dff52 |
p.add_argument("--comment-tag", action="append",
|
|
Jeff Mahoney |
3dff52 |
help="Comment tag for the new comment")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument("--sub-component", action="append",
|
|
Jeff Mahoney |
3dff52 |
help="RHBZ sub component field")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-o', '--os', help="Operating system")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--arch', help="Arch this bug occurs on")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-x', '--severity', help="Bug severity")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-z', '--priority', help="Bug priority")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--alias', help='Bug alias (name)')
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-s', '--status', '--bug_status',
|
|
Jeff Mahoney |
3dff52 |
help='Bug status (NEW, ASSIGNED, etc.)')
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-u', '--url', help="URL field")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-m', '--target_milestone', help="Target milestone")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--target_release', help="RHBZ Target release")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--blocked', action="append",
|
|
Jeff Mahoney |
3dff52 |
help="Bug IDs that this bug blocks")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--dependson', action="append",
|
|
Jeff Mahoney |
3dff52 |
help="Bug IDs that this bug depends on")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--keywords', action="append",
|
|
Jeff Mahoney |
3dff52 |
help="Bug keywords")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--groups', action="append",
|
|
Jeff Mahoney |
3dff52 |
help="Which user groups can view this bug")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--cc', action="append", help="CC list")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-a', '--assigned_to', '--assignee', help="Bug assignee")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-q', '--qa_contact', help='QA contact')
|
|
Michal Koutný |
ccf7f1 |
if cmd_modify:
|
|
Michal Koutný |
ccf7f1 |
p.add_argument("--minor-update", action="store_true",
|
|
Michal Koutný |
ccf7f1 |
help="Request bugzilla to not send any "
|
|
Michal Koutný |
ccf7f1 |
"email about this change")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if not cmd_new:
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-f', '--flag', action='append',
|
|
Jeff Mahoney |
3dff52 |
help="Bug flags state. Ex:\n"
|
|
Jeff Mahoney |
3dff52 |
" --flag needinfo?\n"
|
|
Jeff Mahoney |
3dff52 |
" --flag dev_ack+ \n"
|
|
Jeff Mahoney |
3dff52 |
" clear with --flag needinfoX")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument("--tags", action="append",
|
|
Jeff Mahoney |
3dff52 |
help="Tags/Personal Tags field.")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-w', "--whiteboard", '--status_whiteboard',
|
|
Jeff Mahoney |
3dff52 |
action="append", help='Whiteboard field')
|
|
Jeff Mahoney |
3dff52 |
p.add_argument("--devel_whiteboard", action="append",
|
|
Jeff Mahoney |
3dff52 |
help='RHBZ devel whiteboard field')
|
|
Jeff Mahoney |
3dff52 |
p.add_argument("--internal_whiteboard", action="append",
|
|
Jeff Mahoney |
3dff52 |
help='RHBZ internal whiteboard field')
|
|
Jeff Mahoney |
3dff52 |
p.add_argument("--qa_whiteboard", action="append",
|
|
Jeff Mahoney |
3dff52 |
help='RHBZ QA whiteboard field')
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-F', '--fixed_in',
|
|
Jeff Mahoney |
3dff52 |
help="RHBZ 'Fixed in version' field")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
# Put this at the end, so it sticks out more
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--field',
|
|
Jeff Mahoney |
3dff52 |
metavar="FIELD=VALUE", action="append", dest="fields",
|
|
Michal Koutný |
ccf7f1 |
help="Manually specify a bugzilla API field. FIELD is "
|
|
Michal Koutný |
ccf7f1 |
"the raw name used by the bugzilla instance. For example, if your "
|
|
Jeff Mahoney |
3dff52 |
"bugzilla instance has a custom field cf_my_field, do:\n"
|
|
Jeff Mahoney |
3dff52 |
" --field cf_my_field=VALUE")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if not cmd_modify:
|
|
Jeff Mahoney |
3dff52 |
_parser_add_output_options(rootp)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _setup_action_new_parser(subparsers):
|
|
Jeff Mahoney |
3dff52 |
description = ("Create a new bug report. "
|
|
Jeff Mahoney |
3dff52 |
"--product, --component, --version, --summary, and --comment "
|
|
Jeff Mahoney |
3dff52 |
"must be specified. "
|
|
Jeff Mahoney |
3dff52 |
"Options that take multiple values accept comma separated lists, "
|
|
Jeff Mahoney |
3dff52 |
"including --cc, --blocks, --dependson, --groups, and --keywords.")
|
|
Jeff Mahoney |
3dff52 |
p = subparsers.add_parser("new", description=description)
|
|
Michal Koutný |
029c1e |
p.add_argument('--no-refresh', action='store_true',
|
|
Michal Koutný |
029c1e |
help='Do not refresh bug after creating')
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
_parser_add_bz_fields(p, "new")
|
|
Michal Koutný |
ccf7f1 |
g = p.add_argument_group("'new' specific options")
|
|
Michal Koutný |
ccf7f1 |
g.add_argument('--private', action='store_true', default=False,
|
|
Michal Koutný |
ccf7f1 |
help='Mark new comment as private')
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _setup_action_query_parser(subparsers):
|
|
Jeff Mahoney |
3dff52 |
description = ("List bug reports that match the given criteria. "
|
|
Jeff Mahoney |
3dff52 |
"Certain options can accept a comma separated list to query multiple "
|
|
Jeff Mahoney |
3dff52 |
"values, including --status, --component, --product, --version, --id.")
|
|
Jeff Mahoney |
3dff52 |
epilog = ("Note: querying via explicit command line options will only "
|
|
Jeff Mahoney |
3dff52 |
"get you so far. See the --from-url option for a way to use powerful "
|
|
Jeff Mahoney |
3dff52 |
"Web UI queries from the command line.")
|
|
Jeff Mahoney |
3dff52 |
p = subparsers.add_parser("query",
|
|
Jeff Mahoney |
3dff52 |
description=description, epilog=epilog)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
_parser_add_bz_fields(p, "query")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
g = p.add_argument_group("'query' specific options")
|
|
Jeff Mahoney |
3dff52 |
g.add_argument('-b', '--id', '--bug_id',
|
|
Jeff Mahoney |
3dff52 |
help="specify individual bugs by IDs, separated with commas")
|
|
Jeff Mahoney |
3dff52 |
g.add_argument('-r', '--reporter',
|
|
Jeff Mahoney |
3dff52 |
help="Email: search reporter email for given address")
|
|
Jeff Mahoney |
3dff52 |
g.add_argument('--quicksearch',
|
|
Jeff Mahoney |
3dff52 |
help="Search using bugzilla's quicksearch functionality.")
|
|
Jeff Mahoney |
3dff52 |
g.add_argument('--savedsearch',
|
|
Jeff Mahoney |
3dff52 |
help="Name of a bugzilla saved search. If you don't own this "
|
|
Jeff Mahoney |
3dff52 |
"saved search, you must passed --savedsearch_sharer_id.")
|
|
Jeff Mahoney |
3dff52 |
g.add_argument('--savedsearch-sharer-id',
|
|
Jeff Mahoney |
3dff52 |
help="Owner ID of the --savedsearch. You can get this ID from "
|
|
Jeff Mahoney |
3dff52 |
"the URL bugzilla generates when running the saved search "
|
|
Jeff Mahoney |
3dff52 |
"from the web UI.")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
# Keep this at the end so it sticks out more
|
|
Jeff Mahoney |
3dff52 |
g.add_argument('--from-url', metavar="WEB_QUERY_URL",
|
|
Jeff Mahoney |
3dff52 |
help="Make a working query via bugzilla's 'Advanced search' web UI, "
|
|
Jeff Mahoney |
3dff52 |
"grab the url from your browser (the string with query.cgi or "
|
|
Jeff Mahoney |
3dff52 |
"buglist.cgi in it), and --from-url will run it via the "
|
|
Jeff Mahoney |
3dff52 |
"bugzilla API. Don't forget to quote the string! "
|
|
Jeff Mahoney |
3dff52 |
"This only works for Bugzilla 5 and Red Hat bugzilla")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
# Deprecated options
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-E', '--emailtype', help=argparse.SUPPRESS)
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--components_file', help=argparse.SUPPRESS)
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-U', '--url_type',
|
|
Jeff Mahoney |
3dff52 |
help=argparse.SUPPRESS)
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-K', '--keywords_type',
|
|
Jeff Mahoney |
3dff52 |
help=argparse.SUPPRESS)
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-W', '--status_whiteboard_type',
|
|
Jeff Mahoney |
3dff52 |
help=argparse.SUPPRESS)
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--fixed_in_type', help=argparse.SUPPRESS)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _setup_action_info_parser(subparsers):
|
|
Jeff Mahoney |
3dff52 |
description = ("List products or component information about the "
|
|
Jeff Mahoney |
3dff52 |
"bugzilla server.")
|
|
Jeff Mahoney |
3dff52 |
p = subparsers.add_parser("info", description=description)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
x = p.add_mutually_exclusive_group(required=True)
|
|
Jeff Mahoney |
3dff52 |
x.add_argument('-p', '--products', action='store_true',
|
|
Jeff Mahoney |
3dff52 |
help='Get a list of products')
|
|
Jeff Mahoney |
3dff52 |
x.add_argument('-c', '--components', metavar="PRODUCT",
|
|
Jeff Mahoney |
3dff52 |
help='List the components in the given product')
|
|
Jeff Mahoney |
3dff52 |
x.add_argument('-o', '--component_owners', metavar="PRODUCT",
|
|
Jeff Mahoney |
3dff52 |
help='List components (and their owners)')
|
|
Jeff Mahoney |
3dff52 |
x.add_argument('-v', '--versions', metavar="PRODUCT",
|
|
Jeff Mahoney |
3dff52 |
help='List the versions for the given product')
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('--active-components', action="store_true",
|
|
Jeff Mahoney |
3dff52 |
help='Only show active components. Combine with --components*')
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _setup_action_modify_parser(subparsers):
|
|
Jeff Mahoney |
3dff52 |
usage = ("bugzilla modify [options] BUGID [BUGID...]\n"
|
|
Jeff Mahoney |
3dff52 |
"Fields that take multiple values have a special input format.\n"
|
|
Jeff Mahoney |
3dff52 |
"Append: --cc=foo@example.com\n"
|
|
Jeff Mahoney |
3dff52 |
"Overwrite: --cc==foo@example.com\n"
|
|
Jeff Mahoney |
3dff52 |
"Remove: --cc=-foo@example.com\n"
|
|
Jeff Mahoney |
3dff52 |
"Options that accept this format: --cc, --blocked, --dependson,\n"
|
|
Jeff Mahoney |
3dff52 |
" --groups, --tags, whiteboard fields.")
|
|
Jeff Mahoney |
3dff52 |
p = subparsers.add_parser("modify", usage=usage)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
_parser_add_bz_fields(p, "modify")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
g = p.add_argument_group("'modify' specific options")
|
|
Jeff Mahoney |
3dff52 |
g.add_argument("ids", nargs="+", help="Bug IDs to modify")
|
|
Jeff Mahoney |
3dff52 |
g.add_argument('-k', '--close', metavar="RESOLUTION",
|
|
Jeff Mahoney |
3dff52 |
help='Close with the given resolution (WONTFIX, NOTABUG, etc.)')
|
|
Jeff Mahoney |
3dff52 |
g.add_argument('-d', '--dupeid', metavar="ORIGINAL",
|
|
Jeff Mahoney |
3dff52 |
help='ID of original bug. Implies --close DUPLICATE')
|
|
Jeff Mahoney |
3dff52 |
g.add_argument('--private', action='store_true', default=False,
|
|
Jeff Mahoney |
3dff52 |
help='Mark new comment as private')
|
|
Jeff Mahoney |
3dff52 |
g.add_argument('--reset-assignee', action="store_true",
|
|
Jeff Mahoney |
3dff52 |
help='Reset assignee to component default')
|
|
Jeff Mahoney |
3dff52 |
g.add_argument('--reset-qa-contact', action="store_true",
|
|
Jeff Mahoney |
3dff52 |
help='Reset QA contact to component default')
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _setup_action_attach_parser(subparsers):
|
|
Jeff Mahoney |
3dff52 |
usage = """
|
|
Jeff Mahoney |
3dff52 |
bugzilla attach --file=FILE --desc=DESC [--type=TYPE] BUGID [BUGID...]
|
|
Michal Koutný |
ccf7f1 |
bugzilla attach --get=ATTACHID --getall=BUGID [--ignore-obsolete] [...]
|
|
Jeff Mahoney |
3dff52 |
bugzilla attach --type=TYPE BUGID [BUGID...]"""
|
|
Jeff Mahoney |
3dff52 |
description = "Attach files or download attachments."
|
|
Jeff Mahoney |
3dff52 |
p = subparsers.add_parser("attach", description=description, usage=usage)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
p.add_argument("ids", nargs="*", help="BUGID references")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-f', '--file', metavar="FILENAME",
|
|
Jeff Mahoney |
3dff52 |
help='File to attach, or filename for data provided on stdin')
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-d', '--description', '--summary',
|
|
Jeff Mahoney |
3dff52 |
metavar="SUMMARY", dest='desc',
|
|
Jeff Mahoney |
3dff52 |
help="A short summary of the file being attached")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-t', '--type', metavar="MIMETYPE",
|
|
Jeff Mahoney |
3dff52 |
help="Mime-type for the file being attached")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument('-g', '--get', metavar="ATTACHID", action="append",
|
|
Jeff Mahoney |
3dff52 |
default=[], help="Download the attachment with the given ID")
|
|
Jeff Mahoney |
3dff52 |
p.add_argument("--getall", "--get-all", metavar="BUGID", action="append",
|
|
Jeff Mahoney |
3dff52 |
default=[], help="Download all attachments on the given bug")
|
|
Michal Koutný |
ccf7f1 |
p.add_argument('--ignore-obsolete', action="store_true",
|
|
Michal Koutný |
ccf7f1 |
help='Do not download attachments marked as obsolete.')
|
|
Michal Koutný |
ccf7f1 |
p.add_argument('-l', '--comment', '--long_desc',
|
|
Michal Koutný |
ccf7f1 |
help="Add comment with attachment")
|
|
Michal Koutný |
ccf7f1 |
p.add_argument('--private', action='store_true', default=False,
|
|
Michal Koutný |
ccf7f1 |
help='Mark new comment as private')
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _setup_action_login_parser(subparsers):
|
|
Michal Koutný |
ccf7f1 |
usage = 'bugzilla login [--api-key] [username [password]]'
|
|
Michal Koutný |
ccf7f1 |
description = """Log into bugzilla and save a login cookie or token.
|
|
Michal Koutný |
ccf7f1 |
Note: These tokens are short-lived, and future Bugzilla versions will no
|
|
Michal Koutný |
ccf7f1 |
longer support token authentication at all. Please use a
|
|
Michal Koutný |
ccf7f1 |
~/.config/python-bugzilla/bugzillarc file with an API key instead, or
|
|
Michal Koutný |
ccf7f1 |
use 'bugzilla login --api-key' and we will save it for you."""
|
|
Jeff Mahoney |
3dff52 |
p = subparsers.add_parser("login", description=description, usage=usage)
|
|
Michal Koutný |
ccf7f1 |
p.add_argument('--api-key', action='store_true', default=False,
|
|
Michal Koutný |
ccf7f1 |
help='Prompt for and save an API key into bugzillarc, '
|
|
Michal Koutný |
ccf7f1 |
'rather than prompt for username and password.')
|
|
Michal Koutný |
ccf7f1 |
p.add_argument("pos_username", nargs="?", help="Optional username ",
|
|
Michal Koutný |
ccf7f1 |
metavar="username")
|
|
Michal Koutný |
ccf7f1 |
p.add_argument("pos_password", nargs="?", help="Optional password ",
|
|
Michal Koutný |
ccf7f1 |
metavar="password")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def setup_parser():
|
|
Jeff Mahoney |
3dff52 |
rootparser = _setup_root_parser()
|
|
Jeff Mahoney |
3dff52 |
subparsers = rootparser.add_subparsers(dest="command")
|
|
Jeff Mahoney |
3dff52 |
subparsers.required = True
|
|
Jeff Mahoney |
3dff52 |
_setup_action_new_parser(subparsers)
|
|
Jeff Mahoney |
3dff52 |
_setup_action_query_parser(subparsers)
|
|
Jeff Mahoney |
3dff52 |
_setup_action_info_parser(subparsers)
|
|
Jeff Mahoney |
3dff52 |
_setup_action_modify_parser(subparsers)
|
|
Jeff Mahoney |
3dff52 |
_setup_action_attach_parser(subparsers)
|
|
Jeff Mahoney |
3dff52 |
_setup_action_login_parser(subparsers)
|
|
Jeff Mahoney |
3dff52 |
return rootparser
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
####################
|
|
Jeff Mahoney |
3dff52 |
# Command routines #
|
|
Jeff Mahoney |
3dff52 |
####################
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
def _merge_field_opts(query, fields, parser):
|
|
Jeff Mahoney |
3dff52 |
# Add any custom fields if specified
|
|
Michal Koutný |
ccf7f1 |
for f in fields:
|
|
Jeff Mahoney |
3dff52 |
try:
|
|
Jeff Mahoney |
3dff52 |
f, v = f.split('=', 1)
|
|
Jeff Mahoney |
3dff52 |
query[f] = v
|
|
Jeff Mahoney |
3dff52 |
except Exception:
|
|
Jeff Mahoney |
3dff52 |
parser.error("Invalid field argument provided: %s" % (f))
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _do_query(bz, opt, parser):
|
|
Jeff Mahoney |
3dff52 |
q = {}
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
# Parse preconstructed queries.
|
|
Jeff Mahoney |
3dff52 |
u = opt.from_url
|
|
Jeff Mahoney |
3dff52 |
if u:
|
|
Jeff Mahoney |
3dff52 |
q = bz.url_to_query(u)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if opt.components_file:
|
|
Jeff Mahoney |
3dff52 |
# Components slurped in from file (one component per line)
|
|
Jeff Mahoney |
3dff52 |
# This can be made more robust
|
|
Jeff Mahoney |
3dff52 |
clist = []
|
|
Jeff Mahoney |
3dff52 |
f = open(opt.components_file, 'r')
|
|
Jeff Mahoney |
3dff52 |
for line in f.readlines():
|
|
Jeff Mahoney |
3dff52 |
line = line.rstrip("\n")
|
|
Jeff Mahoney |
3dff52 |
clist.append(line)
|
|
Jeff Mahoney |
3dff52 |
opt.component = clist
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if opt.status:
|
|
Jeff Mahoney |
3dff52 |
val = opt.status
|
|
Jeff Mahoney |
3dff52 |
stat = val
|
|
Jeff Mahoney |
3dff52 |
if val == 'ALL':
|
|
Jeff Mahoney |
3dff52 |
# leaving this out should return bugs of any status
|
|
Jeff Mahoney |
3dff52 |
stat = None
|
|
Jeff Mahoney |
3dff52 |
elif val == 'DEV':
|
|
Jeff Mahoney |
3dff52 |
# Alias for all development bug statuses
|
|
Jeff Mahoney |
3dff52 |
stat = ['NEW', 'ASSIGNED', 'NEEDINFO', 'ON_DEV',
|
|
Jeff Mahoney |
3dff52 |
'MODIFIED', 'POST', 'REOPENED']
|
|
Jeff Mahoney |
3dff52 |
elif val == 'QE':
|
|
Jeff Mahoney |
3dff52 |
# Alias for all QE relevant bug statuses
|
|
Jeff Mahoney |
3dff52 |
stat = ['ASSIGNED', 'ON_QA', 'FAILS_QA', 'PASSES_QA']
|
|
Jeff Mahoney |
3dff52 |
elif val == 'EOL':
|
|
Jeff Mahoney |
3dff52 |
# Alias for EndOfLife bug statuses
|
|
Michal Koutný |
029c1e |
stat = ['VERIFIED', 'RELEASE_PENDING', 'RESOLVED']
|
|
Jeff Mahoney |
3dff52 |
elif val == 'OPEN':
|
|
Michal Koutný |
029c1e |
# non-RESOLVED statuses
|
|
Jeff Mahoney |
3dff52 |
stat = ['NEW', 'ASSIGNED', 'MODIFIED', 'ON_DEV', 'ON_QA',
|
|
Jeff Mahoney |
3dff52 |
'VERIFIED', 'RELEASE_PENDING', 'POST']
|
|
Jeff Mahoney |
3dff52 |
opt.status = stat
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
# Convert all comma separated list parameters to actual lists,
|
|
Jeff Mahoney |
3dff52 |
# which is what bugzilla wants
|
|
Jeff Mahoney |
3dff52 |
# According to bugzilla docs, any parameter can be a list, but
|
|
Jeff Mahoney |
3dff52 |
# let's only do this for options we explicitly mention can be
|
|
Jeff Mahoney |
3dff52 |
# comma separated.
|
|
Jeff Mahoney |
3dff52 |
for optname in ["severity", "id", "status", "component",
|
|
Jeff Mahoney |
3dff52 |
"priority", "product", "version"]:
|
|
Jeff Mahoney |
3dff52 |
val = getattr(opt, optname, None)
|
|
Jeff Mahoney |
3dff52 |
if not isinstance(val, str):
|
|
Jeff Mahoney |
3dff52 |
continue
|
|
Jeff Mahoney |
3dff52 |
setattr(opt, optname, val.split(","))
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
include_fields = None
|
|
Michal Koutný |
ccf7f1 |
if opt.output in ['raw', 'json']:
|
|
Jeff Mahoney |
3dff52 |
# 'raw' always does a getbug() call anyways, so just ask for ID back
|
|
Jeff Mahoney |
3dff52 |
include_fields = ['id']
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
elif opt.outputformat:
|
|
Jeff Mahoney |
3dff52 |
include_fields = []
|
|
Jeff Mahoney |
3dff52 |
for fieldname, rest in format_field_re.findall(opt.outputformat):
|
|
Jeff Mahoney |
3dff52 |
if fieldname == "whiteboard" and rest:
|
|
Jeff Mahoney |
3dff52 |
fieldname = rest + "_" + fieldname
|
|
Jeff Mahoney |
3dff52 |
elif fieldname == "flag":
|
|
Jeff Mahoney |
3dff52 |
fieldname = "flags"
|
|
Jeff Mahoney |
3dff52 |
elif fieldname == "cve":
|
|
Jeff Mahoney |
3dff52 |
fieldname = ["keywords", "blocks"]
|
|
Jeff Mahoney |
3dff52 |
elif fieldname == "__unicode__":
|
|
Jeff Mahoney |
3dff52 |
# Needs to be in sync with bug.__unicode__
|
|
Jeff Mahoney |
3dff52 |
fieldname = ["id", "status", "assigned_to", "summary"]
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
flist = isinstance(fieldname, list) and fieldname or [fieldname]
|
|
Jeff Mahoney |
3dff52 |
for f in flist:
|
|
Jeff Mahoney |
3dff52 |
if f not in include_fields:
|
|
Jeff Mahoney |
3dff52 |
include_fields.append(f)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if include_fields is not None:
|
|
Jeff Mahoney |
3dff52 |
include_fields.sort()
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
kwopts = {}
|
|
Michal Koutný |
ccf7f1 |
if opt.product:
|
|
Michal Koutný |
ccf7f1 |
kwopts["product"] = opt.product
|
|
Michal Koutný |
ccf7f1 |
if opt.component:
|
|
Michal Koutný |
ccf7f1 |
kwopts["component"] = opt.component
|
|
Michal Koutný |
ccf7f1 |
if opt.sub_component:
|
|
Michal Koutný |
ccf7f1 |
kwopts["sub_component"] = opt.sub_component
|
|
Michal Koutný |
ccf7f1 |
if opt.version:
|
|
Michal Koutný |
ccf7f1 |
kwopts["version"] = opt.version
|
|
Michal Koutný |
ccf7f1 |
if opt.reporter:
|
|
Michal Koutný |
ccf7f1 |
kwopts["reporter"] = opt.reporter
|
|
Michal Koutný |
ccf7f1 |
if opt.id:
|
|
Michal Koutný |
ccf7f1 |
kwopts["bug_id"] = opt.id
|
|
Michal Koutný |
ccf7f1 |
if opt.summary:
|
|
Michal Koutný |
ccf7f1 |
kwopts["short_desc"] = opt.summary
|
|
Michal Koutný |
ccf7f1 |
if opt.comment:
|
|
Michal Koutný |
ccf7f1 |
kwopts["long_desc"] = opt.comment
|
|
Michal Koutný |
ccf7f1 |
if opt.cc:
|
|
Michal Koutný |
ccf7f1 |
kwopts["cc"] = opt.cc
|
|
Michal Koutný |
ccf7f1 |
if opt.assigned_to:
|
|
Michal Koutný |
ccf7f1 |
kwopts["assigned_to"] = opt.assigned_to
|
|
Michal Koutný |
ccf7f1 |
if opt.qa_contact:
|
|
Michal Koutný |
ccf7f1 |
kwopts["qa_contact"] = opt.qa_contact
|
|
Michal Koutný |
ccf7f1 |
if opt.status:
|
|
Michal Koutný |
ccf7f1 |
kwopts["status"] = opt.status
|
|
Michal Koutný |
ccf7f1 |
if opt.blocked:
|
|
Michal Koutný |
ccf7f1 |
kwopts["blocked"] = opt.blocked
|
|
Michal Koutný |
ccf7f1 |
if opt.dependson:
|
|
Michal Koutný |
ccf7f1 |
kwopts["dependson"] = opt.dependson
|
|
Michal Koutný |
ccf7f1 |
if opt.keywords:
|
|
Michal Koutný |
ccf7f1 |
kwopts["keywords"] = opt.keywords
|
|
Michal Koutný |
ccf7f1 |
if opt.keywords_type:
|
|
Michal Koutný |
ccf7f1 |
kwopts["keywords_type"] = opt.keywords_type
|
|
Michal Koutný |
ccf7f1 |
if opt.url:
|
|
Michal Koutný |
ccf7f1 |
kwopts["url"] = opt.url
|
|
Michal Koutný |
ccf7f1 |
if opt.url_type:
|
|
Michal Koutný |
ccf7f1 |
kwopts["url_type"] = opt.url_type
|
|
Michal Koutný |
ccf7f1 |
if opt.whiteboard:
|
|
Michal Koutný |
ccf7f1 |
kwopts["status_whiteboard"] = opt.whiteboard
|
|
Michal Koutný |
ccf7f1 |
if opt.status_whiteboard_type:
|
|
Michal Koutný |
ccf7f1 |
kwopts["status_whiteboard_type"] = opt.status_whiteboard_type
|
|
Michal Koutný |
ccf7f1 |
if opt.fixed_in:
|
|
Michal Koutný |
ccf7f1 |
kwopts["fixed_in"] = opt.fixed_in
|
|
Michal Koutný |
ccf7f1 |
if opt.fixed_in_type:
|
|
Michal Koutný |
ccf7f1 |
kwopts["fixed_in_type"] = opt.fixed_in_type
|
|
Michal Koutný |
ccf7f1 |
if opt.flag:
|
|
Michal Koutný |
ccf7f1 |
kwopts["flag"] = opt.flag
|
|
Michal Koutný |
ccf7f1 |
if opt.alias:
|
|
Michal Koutný |
ccf7f1 |
kwopts["alias"] = opt.alias
|
|
Michal Koutný |
ccf7f1 |
if opt.qa_whiteboard:
|
|
Michal Koutný |
ccf7f1 |
kwopts["qa_whiteboard"] = opt.qa_whiteboard
|
|
Michal Koutný |
ccf7f1 |
if opt.devel_whiteboard:
|
|
Michal Koutný |
ccf7f1 |
kwopts["devel_whiteboard"] = opt.devel_whiteboard
|
|
Michal Koutný |
ccf7f1 |
if opt.severity:
|
|
Michal Koutný |
ccf7f1 |
kwopts["bug_severity"] = opt.severity
|
|
Michal Koutný |
ccf7f1 |
if opt.priority:
|
|
Michal Koutný |
ccf7f1 |
kwopts["priority"] = opt.priority
|
|
Michal Koutný |
ccf7f1 |
if opt.target_release:
|
|
Michal Koutný |
ccf7f1 |
kwopts["target_release"] = opt.target_release
|
|
Michal Koutný |
ccf7f1 |
if opt.target_milestone:
|
|
Michal Koutný |
ccf7f1 |
kwopts["target_milestone"] = opt.target_milestone
|
|
Michal Koutný |
ccf7f1 |
if opt.emailtype:
|
|
Michal Koutný |
ccf7f1 |
kwopts["emailtype"] = opt.emailtype
|
|
Michal Koutný |
ccf7f1 |
if include_fields:
|
|
Michal Koutný |
ccf7f1 |
kwopts["include_fields"] = include_fields
|
|
Michal Koutný |
ccf7f1 |
if opt.quicksearch:
|
|
Michal Koutný |
ccf7f1 |
kwopts["quicksearch"] = opt.quicksearch
|
|
Michal Koutný |
ccf7f1 |
if opt.savedsearch:
|
|
Michal Koutný |
ccf7f1 |
kwopts["savedsearch"] = opt.savedsearch
|
|
Michal Koutný |
ccf7f1 |
if opt.savedsearch_sharer_id:
|
|
Michal Koutný |
ccf7f1 |
kwopts["savedsearch_sharer_id"] = opt.savedsearch_sharer_id
|
|
Michal Koutný |
ccf7f1 |
if opt.tags:
|
|
Michal Koutný |
ccf7f1 |
kwopts["tags"] = opt.tags
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
built_query = bz.build_query(**kwopts)
|
|
Michal Koutný |
ccf7f1 |
if opt.fields:
|
|
Michal Koutný |
ccf7f1 |
_merge_field_opts(built_query, opt.fields, parser)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
built_query.update(q)
|
|
Jeff Mahoney |
3dff52 |
q = built_query
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
if not q: # pragma: no cover
|
|
Jeff Mahoney |
3dff52 |
parser.error("'query' command requires additional arguments")
|
|
Jeff Mahoney |
3dff52 |
return bz.query(q)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _do_info(bz, opt):
|
|
Jeff Mahoney |
3dff52 |
"""
|
|
Jeff Mahoney |
3dff52 |
Handle the 'info' subcommand
|
|
Jeff Mahoney |
3dff52 |
"""
|
|
Jeff Mahoney |
3dff52 |
# All these commands call getproducts internally, so do it up front
|
|
Jeff Mahoney |
3dff52 |
# with minimal include_fields for speed
|
|
Jeff Mahoney |
3dff52 |
def _filter_components(compdetails):
|
|
Jeff Mahoney |
3dff52 |
ret = {}
|
|
Jeff Mahoney |
3dff52 |
for k, v in compdetails.items():
|
|
Jeff Mahoney |
3dff52 |
if v.get("is_active", True):
|
|
Jeff Mahoney |
3dff52 |
ret[k] = v
|
|
Jeff Mahoney |
3dff52 |
return ret
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
productname = (opt.components or opt.component_owners or opt.versions)
|
|
Jeff Mahoney |
3dff52 |
fastcomponents = (opt.components and not opt.active_components)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
include_fields = ["name", "id"]
|
|
Michal Koutný |
ccf7f1 |
if opt.components or opt.component_owners:
|
|
Michal Koutný |
ccf7f1 |
include_fields += ["components.name"]
|
|
Michal Koutný |
ccf7f1 |
if opt.component_owners:
|
|
Michal Koutný |
ccf7f1 |
include_fields += ["components.default_assigned_to"]
|
|
Michal Koutný |
ccf7f1 |
if opt.active_components:
|
|
Michal Koutný |
ccf7f1 |
include_fields += ["components.is_active"]
|
|
Michal Koutný |
ccf7f1 |
|
|
Jeff Mahoney |
3dff52 |
if opt.versions:
|
|
Jeff Mahoney |
3dff52 |
include_fields += ["versions"]
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
bz.refresh_products(names=productname and [productname] or None,
|
|
Jeff Mahoney |
3dff52 |
include_fields=include_fields)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if opt.products:
|
|
Jeff Mahoney |
3dff52 |
for name in sorted([p["name"] for p in bz.getproducts()]):
|
|
Jeff Mahoney |
3dff52 |
print(name)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
elif fastcomponents:
|
|
Jeff Mahoney |
3dff52 |
for name in sorted(bz.getcomponents(productname)):
|
|
Jeff Mahoney |
3dff52 |
print(name)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
elif opt.components:
|
|
Jeff Mahoney |
3dff52 |
details = bz.getcomponentsdetails(productname)
|
|
Jeff Mahoney |
3dff52 |
for name in sorted(_filter_components(details)):
|
|
Jeff Mahoney |
3dff52 |
print(name)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
elif opt.versions:
|
|
Jeff Mahoney |
3dff52 |
proddict = bz.getproducts()[0]
|
|
Jeff Mahoney |
3dff52 |
for v in proddict['versions']:
|
|
Michal Koutný |
ccf7f1 |
print(str(v["name"] or ''))
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
elif opt.component_owners:
|
|
Jeff Mahoney |
3dff52 |
details = bz.getcomponentsdetails(productname)
|
|
Jeff Mahoney |
3dff52 |
for c in sorted(_filter_components(details)):
|
|
Michal Koutný |
ccf7f1 |
print("%s: %s" % (c, details[c]['default_assigned_to']))
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _convert_to_outputformat(output):
|
|
Jeff Mahoney |
3dff52 |
fmt = ""
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if output == "normal":
|
|
Jeff Mahoney |
3dff52 |
fmt = "%{__unicode__}"
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
elif output == "ids":
|
|
Jeff Mahoney |
3dff52 |
fmt = "%{id}"
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
elif output == 'full':
|
|
Jeff Mahoney |
3dff52 |
fmt += "%{__unicode__}\n"
|
|
Jeff Mahoney |
3dff52 |
fmt += "Component: %{component}\n"
|
|
Jeff Mahoney |
3dff52 |
fmt += "CC: %{cc}\n"
|
|
Jeff Mahoney |
3dff52 |
fmt += "Blocked: %{blocks}\n"
|
|
Jeff Mahoney |
3dff52 |
fmt += "Depends: %{depends_on}\n"
|
|
Jeff Mahoney |
3dff52 |
fmt += "%{comments}\n"
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
elif output == 'extra':
|
|
Jeff Mahoney |
3dff52 |
fmt += "%{__unicode__}\n"
|
|
Jeff Mahoney |
3dff52 |
fmt += " +Keywords: %{keywords}\n"
|
|
Jeff Mahoney |
3dff52 |
fmt += " +QA Whiteboard: %{qa_whiteboard}\n"
|
|
Jeff Mahoney |
3dff52 |
fmt += " +Status Whiteboard: %{status_whiteboard}\n"
|
|
Jeff Mahoney |
3dff52 |
fmt += " +Devel Whiteboard: %{devel_whiteboard}\n"
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
elif output == 'oneline':
|
|
Jeff Mahoney |
3dff52 |
fmt += "#%{bug_id} %{status} %{assigned_to} %{component}\t"
|
|
Jeff Mahoney |
3dff52 |
fmt += "[%{target_milestone}] %{flags} %{cve}"
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
else: # pragma: no cover
|
|
Jeff Mahoney |
3dff52 |
raise RuntimeError("Unknown output type '%s'" % output)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
return fmt
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
def _xmlrpc_converter(obj):
|
|
Michal Koutný |
ccf7f1 |
if "DateTime" in str(obj.__class__):
|
|
Michal Koutný |
ccf7f1 |
# xmlrpc DateTime object. Convert to date format that
|
|
Michal Koutný |
ccf7f1 |
# bugzilla REST API outputs
|
|
Michal Koutný |
ccf7f1 |
dobj = datetime.datetime.strptime(str(obj), '%Y%m%dT%H:%M:%S')
|
|
Michal Koutný |
ccf7f1 |
return dobj.isoformat() + "Z"
|
|
Michal Koutný |
ccf7f1 |
if "Binary" in str(obj.__class__):
|
|
Michal Koutný |
ccf7f1 |
# xmlrpc Binary object. Convert to base64
|
|
Michal Koutný |
ccf7f1 |
return base64.b64encode(obj.data).decode("utf-8")
|
|
Michal Koutný |
ccf7f1 |
raise RuntimeError(
|
|
Michal Koutný |
ccf7f1 |
"Unexpected JSON conversion class=%s" % obj.__class__)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def _format_output_json(buglist):
|
|
Michal Koutný |
ccf7f1 |
out = {"bugs": [b.get_raw_data() for b in buglist]}
|
|
Michal Koutný |
ccf7f1 |
s = json.dumps(out, default=_xmlrpc_converter, indent=2, sort_keys=True)
|
|
Michal Koutný |
ccf7f1 |
print(s)
|
|
Michal Koutný |
ccf7f1 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
def _format_output_raw(buglist):
|
|
Michal Koutný |
ccf7f1 |
for b in buglist:
|
|
Michal Koutný |
ccf7f1 |
print("Bugzilla %s: " % b.bug_id)
|
|
Michal Koutný |
ccf7f1 |
SKIP_NAMES = ["bugzilla"]
|
|
Michal Koutný |
ccf7f1 |
for attrname in sorted(b.__dict__):
|
|
Michal Koutný |
ccf7f1 |
if attrname in SKIP_NAMES:
|
|
Michal Koutný |
ccf7f1 |
continue
|
|
Michal Koutný |
ccf7f1 |
if attrname.startswith("_"):
|
|
Michal Koutný |
ccf7f1 |
continue
|
|
Michal Koutný |
ccf7f1 |
print("ATTRIBUTE[%s]: %s" % (attrname, b.__dict__[attrname]))
|
|
Michal Koutný |
ccf7f1 |
print("\n\n")
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def _bug_field_repl_cb(bz, b, matchobj):
|
|
Michal Koutný |
ccf7f1 |
# whiteboard and flag allow doing
|
|
Michal Koutný |
ccf7f1 |
# %{whiteboard:devel} and %{flag:needinfo}
|
|
Michal Koutný |
ccf7f1 |
# That's what 'rest' matches
|
|
Michal Koutný |
ccf7f1 |
(fieldname, rest) = matchobj.groups()
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
if fieldname == "whiteboard" and rest:
|
|
Michal Koutný |
ccf7f1 |
fieldname = rest + "_" + fieldname
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
if fieldname == "flag" and rest:
|
|
Michal Koutný |
ccf7f1 |
val = b.get_flag_status(rest)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
elif fieldname in ["flags", "flags_requestee"]:
|
|
Michal Koutný |
ccf7f1 |
tmpstr = []
|
|
Michal Koutný |
ccf7f1 |
for f in getattr(b, "flags", []):
|
|
Michal Koutný |
ccf7f1 |
requestee = f.get('requestee', "")
|
|
Michal Koutný |
ccf7f1 |
if fieldname == "flags":
|
|
Michal Koutný |
ccf7f1 |
requestee = ""
|
|
Michal Koutný |
ccf7f1 |
if fieldname == "flags_requestee":
|
|
Michal Koutný |
ccf7f1 |
if requestee == "":
|
|
Jeff Mahoney |
3dff52 |
continue
|
|
Michal Koutný |
ccf7f1 |
tmpstr.append("%s" % requestee)
|
|
Michal Koutný |
ccf7f1 |
else:
|
|
Michal Koutný |
ccf7f1 |
tmpstr.append("%s%s%s" %
|
|
Michal Koutný |
ccf7f1 |
(f['name'], f['status'], requestee))
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
val = ",".join(tmpstr)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
elif fieldname == "cve":
|
|
Michal Koutný |
ccf7f1 |
cves = []
|
|
Michal Koutný |
ccf7f1 |
for key in getattr(b, "keywords", []):
|
|
Michal Koutný |
ccf7f1 |
# grab CVE from keywords and blockers
|
|
Michal Koutný |
ccf7f1 |
if key.find("Security") == -1:
|
|
Michal Koutný |
ccf7f1 |
continue
|
|
Michal Koutný |
ccf7f1 |
for bl in b.blocks:
|
|
Michal Koutný |
ccf7f1 |
cvebug = bz.getbug(bl)
|
|
Michal Koutný |
ccf7f1 |
for cb in cvebug.alias:
|
|
Michal Koutný |
ccf7f1 |
if (cb.find("CVE") != -1 and
|
|
Michal Koutný |
ccf7f1 |
cb.strip() not in cves):
|
|
Michal Koutný |
ccf7f1 |
cves.append(cb)
|
|
Michal Koutný |
ccf7f1 |
val = ",".join(cves)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
elif fieldname == "comments":
|
|
Michal Koutný |
ccf7f1 |
val = ""
|
|
Michal Koutný |
ccf7f1 |
for c in getattr(b, "comments", []):
|
|
Michal Koutný |
ccf7f1 |
val += ("\n* %s - %s:\n%s\n" % (c['time'],
|
|
Michal Koutný |
ccf7f1 |
c.get("creator", c.get("author", "")), c['text']))
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
elif fieldname == "external_bugs":
|
|
Michal Koutný |
ccf7f1 |
val = ""
|
|
Michal Koutný |
ccf7f1 |
for e in getattr(b, "external_bugs", []):
|
|
Michal Koutný |
ccf7f1 |
url = e["type"]["full_url"].replace("%id%", e["ext_bz_bug_id"])
|
|
Michal Koutný |
ccf7f1 |
if not val:
|
|
Michal Koutný |
ccf7f1 |
val += "\n"
|
|
Michal Koutný |
ccf7f1 |
val += "External bug: %s\n" % url
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
elif fieldname == "__unicode__":
|
|
Michal Koutný |
ccf7f1 |
val = b.__unicode__()
|
|
Michal Koutný |
ccf7f1 |
else:
|
|
Michal Koutný |
ccf7f1 |
val = getattr(b, fieldname, "")
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
vallist = isinstance(val, list) and val or [val]
|
|
Michal Koutný |
ccf7f1 |
val = ','.join([str(v or '') for v in vallist])
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
return val
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def _format_output(bz, opt, buglist):
|
|
Michal Koutný |
ccf7f1 |
if opt.output in ['raw', 'json']:
|
|
Michal Koutný |
ccf7f1 |
include_fields = None
|
|
Michal Koutný |
ccf7f1 |
exclude_fields = None
|
|
Michal Koutný |
ccf7f1 |
extra_fields = None
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
if opt.includefield:
|
|
Michal Koutný |
ccf7f1 |
include_fields = opt.includefield
|
|
Michal Koutný |
ccf7f1 |
if opt.excludefield:
|
|
Michal Koutný |
ccf7f1 |
exclude_fields = opt.excludefield
|
|
Michal Koutný |
ccf7f1 |
if opt.extrafield:
|
|
Michal Koutný |
ccf7f1 |
extra_fields = opt.extrafield
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
buglist = bz.getbugs([b.bug_id for b in buglist],
|
|
Michal Koutný |
ccf7f1 |
include_fields=include_fields,
|
|
Michal Koutný |
ccf7f1 |
exclude_fields=exclude_fields,
|
|
Michal Koutný |
ccf7f1 |
extra_fields=extra_fields)
|
|
Michal Koutný |
ccf7f1 |
if opt.output == 'json':
|
|
Michal Koutný |
ccf7f1 |
_format_output_json(buglist)
|
|
Michal Koutný |
ccf7f1 |
if opt.output == 'raw':
|
|
Michal Koutný |
ccf7f1 |
_format_output_raw(buglist)
|
|
Michal Koutný |
ccf7f1 |
return
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
for b in buglist:
|
|
Michal Koutný |
ccf7f1 |
# pylint: disable=cell-var-from-loop
|
|
Michal Koutný |
ccf7f1 |
def cb(matchobj):
|
|
Michal Koutný |
ccf7f1 |
return _bug_field_repl_cb(bz, b, matchobj)
|
|
Michal Koutný |
ccf7f1 |
print(format_field_re.sub(cb, opt.outputformat))
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _parse_triset(vallist, checkplus=True, checkminus=True, checkequal=True,
|
|
Jeff Mahoney |
3dff52 |
splitcomma=False):
|
|
Jeff Mahoney |
3dff52 |
add_val = []
|
|
Jeff Mahoney |
3dff52 |
rm_val = []
|
|
Jeff Mahoney |
3dff52 |
set_val = None
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def make_list(v):
|
|
Jeff Mahoney |
3dff52 |
if not v:
|
|
Jeff Mahoney |
3dff52 |
return []
|
|
Jeff Mahoney |
3dff52 |
if splitcomma:
|
|
Jeff Mahoney |
3dff52 |
return v.split(",")
|
|
Jeff Mahoney |
3dff52 |
return [v]
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
for val in isinstance(vallist, list) and vallist or [vallist]:
|
|
Jeff Mahoney |
3dff52 |
val = val or ""
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if val.startswith("+") and checkplus:
|
|
Jeff Mahoney |
3dff52 |
add_val += make_list(val[1:])
|
|
Jeff Mahoney |
3dff52 |
elif val.startswith("-") and checkminus:
|
|
Jeff Mahoney |
3dff52 |
rm_val += make_list(val[1:])
|
|
Jeff Mahoney |
3dff52 |
elif val.startswith("=") and checkequal:
|
|
Jeff Mahoney |
3dff52 |
# Intentionally overwrite this
|
|
Jeff Mahoney |
3dff52 |
set_val = make_list(val[1:])
|
|
Jeff Mahoney |
3dff52 |
else:
|
|
Jeff Mahoney |
3dff52 |
add_val += make_list(val)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
return add_val, rm_val, set_val
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _do_new(bz, opt, parser):
|
|
Jeff Mahoney |
3dff52 |
# Parse options that accept comma separated list
|
|
Jeff Mahoney |
3dff52 |
def parse_multi(val):
|
|
Jeff Mahoney |
3dff52 |
return _parse_triset(val, checkplus=False, checkminus=False,
|
|
Jeff Mahoney |
3dff52 |
checkequal=False, splitcomma=True)[0]
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
kwopts = {}
|
|
Michal Koutný |
ccf7f1 |
if opt.blocked:
|
|
Michal Koutný |
ccf7f1 |
kwopts["blocks"] = parse_multi(opt.blocked)
|
|
Michal Koutný |
ccf7f1 |
if opt.cc:
|
|
Michal Koutný |
ccf7f1 |
kwopts["cc"] = parse_multi(opt.cc)
|
|
Michal Koutný |
ccf7f1 |
if opt.component:
|
|
Michal Koutný |
ccf7f1 |
kwopts["component"] = opt.component
|
|
Michal Koutný |
ccf7f1 |
if opt.dependson:
|
|
Michal Koutný |
ccf7f1 |
kwopts["depends_on"] = parse_multi(opt.dependson)
|
|
Michal Koutný |
ccf7f1 |
if opt.comment:
|
|
Michal Koutný |
ccf7f1 |
kwopts["description"] = opt.comment
|
|
Michal Koutný |
ccf7f1 |
if opt.groups:
|
|
Michal Koutný |
ccf7f1 |
kwopts["groups"] = parse_multi(opt.groups)
|
|
Michal Koutný |
ccf7f1 |
if opt.keywords:
|
|
Michal Koutný |
ccf7f1 |
kwopts["keywords"] = parse_multi(opt.keywords)
|
|
Michal Koutný |
ccf7f1 |
if opt.os:
|
|
Michal Koutný |
ccf7f1 |
kwopts["op_sys"] = opt.os
|
|
Michal Koutný |
ccf7f1 |
if opt.arch:
|
|
Michal Koutný |
ccf7f1 |
kwopts["platform"] = opt.arch
|
|
Michal Koutný |
ccf7f1 |
if opt.priority:
|
|
Michal Koutný |
ccf7f1 |
kwopts["priority"] = opt.priority
|
|
Michal Koutný |
ccf7f1 |
if opt.product:
|
|
Michal Koutný |
ccf7f1 |
kwopts["product"] = opt.product
|
|
Michal Koutný |
ccf7f1 |
if opt.severity:
|
|
Michal Koutný |
ccf7f1 |
kwopts["severity"] = opt.severity
|
|
Michal Koutný |
ccf7f1 |
if opt.summary:
|
|
Michal Koutný |
ccf7f1 |
kwopts["summary"] = opt.summary
|
|
Michal Koutný |
ccf7f1 |
if opt.url:
|
|
Michal Koutný |
ccf7f1 |
kwopts["url"] = opt.url
|
|
Michal Koutný |
ccf7f1 |
if opt.version:
|
|
Michal Koutný |
ccf7f1 |
kwopts["version"] = opt.version
|
|
Michal Koutný |
ccf7f1 |
if opt.assigned_to:
|
|
Michal Koutný |
ccf7f1 |
kwopts["assigned_to"] = opt.assigned_to
|
|
Michal Koutný |
ccf7f1 |
if opt.qa_contact:
|
|
Michal Koutný |
ccf7f1 |
kwopts["qa_contact"] = opt.qa_contact
|
|
Michal Koutný |
ccf7f1 |
if opt.sub_component:
|
|
Michal Koutný |
ccf7f1 |
kwopts["sub_component"] = opt.sub_component
|
|
Michal Koutný |
ccf7f1 |
if opt.alias:
|
|
Michal Koutný |
ccf7f1 |
kwopts["alias"] = opt.alias
|
|
Michal Koutný |
ccf7f1 |
if opt.comment_tag:
|
|
Michal Koutný |
ccf7f1 |
kwopts["comment_tags"] = opt.comment_tag
|
|
Michal Koutný |
ccf7f1 |
if opt.private:
|
|
Michal Koutný |
ccf7f1 |
kwopts["comment_private"] = opt.private
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
ret = bz.build_createbug(**kwopts)
|
|
Michal Koutný |
ccf7f1 |
if opt.fields:
|
|
Michal Koutný |
ccf7f1 |
_merge_field_opts(ret, opt.fields, parser)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
b = bz.createbug(ret)
|
|
Michal Koutný |
029c1e |
if not opt.no_refresh:
|
|
Michal Koutný |
029c1e |
b.refresh()
|
|
Jeff Mahoney |
3dff52 |
return [b]
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _do_modify(bz, parser, opt):
|
|
Jeff Mahoney |
3dff52 |
bugid_list = [bugid for a in opt.ids for bugid in a.split(',')]
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
add_wb, rm_wb, set_wb = _parse_triset(opt.whiteboard)
|
|
Jeff Mahoney |
3dff52 |
add_devwb, rm_devwb, set_devwb = _parse_triset(opt.devel_whiteboard)
|
|
Jeff Mahoney |
3dff52 |
add_intwb, rm_intwb, set_intwb = _parse_triset(opt.internal_whiteboard)
|
|
Jeff Mahoney |
3dff52 |
add_qawb, rm_qawb, set_qawb = _parse_triset(opt.qa_whiteboard)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
add_blk, rm_blk, set_blk = _parse_triset(opt.blocked, splitcomma=True)
|
|
Jeff Mahoney |
3dff52 |
add_deps, rm_deps, set_deps = _parse_triset(opt.dependson, splitcomma=True)
|
|
Jeff Mahoney |
3dff52 |
add_key, rm_key, set_key = _parse_triset(opt.keywords)
|
|
Jeff Mahoney |
3dff52 |
add_cc, rm_cc, ignore = _parse_triset(opt.cc,
|
|
Jeff Mahoney |
3dff52 |
checkplus=False,
|
|
Jeff Mahoney |
3dff52 |
checkequal=False)
|
|
Jeff Mahoney |
3dff52 |
add_groups, rm_groups, ignore = _parse_triset(opt.groups,
|
|
Jeff Mahoney |
3dff52 |
checkequal=False,
|
|
Jeff Mahoney |
3dff52 |
splitcomma=True)
|
|
Jeff Mahoney |
3dff52 |
add_tags, rm_tags, ignore = _parse_triset(opt.tags, checkequal=False)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
status = opt.status or None
|
|
Jeff Mahoney |
3dff52 |
if opt.dupeid is not None:
|
|
Jeff Mahoney |
3dff52 |
opt.close = "DUPLICATE"
|
|
Jeff Mahoney |
3dff52 |
if opt.close:
|
|
Michal Koutný |
029c1e |
status = "RESOLVED"
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
flags = []
|
|
Jeff Mahoney |
3dff52 |
if opt.flag:
|
|
Jeff Mahoney |
3dff52 |
# Convert "foo+" to tuple ("foo", "+")
|
|
Jeff Mahoney |
3dff52 |
for f in opt.flag:
|
|
Jeff Mahoney |
3dff52 |
flags.append({"name": f[:-1], "status": f[-1]})
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
update_opts = {}
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
if opt.assigned_to:
|
|
Michal Koutný |
ccf7f1 |
update_opts["assigned_to"] = opt.assigned_to
|
|
Michal Koutný |
ccf7f1 |
if opt.comment:
|
|
Michal Koutný |
ccf7f1 |
update_opts["comment"] = opt.comment
|
|
Michal Koutný |
ccf7f1 |
if opt.private:
|
|
Michal Koutný |
ccf7f1 |
update_opts["comment_private"] = opt.private
|
|
Michal Koutný |
ccf7f1 |
if opt.component:
|
|
Michal Koutný |
ccf7f1 |
update_opts["component"] = opt.component
|
|
Michal Koutný |
ccf7f1 |
if opt.product:
|
|
Michal Koutný |
ccf7f1 |
update_opts["product"] = opt.product
|
|
Michal Koutný |
ccf7f1 |
if add_blk:
|
|
Michal Koutný |
ccf7f1 |
update_opts["blocks_add"] = add_blk
|
|
Michal Koutný |
ccf7f1 |
if rm_blk:
|
|
Michal Koutný |
ccf7f1 |
update_opts["blocks_remove"] = rm_blk
|
|
Michal Koutný |
ccf7f1 |
if set_blk is not None:
|
|
Michal Koutný |
ccf7f1 |
update_opts["blocks_set"] = set_blk
|
|
Michal Koutný |
ccf7f1 |
if opt.url:
|
|
Michal Koutný |
ccf7f1 |
update_opts["url"] = opt.url
|
|
Michal Koutný |
ccf7f1 |
if add_cc:
|
|
Michal Koutný |
ccf7f1 |
update_opts["cc_add"] = add_cc
|
|
Michal Koutný |
ccf7f1 |
if rm_cc:
|
|
Michal Koutný |
ccf7f1 |
update_opts["cc_remove"] = rm_cc
|
|
Michal Koutný |
ccf7f1 |
if add_deps:
|
|
Michal Koutný |
ccf7f1 |
update_opts["depends_on_add"] = add_deps
|
|
Michal Koutný |
ccf7f1 |
if rm_deps:
|
|
Michal Koutný |
ccf7f1 |
update_opts["depends_on_remove"] = rm_deps
|
|
Michal Koutný |
ccf7f1 |
if set_deps is not None:
|
|
Michal Koutný |
ccf7f1 |
update_opts["depends_on_set"] = set_deps
|
|
Michal Koutný |
ccf7f1 |
if add_groups:
|
|
Michal Koutný |
ccf7f1 |
update_opts["groups_add"] = add_groups
|
|
Michal Koutný |
ccf7f1 |
if rm_groups:
|
|
Michal Koutný |
ccf7f1 |
update_opts["groups_remove"] = rm_groups
|
|
Michal Koutný |
ccf7f1 |
if add_key:
|
|
Michal Koutný |
ccf7f1 |
update_opts["keywords_add"] = add_key
|
|
Michal Koutný |
ccf7f1 |
if rm_key:
|
|
Michal Koutný |
ccf7f1 |
update_opts["keywords_remove"] = rm_key
|
|
Michal Koutný |
ccf7f1 |
if set_key is not None:
|
|
Michal Koutný |
ccf7f1 |
update_opts["keywords_set"] = set_key
|
|
Michal Koutný |
ccf7f1 |
if opt.os:
|
|
Michal Koutný |
ccf7f1 |
update_opts["op_sys"] = opt.os
|
|
Michal Koutný |
ccf7f1 |
if opt.arch:
|
|
Michal Koutný |
ccf7f1 |
update_opts["platform"] = opt.arch
|
|
Michal Koutný |
ccf7f1 |
if opt.priority:
|
|
Michal Koutný |
ccf7f1 |
update_opts["priority"] = opt.priority
|
|
Michal Koutný |
ccf7f1 |
if opt.qa_contact:
|
|
Michal Koutný |
ccf7f1 |
update_opts["qa_contact"] = opt.qa_contact
|
|
Michal Koutný |
ccf7f1 |
if opt.severity:
|
|
Michal Koutný |
ccf7f1 |
update_opts["severity"] = opt.severity
|
|
Michal Koutný |
ccf7f1 |
if status:
|
|
Michal Koutný |
ccf7f1 |
update_opts["status"] = status
|
|
Michal Koutný |
ccf7f1 |
if opt.summary:
|
|
Michal Koutný |
ccf7f1 |
update_opts["summary"] = opt.summary
|
|
Michal Koutný |
ccf7f1 |
if opt.version:
|
|
Michal Koutný |
ccf7f1 |
update_opts["version"] = opt.version
|
|
Michal Koutný |
ccf7f1 |
if opt.reset_assignee:
|
|
Michal Koutný |
ccf7f1 |
update_opts["reset_assigned_to"] = opt.reset_assignee
|
|
Michal Koutný |
ccf7f1 |
if opt.reset_qa_contact:
|
|
Michal Koutný |
ccf7f1 |
update_opts["reset_qa_contact"] = opt.reset_qa_contact
|
|
Michal Koutný |
ccf7f1 |
if opt.close:
|
|
Michal Koutný |
ccf7f1 |
update_opts["resolution"] = opt.close
|
|
Michal Koutný |
ccf7f1 |
if opt.target_release:
|
|
Michal Koutný |
ccf7f1 |
update_opts["target_release"] = opt.target_release
|
|
Michal Koutný |
ccf7f1 |
if opt.target_milestone:
|
|
Michal Koutný |
ccf7f1 |
update_opts["target_milestone"] = opt.target_milestone
|
|
Michal Koutný |
ccf7f1 |
if opt.dupeid:
|
|
Michal Koutný |
ccf7f1 |
update_opts["dupe_of"] = opt.dupeid
|
|
Michal Koutný |
ccf7f1 |
if opt.fixed_in:
|
|
Michal Koutný |
ccf7f1 |
update_opts["fixed_in"] = opt.fixed_in
|
|
Michal Koutný |
ccf7f1 |
if set_wb and set_wb[0]:
|
|
Michal Koutný |
ccf7f1 |
update_opts["whiteboard"] = set_wb and set_wb[0]
|
|
Michal Koutný |
ccf7f1 |
if set_devwb and set_devwb[0]:
|
|
Michal Koutný |
ccf7f1 |
update_opts["devel_whiteboard"] = set_devwb and set_devwb[0]
|
|
Michal Koutný |
ccf7f1 |
if set_intwb and set_intwb[0]:
|
|
Michal Koutný |
ccf7f1 |
update_opts["internal_whiteboard"] = set_intwb and set_intwb[0]
|
|
Michal Koutný |
ccf7f1 |
if set_qawb and set_qawb[0]:
|
|
Michal Koutný |
ccf7f1 |
update_opts["qa_whiteboard"] = set_qawb and set_qawb[0]
|
|
Michal Koutný |
ccf7f1 |
if opt.sub_component:
|
|
Michal Koutný |
ccf7f1 |
update_opts["sub_component"] = opt.sub_component
|
|
Michal Koutný |
ccf7f1 |
if opt.alias:
|
|
Michal Koutný |
ccf7f1 |
update_opts["alias"] = opt.alias
|
|
Michal Koutný |
ccf7f1 |
if flags:
|
|
Michal Koutný |
ccf7f1 |
update_opts["flags"] = flags
|
|
Michal Koutný |
ccf7f1 |
if opt.comment_tag:
|
|
Michal Koutný |
ccf7f1 |
update_opts["comment_tags"] = opt.comment_tag
|
|
Michal Koutný |
ccf7f1 |
if opt.minor_update:
|
|
Michal Koutný |
ccf7f1 |
update_opts["minor_update"] = opt.minor_update
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
update = bz.build_update(**update_opts)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
# We make this a little convoluted to facilitate unit testing
|
|
Jeff Mahoney |
3dff52 |
wbmap = {
|
|
Jeff Mahoney |
3dff52 |
"whiteboard": (add_wb, rm_wb),
|
|
Jeff Mahoney |
3dff52 |
"internal_whiteboard": (add_intwb, rm_intwb),
|
|
Jeff Mahoney |
3dff52 |
"qa_whiteboard": (add_qawb, rm_qawb),
|
|
Jeff Mahoney |
3dff52 |
"devel_whiteboard": (add_devwb, rm_devwb),
|
|
Jeff Mahoney |
3dff52 |
}
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
for k, v in wbmap.copy().items():
|
|
Jeff Mahoney |
3dff52 |
if not v[0] and not v[1]:
|
|
Jeff Mahoney |
3dff52 |
del(wbmap[k])
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
if opt.fields:
|
|
Michal Koutný |
ccf7f1 |
_merge_field_opts(update, opt.fields, parser)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
log.debug("update bug dict=%s", update)
|
|
Jeff Mahoney |
3dff52 |
log.debug("update whiteboard dict=%s", wbmap)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if not any([update, wbmap, add_tags, rm_tags]):
|
|
Jeff Mahoney |
3dff52 |
parser.error("'modify' command requires additional arguments")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if add_tags or rm_tags:
|
|
Jeff Mahoney |
3dff52 |
ret = bz.update_tags(bugid_list,
|
|
Jeff Mahoney |
3dff52 |
tags_add=add_tags, tags_remove=rm_tags)
|
|
Jeff Mahoney |
3dff52 |
log.debug("bz.update_tags returned=%s", ret)
|
|
Jeff Mahoney |
3dff52 |
if update:
|
|
Jeff Mahoney |
3dff52 |
ret = bz.update_bugs(bugid_list, update)
|
|
Jeff Mahoney |
3dff52 |
log.debug("bz.update_bugs returned=%s", ret)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if not wbmap:
|
|
Jeff Mahoney |
3dff52 |
return
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
# Now for the things we can't blindly batch.
|
|
Jeff Mahoney |
3dff52 |
# Being able to prepend/append to whiteboards, which are just
|
|
Jeff Mahoney |
3dff52 |
# plain string values, is an old rhbz semantic that we try to maintain
|
|
Michal Koutný |
ccf7f1 |
# here. This is a bit weird for traditional bugzilla API
|
|
Jeff Mahoney |
3dff52 |
log.debug("Adjusting whiteboard fields one by one")
|
|
Jeff Mahoney |
3dff52 |
for bug in bz.getbugs(bugid_list):
|
|
Michal Koutný |
ccf7f1 |
update_kwargs = {}
|
|
Michal Koutný |
ccf7f1 |
for wbkey, (add_list, rm_list) in wbmap.items():
|
|
Michal Koutný |
ccf7f1 |
bugval = getattr(bug, wbkey) or ""
|
|
Jeff Mahoney |
3dff52 |
for tag in add_list:
|
|
Michal Koutný |
ccf7f1 |
if bugval:
|
|
Michal Koutný |
ccf7f1 |
bugval += " "
|
|
Michal Koutný |
ccf7f1 |
bugval += tag
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
for tag in rm_list:
|
|
Michal Koutný |
ccf7f1 |
bugsplit = bugval.split()
|
|
Michal Koutný |
ccf7f1 |
for t in bugsplit[:]:
|
|
Jeff Mahoney |
3dff52 |
if t == tag:
|
|
Michal Koutný |
ccf7f1 |
bugsplit.remove(t)
|
|
Michal Koutný |
ccf7f1 |
bugval = " ".join(bugsplit)
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
update_kwargs[wbkey] = bugval
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
bz.update_bugs([bug.id], bz.build_update(**update_kwargs))
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _do_get_attach(bz, opt):
|
|
Michal Koutný |
ccf7f1 |
data = {}
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
def _process_attachment_data(_attlist):
|
|
Michal Koutný |
ccf7f1 |
for _att in _attlist:
|
|
Michal Koutný |
ccf7f1 |
data[_att["id"]] = _att
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
if opt.getall:
|
|
Michal Koutný |
ccf7f1 |
for attlist in bz.get_attachments(opt.getall, None)["bugs"].values():
|
|
Michal Koutný |
ccf7f1 |
_process_attachment_data(attlist)
|
|
Michal Koutný |
ccf7f1 |
if opt.get:
|
|
Michal Koutný |
ccf7f1 |
_process_attachment_data(
|
|
Michal Koutný |
ccf7f1 |
bz.get_attachments(None, opt.get)["attachments"].values())
|
|
Michal Koutný |
ccf7f1 |
|
|
Michal Koutný |
ccf7f1 |
for attdata in data.values():
|
|
Michal Koutný |
ccf7f1 |
is_obsolete = attdata.get("is_obsolete", None) == 1
|
|
Michal Koutný |
ccf7f1 |
if opt.ignore_obsolete and is_obsolete:
|
|
Michal Koutný |
ccf7f1 |
continue
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
att = bz.openattachment_data(attdata)
|
|
Jeff Mahoney |
3dff52 |
outfile = open_without_clobber(att.name, "wb")
|
|
Jeff Mahoney |
3dff52 |
data = att.read(4096)
|
|
Jeff Mahoney |
3dff52 |
while data:
|
|
Jeff Mahoney |
3dff52 |
outfile.write(data)
|
|
Jeff Mahoney |
3dff52 |
data = att.read(4096)
|
|
Jeff Mahoney |
3dff52 |
print("Wrote %s" % outfile.name)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _do_set_attach(bz, opt, parser):
|
|
Jeff Mahoney |
3dff52 |
if not opt.ids:
|
|
Jeff Mahoney |
3dff52 |
parser.error("Bug ID must be specified for setting attachments")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if sys.stdin.isatty():
|
|
Jeff Mahoney |
3dff52 |
if not opt.file:
|
|
Jeff Mahoney |
3dff52 |
parser.error("--file must be specified")
|
|
Jeff Mahoney |
3dff52 |
fileobj = open(opt.file, "rb")
|
|
Jeff Mahoney |
3dff52 |
else:
|
|
Jeff Mahoney |
3dff52 |
# piped input on stdin
|
|
Jeff Mahoney |
3dff52 |
if not opt.desc:
|
|
Jeff Mahoney |
3dff52 |
parser.error("--description must be specified if passing "
|
|
Jeff Mahoney |
3dff52 |
"file on stdin")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
fileobj = tempfile.NamedTemporaryFile(prefix="bugzilla-attach.")
|
|
Jeff Mahoney |
3dff52 |
data = sys.stdin.read(4096)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
while data:
|
|
Jeff Mahoney |
3dff52 |
fileobj.write(data.encode(locale.getpreferredencoding()))
|
|
Jeff Mahoney |
3dff52 |
data = sys.stdin.read(4096)
|
|
Jeff Mahoney |
3dff52 |
fileobj.seek(0)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
kwargs = {}
|
|
Jeff Mahoney |
3dff52 |
if opt.file:
|
|
Jeff Mahoney |
3dff52 |
kwargs["filename"] = os.path.basename(opt.file)
|
|
Jeff Mahoney |
3dff52 |
if opt.type:
|
|
Jeff Mahoney |
3dff52 |
kwargs["contenttype"] = opt.type
|
|
Jeff Mahoney |
3dff52 |
if opt.type in ["text/x-patch"]:
|
|
Jeff Mahoney |
3dff52 |
kwargs["ispatch"] = True
|
|
Jeff Mahoney |
3dff52 |
if opt.comment:
|
|
Jeff Mahoney |
3dff52 |
kwargs["comment"] = opt.comment
|
|
Michal Koutný |
ccf7f1 |
if opt.private:
|
|
Michal Koutný |
ccf7f1 |
kwargs["is_private"] = True
|
|
Jeff Mahoney |
3dff52 |
desc = opt.desc or os.path.basename(fileobj.name)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
# Upload attachments
|
|
Jeff Mahoney |
3dff52 |
for bugid in opt.ids:
|
|
Jeff Mahoney |
3dff52 |
attid = bz.attachfile(bugid, fileobj, desc, **kwargs)
|
|
Jeff Mahoney |
3dff52 |
print("Created attachment %i on bug %s" % (attid, bugid))
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
#################
|
|
Jeff Mahoney |
3dff52 |
# Main handling #
|
|
Jeff Mahoney |
3dff52 |
#################
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _make_bz_instance(opt):
|
|
Jeff Mahoney |
3dff52 |
"""
|
|
Jeff Mahoney |
3dff52 |
Build the Bugzilla instance we will use
|
|
Jeff Mahoney |
3dff52 |
"""
|
|
Jeff Mahoney |
3dff52 |
if opt.bztype != 'auto':
|
|
Jeff Mahoney |
3dff52 |
log.info("Explicit --bztype is no longer supported, ignoring")
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
cookiefile = None
|
|
Jeff Mahoney |
3dff52 |
tokenfile = None
|
|
Michal Koutný |
ccf7f1 |
use_creds = False
|
|
Jeff Mahoney |
3dff52 |
if opt.cache_credentials:
|
|
Jeff Mahoney |
3dff52 |
cookiefile = opt.cookiefile or -1
|
|
Jeff Mahoney |
3dff52 |
tokenfile = opt.tokenfile or -1
|
|
Michal Koutný |
ccf7f1 |
use_creds = True
|
|
Jeff Mahoney |
3dff52 |
|
|
Michal Koutný |
ccf7f1 |
return bugzilla.Bugzilla(
|
|
Jeff Mahoney |
3dff52 |
url=opt.bugzilla,
|
|
Jeff Mahoney |
3dff52 |
cookiefile=cookiefile,
|
|
Jeff Mahoney |
3dff52 |
tokenfile=tokenfile,
|
|
Jeff Mahoney |
3dff52 |
sslverify=opt.sslverify,
|
|
Michal Koutný |
ccf7f1 |
use_creds=use_creds,
|
|
Jeff Mahoney |
3dff52 |
cert=opt.cert)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _handle_login(opt, action, bz):
|
|
Jeff Mahoney |
3dff52 |
"""
|
|
Jeff Mahoney |
3dff52 |
Handle all login related bits
|
|
Jeff Mahoney |
3dff52 |
"""
|
|
Jeff Mahoney |
3dff52 |
is_login_command = (action == 'login')
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
do_interactive_login = (is_login_command or
|
|
Jeff Mahoney |
3dff52 |
opt.login or opt.username or opt.password)
|
|
Jeff Mahoney |
3dff52 |
username = getattr(opt, "pos_username", None) or opt.username
|
|
Jeff Mahoney |
3dff52 |
password = getattr(opt, "pos_password", None) or opt.password
|
|
Michal Koutný |
ccf7f1 |
use_key = getattr(opt, "api_key", False)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
try:
|
|
Michal Koutný |
ccf7f1 |
if use_key:
|
|
Michal Koutný |
ccf7f1 |
bz.interactive_save_api_key()
|
|
Michal Koutný |
ccf7f1 |
elif do_interactive_login:
|
|
Michal Koutný |
ccf7f1 |
if bz.api_key:
|
|
Michal Koutný |
ccf7f1 |
print("You already have an API key configured for %s" % bz.url)
|
|
Michal Koutný |
ccf7f1 |
print("There is no need to cache a login token. Exiting.")
|
|
Michal Koutný |
ccf7f1 |
sys.exit(0)
|
|
Michal Koutný |
ccf7f1 |
print("Logging into %s" % urllib.parse.urlparse(bz.url)[1])
|
|
Michal Koutný |
ccf7f1 |
bz.interactive_login(username, password,
|
|
Michal Koutný |
ccf7f1 |
restrict_login=opt.restrict_login)
|
|
Jeff Mahoney |
3dff52 |
except bugzilla.BugzillaError as e:
|
|
Jeff Mahoney |
3dff52 |
print(str(e))
|
|
Jeff Mahoney |
3dff52 |
sys.exit(1)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if opt.ensure_logged_in and not bz.logged_in:
|
|
Jeff Mahoney |
3dff52 |
print("--ensure-logged-in passed but you aren't logged in to %s" %
|
|
Jeff Mahoney |
3dff52 |
bz.url)
|
|
Jeff Mahoney |
3dff52 |
sys.exit(1)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if is_login_command:
|
|
Jeff Mahoney |
3dff52 |
sys.exit(0)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def _main(unittest_bz_instance):
|
|
Jeff Mahoney |
3dff52 |
parser = setup_parser()
|
|
Jeff Mahoney |
3dff52 |
opt = parser.parse_args()
|
|
Jeff Mahoney |
3dff52 |
action = opt.command
|
|
Jeff Mahoney |
3dff52 |
setup_logging(opt.debug, opt.verbose)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
log.debug("Launched with command line: %s", " ".join(sys.argv))
|
|
Michal Koutný |
ccf7f1 |
log.debug("Bugzilla module: %s", bugzilla)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if unittest_bz_instance:
|
|
Jeff Mahoney |
3dff52 |
bz = unittest_bz_instance
|
|
Jeff Mahoney |
3dff52 |
else:
|
|
Jeff Mahoney |
3dff52 |
bz = _make_bz_instance(opt)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
# Handle login options
|
|
Jeff Mahoney |
3dff52 |
_handle_login(opt, action, bz)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
###########################
|
|
Jeff Mahoney |
3dff52 |
# Run the actual commands #
|
|
Jeff Mahoney |
3dff52 |
###########################
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
if hasattr(opt, "outputformat"):
|
|
Michal Koutný |
ccf7f1 |
if not opt.outputformat and opt.output not in ['raw', 'json', None]:
|
|
Jeff Mahoney |
3dff52 |
opt.outputformat = _convert_to_outputformat(opt.output)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
buglist = []
|
|
Jeff Mahoney |
3dff52 |
if action == 'info':
|
|
Jeff Mahoney |
3dff52 |
_do_info(bz, opt)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
elif action == 'query':
|
|
Jeff Mahoney |
3dff52 |
buglist = _do_query(bz, opt, parser)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
elif action == 'new':
|
|
Jeff Mahoney |
3dff52 |
buglist = _do_new(bz, opt, parser)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
elif action == 'attach':
|
|
Jeff Mahoney |
3dff52 |
if opt.get or opt.getall:
|
|
Jeff Mahoney |
3dff52 |
if opt.ids:
|
|
Jeff Mahoney |
3dff52 |
parser.error("Bug IDs '%s' not used for "
|
|
Jeff Mahoney |
3dff52 |
"getting attachments" % opt.ids)
|
|
Jeff Mahoney |
3dff52 |
_do_get_attach(bz, opt)
|
|
Jeff Mahoney |
3dff52 |
else:
|
|
Jeff Mahoney |
3dff52 |
_do_set_attach(bz, opt, parser)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
elif action == 'modify':
|
|
Michal Koutný |
ccf7f1 |
_do_modify(bz, parser, opt)
|
|
Michal Koutný |
ccf7f1 |
else: # pragma: no cover
|
|
Jeff Mahoney |
3dff52 |
raise RuntimeError("Unexpected action '%s'" % action)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
# If we're doing new/query/modify, output our results
|
|
Jeff Mahoney |
3dff52 |
if action in ['new', 'query']:
|
|
Jeff Mahoney |
3dff52 |
_format_output(bz, opt, buglist)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def main(unittest_bz_instance=None):
|
|
Jeff Mahoney |
3dff52 |
try:
|
|
Jeff Mahoney |
3dff52 |
try:
|
|
Jeff Mahoney |
3dff52 |
return _main(unittest_bz_instance)
|
|
Jeff Mahoney |
3dff52 |
except (Exception, KeyboardInterrupt):
|
|
Jeff Mahoney |
3dff52 |
log.debug("", exc_info=True)
|
|
Jeff Mahoney |
3dff52 |
raise
|
|
Michal Koutný |
ccf7f1 |
except KeyboardInterrupt:
|
|
Michal Koutný |
ccf7f1 |
print("\nExited at user request.")
|
|
Michal Koutný |
ccf7f1 |
sys.exit(1)
|
|
Michal Koutný |
ccf7f1 |
except (xmlrpc.client.Fault, bugzilla.BugzillaError) as e:
|
|
Jeff Mahoney |
3dff52 |
print("\nServer error: %s" % str(e))
|
|
Jeff Mahoney |
3dff52 |
sys.exit(3)
|
|
Jeff Mahoney |
3dff52 |
except requests.exceptions.SSLError as e:
|
|
Jeff Mahoney |
3dff52 |
# Give SSL recommendations
|
|
Jeff Mahoney |
3dff52 |
print("SSL error: %s" % e)
|
|
Jeff Mahoney |
3dff52 |
print("\nIf you trust the remote server, you can work "
|
|
Jeff Mahoney |
3dff52 |
"around this error with:\n"
|
|
Jeff Mahoney |
3dff52 |
" bugzilla --nosslverify ...")
|
|
Jeff Mahoney |
3dff52 |
sys.exit(4)
|
|
Jeff Mahoney |
3dff52 |
except (socket.error,
|
|
Jeff Mahoney |
3dff52 |
requests.exceptions.HTTPError,
|
|
Jeff Mahoney |
3dff52 |
requests.exceptions.ConnectionError,
|
|
Michal Koutný |
ccf7f1 |
requests.exceptions.InvalidURL,
|
|
Michal Koutný |
ccf7f1 |
xmlrpc.client.ProtocolError) as e:
|
|
Jeff Mahoney |
3dff52 |
print("\nConnection lost/failed: %s" % str(e))
|
|
Jeff Mahoney |
3dff52 |
sys.exit(2)
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
|
|
Jeff Mahoney |
3dff52 |
def cli():
|
|
Michal Koutný |
ccf7f1 |
main()
|