From 96a1e3439e879078ef0d4d393a7676d572a8dc33 Mon Sep 17 00:00:00 2001 From: Pavel Raiskup Date: Mar 13 2013 12:46:38 +0000 Subject: Update to latest version Also remove the downstream patches. There is real that they are not going to be pushed upstream so we really have to wait for upstream here. Version: 0.10-3 --- diff --git a/.gitignore b/.gitignore index 7a55d68..413a1f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/pybugz-0.10-git89df2.tar.gz +/pybugz-0.10-git683dd.tar.gz diff --git a/pybugz-0.10-git683dd-rh-specific.patch b/pybugz-0.10-git683dd-rh-specific.patch new file mode 100644 index 0000000..e3db0d0 --- /dev/null +++ b/pybugz-0.10-git683dd-rh-specific.patch @@ -0,0 +1,58 @@ +diff --git a/bugz/cli.py b/bugz/cli.py +index aa45d3b..12e2224 100644 +--- a/bugz/cli.py ++++ b/bugz/cli.py +@@ -240,7 +240,10 @@ class PrettyBugz: + log_info(log_msg) + + if not 'status' in params.keys(): +- params['status'] = ['CONFIRMED', 'IN_PROGRESS', 'UNCONFIRMED'] ++ params['status'] = ['CONFIRMED', 'IN_PROGRESS', ++ 'UNCONFIRMED', 'NEW', 'ASSIGNED', 'ON_DEV', ++ 'POST', 'MODIFIED', 'ON_QA', 'VERIFIED', ++ 'RELEASE_PENDING'] + elif 'ALL' in params['status']: + del params['status'] + +diff --git a/bugzrc.example b/bugzrc.example +index f516bf0..e8e1234 100644 +--- a/bugzrc.example ++++ b/bugzrc.example +@@ -59,3 +59,20 @@ + # + # All parameters listed above can be used in the default section if you + # only use one bugzilla installation. ++ ++[gentoo] ++base: https://bugs.gentoo.org/xmlrpc.cgi ++columns: 80 ++encoding: utf-8 ++# user: myname@my.project.com ++# password: secret2 ++ ++[redhat] ++base: https://bugzilla.redhat.com/xmlrpc.cgi ++columns: 80 ++encoding: utf-8 ++# user: myname@my.project.com ++# password: secret2 ++ ++[default] ++connection: redhat +diff --git a/man/bugz.1 b/man/bugz.1 +index 5a88494..365ab7e 100644 +--- a/man/bugz.1 ++++ b/man/bugz.1 +@@ -28,6 +28,12 @@ This man page is a stub; the bugz program has extensive built in help. + will show the help for the global options and + .B bugz [subcommand] -h + will show the help for a specific subcommand. ++.SH CONFIGURATION ++PyBugz is still configured just using the ~/.bugzrc file. For simple template ++look at the bugzrc.example ++.B (rpm -qd pybugz | grep example) ++- you may copy it directly into your home directory to make PyBugz work by ++default with Red Hat bugzilla. + .SH BUGS + .PP + The home page of this project is http://www.github.com/williamh/pybugz. diff --git a/pybugz-0.10-git89df2-downstream.patch b/pybugz-0.10-git89df2-downstream.patch deleted file mode 100644 index fc95094..0000000 --- a/pybugz-0.10-git89df2-downstream.patch +++ /dev/null @@ -1,1145 +0,0 @@ -From ce7591bee38eb3b94f9b68cb5699a597195ff8fa Mon Sep 17 00:00:00 2001 -From: Pavel Raiskup -Date: Sun, 20 Jan 2013 23:20:50 +0100 -Subject: [PATCH] Downstream patch to follow - https://github.com/praiskup/pybugz - ---- - bin/bugz | 19 ++--- - bugz/argparsers.py | 10 ++- - bugz/cli.py | 209 ++++++++++++++++++++++++++++------------------ - bugz/configfile.py | 217 +++++++++++++++++++++++++++++++++--------------- - bugz/errhandling.py | 6 ++ - bugz/log.py | 72 ++++++++++++++++ - bugzrc.example | 61 -------------- - conf/conf.d/gentoo.conf | 3 + - conf/conf.d/redhat.conf | 3 + - conf/pybugz.conf | 126 ++++++++++++++++++++++++++++ - man/bugz.1 | 18 ++-- - setup.py | 4 + - 12 files changed, 520 insertions(+), 228 deletions(-) - create mode 100644 bugz/errhandling.py - create mode 100644 bugz/log.py - delete mode 100644 bugzrc.example - create mode 100644 conf/conf.d/gentoo.conf - create mode 100644 conf/conf.d/redhat.conf - create mode 100644 conf/pybugz.conf - -diff --git a/bin/bugz b/bin/bugz -index 4e61ddf..6f72fa1 100755 ---- a/bin/bugz -+++ b/bin/bugz -@@ -25,17 +25,16 @@ import sys - import traceback - - from bugz.argparsers import make_parser --from bugz.cli import BugzError, PrettyBugz --from bugz.configfile import get_config -+from bugz.cli import PrettyBugz -+from bugz.errhandling import BugzError -+from bugz.log import * - - def main(): -- parser = make_parser() - - # parse options -+ args = None -+ parser = make_parser() - args = parser.parse_args() -- get_config(args) -- if getattr(args, 'columns') is None: -- setattr(args, 'columns', 0) - - try: - bugz = PrettyBugz(args) -@@ -43,17 +42,17 @@ def main(): - return 0 - - except BugzError, e: -- print ' ! Error: %s' % e -+ log_error(e) - return 1 - - except TypeError, e: -- print ' ! Error: Incorrect number of arguments supplied' -- print -+ # where this comes from? -+ log_error('Incorrect number of arguments supplied') - traceback.print_exc() - return 1 - - except RuntimeError, e: -- print ' ! Error: %s' % e -+ log_error(e) - return 1 - - except KeyboardInterrupt: -diff --git a/bugz/argparsers.py b/bugz/argparsers.py -index d14dd84..4cf3936 100644 ---- a/bugz/argparsers.py -+++ b/bugz/argparsers.py -@@ -258,11 +258,12 @@ def make_parser(): - parser = argparse.ArgumentParser( - epilog = 'use -h after a sub-command for sub-command specific help') - parser.add_argument('--config-file', -+ default = None, - help = 'read an alternate configuration file') - parser.add_argument('--connection', - help = 'use [connection] section of your configuration file') - parser.add_argument('-b', '--base', -- default = 'https://bugs.gentoo.org/xmlrpc.cgi', -+ default = None, - help = 'base URL of Bugzilla') - parser.add_argument('-u', '--user', - help = 'username for commands requiring authentication') -@@ -272,10 +273,15 @@ def make_parser(): - help = 'password command to evaluate for commands requiring authentication') - parser.add_argument('-q', '--quiet', - action='store_true', -+ default=None, - help = 'quiet mode') -+ parser.add_argument('-d', '--debug', -+ type=int, -+ default=None, -+ help = 'debug level (from 0 to 3)') - parser.add_argument('--columns', - type = int, -- help = 'maximum number of columns output should use') -+ help = 'maximum number of columns output should use (0 = unlimited)') - parser.add_argument('--encoding', - help = 'output encoding (default: utf-8).') - parser.add_argument('--skip-auth', -diff --git a/bugz/cli.py b/bugz/cli.py -index 62ba540..7112387 100644 ---- a/bugz/cli.py -+++ b/bugz/cli.py -@@ -1,5 +1,3 @@ --#!/usr/bin/env python -- - import commands - import getpass - from cookielib import CookieJar, LWPCookieJar -@@ -12,6 +10,11 @@ import sys - import tempfile - import textwrap - import xmlrpclib -+import pdb -+ -+from bugz.configfile import discover_configs -+from bugz.log import * -+from bugz.errhandling import BugzError - - try: - import readline -@@ -28,8 +31,8 @@ BUGZ: Any line beginning with 'BUGZ:' will be ignored. - BUGZ: --------------------------------------------------- - """ - --DEFAULT_COOKIE_FILE = '.bugz_cookie' - DEFAULT_NUM_COLS = 80 -+DEFAULT_CONFIG_FILE = '/etc/pybugz/pybugz.conf' - - # - # Auxiliary functions -@@ -119,23 +122,63 @@ def block_edit(comment, comment_from = ''): - else: - return '' - --# --# Bugz specific exceptions --# -- --class BugzError(Exception): -- pass -- - class PrettyBugz: -+ enc = "utf-8" -+ columns = 0 -+ quiet = None -+ skip_auth = None -+ -+ # TODO: -+ # * make this class more library-like (allow user to script on the python -+ # level using this PrettyBugz class) -+ # * get the "__init__" phase into main() and change parameters to accept -+ # only 'settings' structure - def __init__(self, args): -- self.quiet = args.quiet -- self.columns = args.columns or terminal_width() -- self.user = args.user -- self.password = args.password -- self.passwordcmd = args.passwordcmd -- self.skip_auth = args.skip_auth -- -- cookie_file = os.path.join(os.environ['HOME'], DEFAULT_COOKIE_FILE) -+ -+ sys_config = DEFAULT_CONFIG_FILE -+ home_config = getattr(args, 'config_file') -+ setDebugLvl(getattr(args, 'debug')) -+ settings = discover_configs(sys_config, home_config) -+ -+ # use the default connection name -+ conn_name = settings['default'] -+ -+ # check for redefinition by --connection -+ opt_conn = getattr(args, 'connection') -+ if opt_conn != None: -+ conn_name = opt_conn -+ -+ if not conn_name in settings['connections']: -+ raise BugzError("can't find connection '{0}'".format(conn_name)) -+ -+ # get proper 'Connection' instance -+ connection = settings['connections'][conn_name] -+ -+ def fix_con(con, name,opt): -+ if opt != None: -+ setattr(con, name, opt) -+ con.option_change = True -+ -+ fix_con(connection, "base", args.base) -+ fix_con(connection, "quiet", args.quiet) -+ fix_con(connection, "columns", args.columns) -+ connection.columns = int(connection.columns) or terminal_width() -+ fix_con(connection, "user", args.user) -+ fix_con(connection, "password", args.password) -+ fix_con(connection, "password_cmd", args.passwordcmd) -+ fix_con(connection, "skip_auth", args.skip_auth) -+ fix_con(connection, "encoding", args.encoding) -+ -+ # now must the "connection" be complete -+ -+ # propagate layout settings to 'self' -+ self.enc = connection.encoding -+ self.skip_auth = connection.skip_auth -+ self.columns = connection.columns -+ -+ setQuiet(connection.quiet) -+ -+ cookie_file = os.path.expanduser(connection.cookie_file) - self.cookiejar = LWPCookieJar(cookie_file) - - try: -@@ -143,9 +186,7 @@ class PrettyBugz: - except IOError: - pass - -- if getattr(args, 'encoding'): -- self.enc = args.encoding -- else: -+ if not self.enc: - try: - self.enc = locale.getdefaultlocale()[1] - except: -@@ -153,19 +194,9 @@ class PrettyBugz: - if not self.enc: - self.enc = 'utf-8' - -- self.log("Using %s " % args.base) -- self.bz = BugzillaProxy(args.base, cookiejar=self.cookiejar) -- -- def log(self, status_msg, newline = True): -- if not self.quiet: -- if newline: -- print ' * %s' % status_msg -- else: -- print ' * %s' % status_msg, -- -- def warn(self, warn_msg): -- if not self.quiet: -- print ' ! Warning: %s' % warn_msg -+ self.bz = BugzillaProxy(connection.base, cookiejar=self.cookiejar) -+ connection.dump() -+ self.connection = connection - - def get_input(self, prompt): - return raw_input(prompt) -@@ -186,27 +217,29 @@ class PrettyBugz: - """Authenticate a session. - """ - # prompt for username if we were not supplied with it -- if not self.user: -- self.log('No username given.') -- self.user = self.get_input('Username: ') -+ if not self.connection.user: -+ log_info('No username given.') -+ self.connection.user = self.get_input('Username: ') - - # prompt for password if we were not supplied with it -- if not self.password: -- if not self.passwordcmd: -- self.log('No password given.') -- self.password = getpass.getpass() -+ if not self.connection.password: -+ if not self.connection.password_cmd: -+ log_info('No password given.') -+ self.connection.password = getpass.getpass() - else: -- process = subprocess.Popen(self.passwordcmd.split(), shell=False, -- stdout=subprocess.PIPE) -+ cmd = self.connection.password_cmd.split() -+ stdout = stdout=subprocess.PIPE -+ process = subprocess.Popen(cmd, shell=False, stdout=stdout) - self.password, _ = process.communicate() -+ self.connection.password, _ = process.communicate() - - # perform login - params = {} -- params['login'] = self.user -- params['password'] = self.password -+ params['login'] = self.connection.user -+ params['password'] = self.connection.password - if args is not None: - params['remember'] = True -- self.log('Logging in') -+ log_info('Logging in') - try: - self.bz.User.login(params) - except xmlrpclib.Fault as fault: -@@ -217,7 +250,7 @@ class PrettyBugz: - os.chmod(self.cookiejar.filename, 0600) - - def logout(self, args): -- self.log('logging out') -+ log_info('logging out') - try: - self.bz.User.logout() - except xmlrpclib.Fault as fault: -@@ -251,29 +284,39 @@ class PrettyBugz: - else: - log_msg = 'Searching for bugs ' - -- if search_opts: -- self.log(log_msg + 'with the following options:') -- for opt, val in search_opts: -- self.log(' %-20s = %s' % (opt, val)) -- else: -- self.log(log_msg) -- - if not 'status' in params.keys(): -- params['status'] = ['CONFIRMED', 'IN_PROGRESS', 'UNCONFIRMED'] -- elif 'ALL' in params['status']: -+ if self.connection.query_statuses: -+ params['status'] = self.connection.query_statuses -+ else: -+ # this seems to be most portable among bugzillas as each -+ # bugzilla may have its own set of statuses. -+ params['status'] = ['ALL'] -+ -+ if 'ALL' in params['status']: - del params['status'] - -+ if len(params): -+ log_info(log_msg + 'with the following options:') -+ for opt, val in params.items(): -+ log_info(' %-20s = %s' % (opt, str(val))) -+ else: -+ log_info(log_msg) -+ - result = self.bzcall(self.bz.Bug.search, params)['bugs'] - - if not len(result): -- self.log('No bugs found.') -+ log_info('No bugs found.') - else: - self.listbugs(result, args.show_status) - - def get(self, args): - """ Fetch bug details given the bug id """ -- self.log('Getting bug %s ..' % args.bugid) -- result = self.bzcall(self.bz.Bug.get, {'ids':[args.bugid]}) -+ log_info('Getting bug %s ..' % args.bugid) -+ try: -+ result = self.bzcall(self.bz.Bug.get, {'ids':[args.bugid]}) -+ except xmlrpclib.Fault as fault: -+ raise BugzError("Can't get bug #" + str(args.bugid) + ": " \ -+ + fault.faultString) - - for bug in result['bugs']: - self.showbuginfo(bug, args.attachments, args.comments) -@@ -293,7 +336,7 @@ class PrettyBugz: - (args.description_from, e)) - - if not args.batch: -- self.log('Press Ctrl+C at any time to abort.') -+ log_info('Press Ctrl+C at any time to abort.') - - # - # Check all bug fields. -@@ -306,14 +349,14 @@ class PrettyBugz: - while not args.product or len(args.product) < 1: - args.product = self.get_input('Enter product: ') - else: -- self.log('Enter product: %s' % args.product) -+ log_info('Enter product: %s' % args.product) - - # check for component - if not args.component: - while not args.component or len(args.component) < 1: - args.component = self.get_input('Enter component: ') - else: -- self.log('Enter component: %s' % args.component) -+ log_info('Enter component: %s' % args.component) - - # check for version - # FIXME: This default behaviour is not too nice. -@@ -324,14 +367,14 @@ class PrettyBugz: - else: - args.version = 'unspecified' - else: -- self.log('Enter version: %s' % args.version) -+ log_info('Enter version: %s' % args.version) - - # check for title - if not args.summary: - while not args.summary or len(args.summary) < 1: - args.summary = self.get_input('Enter title: ') - else: -- self.log('Enter title: %s' % args.summary) -+ log_info('Enter title: %s' % args.summary) - - # check for description - if not args.description: -@@ -339,7 +382,7 @@ class PrettyBugz: - if len(line): - args.description = line - else: -- self.log('Enter bug description: %s' % args.description) -+ log_info('Enter bug description: %s' % args.description) - - # check for operating system - if not args.op_sys: -@@ -348,7 +391,7 @@ class PrettyBugz: - if len(line): - args.op_sys = line - else: -- self.log('Enter operating system: %s' % args.op_sys) -+ log_info('Enter operating system: %s' % args.op_sys) - - # check for platform - if not args.platform: -@@ -357,7 +400,7 @@ class PrettyBugz: - if len(line): - args.platform = line - else: -- self.log('Enter hardware platform: %s' % args.platform) -+ log_info('Enter hardware platform: %s' % args.platform) - - # check for default priority - if args.priority is None: -@@ -366,7 +409,7 @@ class PrettyBugz: - if len(line): - args.priority = line - else: -- self.log('Enter priority (optional): %s' % args.priority) -+ log_info('Enter priority (optional): %s' % args.priority) - - # check for default severity - if args.severity is None: -@@ -375,7 +418,7 @@ class PrettyBugz: - if len(line): - args.severity = line - else: -- self.log('Enter severity (optional): %s' % args.severity) -+ log_info('Enter severity (optional): %s' % args.severity) - - # check for default alias - if args.alias is None: -@@ -384,7 +427,7 @@ class PrettyBugz: - if len(line): - args.alias = line - else: -- self.log('Enter alias (optional): %s' % args.alias) -+ log_info('Enter alias (optional): %s' % args.alias) - - # check for default assignee - if args.assigned_to is None: -@@ -393,7 +436,7 @@ class PrettyBugz: - if len(line): - args.assigned_to = line - else: -- self.log('Enter assignee (optional): %s' % args.assigned_to) -+ log_info('Enter assignee (optional): %s' % args.assigned_to) - - # check for CC list - if args.cc is None: -@@ -402,7 +445,7 @@ class PrettyBugz: - if len(line): - args.cc = line.split(', ') - else: -- self.log('Enter a CC list (optional): %s' % args.cc) -+ log_info('Enter a CC list (optional): %s' % args.cc) - - # check for URL - if args.url is None: -@@ -422,7 +465,7 @@ class PrettyBugz: - if args.append_command is None: - args.append_command = self.get_input('Append the output of the following command (leave blank for none): ') - else: -- self.log('Append command (optional): %s' % args.append_command) -+ log_info('Append command (optional): %s' % args.append_command) - - # raise an exception if mandatory fields are not specified. - if args.product is None: -@@ -470,7 +513,7 @@ class PrettyBugz: - if len(confirm) < 1: - confirm = args.default_confirm - if confirm[0] not in ('y', 'Y'): -- self.log('Submission aborted') -+ log_info('Submission aborted') - return - - params={} -@@ -498,7 +541,7 @@ class PrettyBugz: - params['url'] = args.url - - result = self.bzcall(self.bz.Bug.create, params) -- self.log('Bug %d submitted' % result['id']) -+ log_info('Bug %d submitted' % result['id']) - - def modify(self, args): - """Modify an existing bug (eg. adding a comment or changing resolution.)""" -@@ -604,16 +647,16 @@ class PrettyBugz: - for bug in result['bugs']: - changes = bug['changes'] - if not len(changes): -- self.log('Added comment to bug %s' % bug['id']) -+ log_info('Added comment to bug %s' % bug['id']) - else: -- self.log('Modified the following fields in bug %s' % bug['id']) -+ log_info('Modified the following fields in bug %s' % bug['id']) - for key in changes.keys(): -- self.log('%-12s: removed %s' %(key, changes[key]['removed'])) -- self.log('%-12s: added %s' %(key, changes[key]['added'])) -+ log_info('%-12s: removed %s' %(key, changes[key]['removed'])) -+ log_info('%-12s: added %s' %(key, changes[key]['added'])) - - def attachment(self, args): - """ Download or view an attachment given the id.""" -- self.log('Getting attachment %s' % args.attachid) -+ log_info('Getting attachment %s' % args.attachid) - - params = {} - params['attachment_ids'] = [args.attachid] -@@ -621,7 +664,7 @@ class PrettyBugz: - result = result['attachments'][args.attachid] - - action = {True:'Viewing', False:'Saving'} -- self.log('%s attachment: "%s"' % -+ log_info('%s attachment: "%s"' % - (action[args.view], result['file_name'])) - safe_filename = os.path.basename(re.sub(r'\.\.', '', - result['file_name'])) -@@ -671,7 +714,7 @@ class PrettyBugz: - params['comment'] = comment - params['is_patch'] = is_patch - result = self.bzcall(self.bz.Bug.add_attachment, params) -- self.log("'%s' has been attached to bug %s" % (filename, bugid)) -+ log_info("'%s' has been attached to bug %s" % (filename, bugid)) - - def listbugs(self, buglist, show_status=False): - for bug in buglist: -@@ -690,7 +733,7 @@ class PrettyBugz: - except UnicodeDecodeError: - print line[:self.columns] - -- self.log("%i bug(s) found." % len(buglist)) -+ log_info("%i bug(s) found." % len(buglist)) - - def showbuginfo(self, bug, show_attachments, show_comments): - FIELDS = ( -diff --git a/bugz/configfile.py b/bugz/configfile.py -index a900245..43a502c 100644 ---- a/bugz/configfile.py -+++ b/bugz/configfile.py -@@ -1,70 +1,155 @@ - import ConfigParser --import os -+import os, glob - import sys -+import pdb - --DEFAULT_CONFIG_FILE = '~/.bugzrc' -- --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, e: -- print " ! Error: option "+option+" is not in the right format: "+str(e) -- sys.exit(1) -- --def fill_config_option(args, parser, get, section, option): -- value = config_option(parser, get, section, option) -- if value is not None: -- setattr(args, option, value) -- --def fill_config(args, parser, section): -- fill_config_option(args, parser, parser.get, section, 'base') -- fill_config_option(args, parser, parser.get, section, 'user') -- fill_config_option(args, parser, parser.get, section, 'password') -- fill_config_option(args, parser, parser.get, section, 'passwordcmd') -- fill_config_option(args, parser, parser.getint, section, 'columns') -- fill_config_option(args, parser, parser.get, section, 'encoding') -- fill_config_option(args, parser, parser.getboolean, section, 'quiet') -- --def get_config(args): -- config_file = getattr(args, 'config_file') -- if config_file is None: -- config_file = DEFAULT_CONFIG_FILE -- 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 -- -- # try to parse config file -+from bugz.errhandling import BugzError -+from bugz.log import * -+ -+class Connection: -+ name = "default" -+ base = 'https://bugs.gentoo.org/xmlrpc.cgi' -+ columns = 0 -+ user = None -+ password = None -+ password_cmd = None -+ dbglvl = 0 -+ quiet = None -+ skip_auth = None -+ encoding = "utf-8" -+ cookie_file = "~/.bugz_cookie" -+ option_change = False -+ query_statuses = [] -+ -+ def dump(self): -+ log_info("Using [{0}] ({1})".format(self.name, self.base)) -+ log_debug("User: '{0}'".format(self.user), 3) -+ # loglvl == 4, only for developers (&& only by hardcoding) -+ log_debug("Pass: '{0}'".format(self.password), 10) -+ log_debug("Columns: {0}".format(self.columns), 3) -+ -+def handle_default(settings, newDef): -+ oldDef = str(settings['default']) -+ if oldDef != newDef: -+ log_debug("redefining default connection from '{0}' to '{1}'". \ -+ format(oldDef, newDef), 2) -+ settings['default'] = newDef -+ -+def handle_settings(settings, context, stack, cp, sec_name): -+ log_debug("contains SETTINGS section named [{0}]".format(sec_name), 3) -+ -+ if cp.has_option(sec_name, 'homeconf'): -+ settings['homeconf'] = cp.get(sec_name, 'homeconf') -+ -+ if cp.has_option(sec_name, 'default'): -+ handle_default(settings, cp.get(sec_name, 'default')) -+ -+ # handle 'confdir' ~> explore and push target files into the stack -+ if cp.has_option(sec_name, 'confdir'): -+ confdir = cp.get(sec_name, 'confdir') -+ full_confdir = os.path.expanduser(confdir) -+ wildcard = os.path.join(full_confdir, '*.conf') -+ log_debug("adding wildcard " + wildcard, 3) -+ for cnffile in glob.glob(wildcard): -+ log_debug(" ++ " + cnffile, 3) -+ if cnffile in context['included']: -+ log_debug("skipping (already included)") -+ break -+ stack.append(cnffile) -+ -+def handle_connection(settings, context, stack, parser, name): -+ log_debug("reading connection '{0}'".format(name), 2) -+ connection = None -+ -+ if name in settings['connections']: -+ log_debug("redefining connection '{0}'".format(name), 2) -+ connection = settings['connections'][name] -+ else: -+ connection = Connection() -+ connection.name = name -+ -+ def fill(conn, id): -+ if parser.has_option(name, id): -+ val = parser.get(name, id) -+ setattr(conn, id, val) -+ log_debug("has {0} - {1}".format(id, val), 3) -+ -+ fill(connection, "base") -+ fill(connection, "user") -+ fill(connection, "password") -+ fill(connection, "encoding") -+ fill(connection, "columns") -+ fill(connection, "quiet") -+ -+ if parser.has_option(name, 'query_statuses'): -+ line = parser.get(name, 'query_statuses') -+ lines = line.split() -+ connection.query_statuses = lines -+ -+ settings['connections'][name] = connection -+ -+def parse_file(settings, context, stack): -+ file_name = stack.pop() -+ full_name = os.path.expanduser(file_name) -+ -+ context['included'][full_name] = None -+ -+ log_debug("parsing '" + file_name + "'", 1) -+ -+ cp = ConfigParser.ConfigParser() -+ parsed = None - try: -- parser.readfp(file) -- sections = parser.sections() -- except ConfigParser.ParsingError, e: -- print " ! Error: Can't parse user configuration file: "+str(e) -- sys.exit(1) -- -- # parse the default section first -- if "default" in sections: -- fill_config(args, parser, "default") -- if section is None: -- section = config_option(parser, parser.get, "default", "connection") -- -- # parse a specific section -- if section in sections: -- fill_config(args, parser, section) -- elif section is not None: -- print " ! Error: Can't find section ["+section+"] in configuration file" -- sys.exit(1) -+ parsed = cp.read(full_name) -+ if parsed != [ full_name ]: -+ raise BugzError("problem with file '" + file_name + "'") -+ except ConfigParser.Error, err: -+ msg = err.message -+ raise BugzError("can't parse: '" + file_name + "'\n" + msg ) -+ -+ # successfully parsed file -+ -+ for sec in cp.sections(): -+ sectype = "connection" -+ -+ if cp.has_option(sec, 'type'): -+ sectype = cp.get(sec, 'type') -+ -+ if sectype == "settings": -+ handle_settings(settings, context, stack, cp, sec) -+ -+ if sectype == "connection": -+ handle_connection(settings, context, stack, cp, sec) -+ -+def discover_configs(file, homeConf=None): -+ settings = { -+ # where to look for user's configuration -+ 'homeconf' : '~/.bugzrc', -+ # list of objects of Connection -+ 'connections' : {}, -+ # the default Connection name -+ 'default' : None, -+ } -+ context = { -+ 'where' : 'sys', -+ 'homeparsed' : False, -+ 'included' : {}, -+ } -+ stack = [ file ] -+ -+ # parse sys configs -+ while len(stack) > 0: -+ parse_file(settings, context, stack) -+ -+ if not homeConf: -+ # the command-line option must win -+ homeConf = settings['homeconf'] -+ -+ if not os.path.isfile(os.path.expanduser(homeConf)): -+ return settings -+ -+ # parse home configs -+ stack = [ homeConf ] -+ while len(stack) > 0: -+ parse_file(settings, context, stack) -+ -+ return settings -diff --git a/bugz/errhandling.py b/bugz/errhandling.py -new file mode 100644 -index 0000000..d3fec06 ---- /dev/null -+++ b/bugz/errhandling.py -@@ -0,0 +1,6 @@ -+# -+# Bugz specific exceptions -+# -+ -+class BugzError(Exception): -+ pass -diff --git a/bugz/log.py b/bugz/log.py -new file mode 100644 -index 0000000..df4bb9a ---- /dev/null -+++ b/bugz/log.py -@@ -0,0 +1,72 @@ -+# TODO: use the python's 'logging' feature? -+ -+dbglvl = 0 -+quiet = False -+ -+LogSettins = { -+ 'W' : { -+ 'symb' : '!', -+ 'word' : 'Warn', -+ }, -+ 'E' : { -+ 'symb' : '#', -+ 'word' : 'Error', -+ }, -+ 'D' : { -+ 'symb' : '~', -+ 'word' : 'Dbg', -+ }, -+ 'I' : { -+ 'symb' : '*', -+ 'word' : 'Info', -+ }, -+ '!' : { -+ 'symb' : '!', -+ 'word' : 'UNKNWN', -+ }, -+} -+ -+def setQuiet(newQuiet): -+ global quiet -+ quiet = newQuiet -+ -+def setDebugLvl(newlvl): -+ global dbglvl -+ if not newlvl: -+ return -+ if newlvl > 3: -+ log_warn("bad debug level '{0}', using '3'".format(str(newlvl))) -+ dbglvl = 3 -+ else: -+ dbglvl = newlvl -+ -+def formatOut(msg, id='!'): -+ lines = str(msg).split('\n') -+ start = True -+ symb=LogSettins[id]['symb'] -+ word=LogSettins[id]['word'] + ":" -+ -+ for line in lines: -+ print ' ' + symb + ' ' + line -+ -+def log_error(string): -+ formatOut(string, 'E') -+ return -+ -+def log_warn(string): -+ formatOut(string, 'W') -+ return -+ -+def log_info(string): -+ global quiet -+ global dbglvl -+ # debug implies info -+ if not quiet or dbglvl: -+ formatOut(string, 'I') -+ return -+ -+def log_debug(string, verboseness=1): -+ global dbglvl -+ if dbglvl >= verboseness: -+ formatOut(string, 'D') -+ return -diff --git a/bugzrc.example b/bugzrc.example -deleted file mode 100644 -index f516bf0..0000000 ---- a/bugzrc.example -+++ /dev/null -@@ -1,61 +0,0 @@ --# --# bugzrc.example - an example configuration file for pybugz --# --# This file consists of sections which define parameters for each --# bugzilla you plan to use. --# --# Each section begins with a name in square brackets. This is also the --# name that should be used with the --connection parameter to the bugz --# command. --# --# Each section of this file consists of lines in the form: --# key: value --# as listed below. --# --# [sectionname] --# --# The base url of the bugzilla you wish to use. --# This must point to the xmlrpc.cgi script on the bugzilla installation. --# --# base: http://my.project.com/bugzilla/xmlrpc.cgi --# --# It is also possible to encode a username and password into this URL --# for basic http authentication as follows: --# --# base: http://myhttpname:myhttppasswd@my.project.com/bugzilla/xmlrpc.cgi --# --# Next are your username and password for this bugzilla. If you do not --# provide these, you will be prompted for them. --# --# user: myname@my.project.com --# password: secret2 --# --# As an alternative to keeping your password in this file you can provide a --# password command. It is evaluated and pybugz expects this command to output --# the password to standard out. E.g.: --# --# passwordcmd: gpg2 --decrypt /myhome/.my-encrypted-password.gpg --# --# The number of columns your terminal can display. --# Most of the time you should not have to set this. --# --# columns: 80 --# --# Set the output encoding for pybugz. --# --# encoding: utf-8 --# --# Run in quiet mode. --# --# quiet: True --# --# The special section named 'default' may also be used. Other sections will --# override any values specified here. The optional special key 'connection' is --# used to name the default connection, to use when no --connection parameter is --# specified to the bugz command. --# --# [default] --# connection: sectionname --# --# All parameters listed above can be used in the default section if you --# only use one bugzilla installation. -diff --git a/conf/conf.d/gentoo.conf b/conf/conf.d/gentoo.conf -new file mode 100644 -index 0000000..42fae46 ---- /dev/null -+++ b/conf/conf.d/gentoo.conf -@@ -0,0 +1,3 @@ -+[Gentoo] -+base = https://bugs.gentoo.org/xmlrpc.cgi -+query_statuses = CONFIRMED IN_PROGRESS UNCONFIRMED -diff --git a/conf/conf.d/redhat.conf b/conf/conf.d/redhat.conf -new file mode 100644 -index 0000000..0f50fb7 ---- /dev/null -+++ b/conf/conf.d/redhat.conf -@@ -0,0 +1,3 @@ -+[RedHat] -+base = https://bugzilla.redhat.com/xmlrpc.cgi -+query_statuses = NEW ASSIGNED MODIFIED ON_DEV POST -diff --git a/conf/pybugz.conf b/conf/pybugz.conf -new file mode 100644 -index 0000000..3a486b4 ---- /dev/null -+++ b/conf/pybugz.conf -@@ -0,0 +1,126 @@ -+# =========================================================================== -+# The "root" configuration file of PyBugz bugzilla interface. -+# =========================================================================== -+# -+# Overview -+# ======== -+# PyBugz is configured by hierarchy of *.conf files. All the configuration -+# job starts from this file. User specific configuration is by default in -+# file ~/.bugzrc. This is specially usefull to allow user to redefine some -+# system configuration (an example could be adding user's credentials -+# for specific connection — see the following text). -+# -+# Syntax -+# ====== -+# The syntax is similar to Windows INI files. For more info, see the -+# documentation for python's ConfigParser library class — this class is used -+# for parsing configuration files here. Quickly, each file consists of -+# sections (section's name in brackets, e.g. [section]). Each section -+# consists of set of configuration options separated by newlines. -+# -+# [sectionName] -+# optionA = value A -+# optionB = this is value of B # comments are possible -+# -+# Section types -+# ============= -+# Currently, there are implemented two types of sections in PyBugz. Those -+# are 'connection' (default type of section) and 'settings'. -+# Type 'settings' has purpose for setting up some global feature of PyBugz. -+# The type 'connection', however, describes attributes of particular -+# connection to some concrete instance of bugzilla. -+# -+# +------------------------+ -+# | 1. "type = connection" | -+# +------------------------+ -+# -+# Important property of this type is its section identifier (name of -+# section). By passing this name as an argument of --connection option is -+# PyBugz's user able to select which connection will be used. -+# -+# Accepted options / semantics -+# ---------------------------- -+# -+# Note that you may specify each section of type 'connection' multiple -+# times (using the same ID). All settings are combined among same named -+# sections with one rule: the last one wins. This is important when you -+# want to specify some defaults system wide and let particular user -+# redefine (or correct) concrete connection — user's configuration is -+# loaded _later_ than system's. -+# -+# * type -+# May be set optionally to 'connection', but it is the default in each -+# section. -+# -+# * base -+# Sets up the xmlrpc entrance into bugzilla, for example: -+# https://bugzilla.redhat.com/xmlrpc.cgi -+# -+# * user & password -+# These two options let you specify your login information to bugzilla -+# instance (you must be registered there of course). It is also -+# possible to encode a user (usually user's email) and password into -+# base: -+# http://myhttpname:myhttppasswd@my.project.com/bugzilla/xmlrpc.cgi -+# Note that if you don't specify your login information, you will be -+# prompted for them. -+# -+# * passwordcmd -+# As an alternative to keeping your password in this file you can -+# provide a password command. It is evaluated and pybugz expects this -+# command to output the password to standard out. E.g.: -+# -+# passwordcmd = gpg2 --decrypt /myhome/.my-encrypted-password.gpg -+# -+# * columns -+# The number of columns your terminal can display (or you want to be -+# displayed) during using of this connection. Expects integer number. -+# -+# * query_statuses -+# List of bug-statuses to be displayed by default (when *not* redefined by -+# --status option). Accepts list of properly spelled statuses separated -+# by single space, e.g.: query_statuses = ASSIGNED CLOSED -+# -+# * encoding -+# Set the output encoding for PyBugz. Default is utf-8. -+# -+# * quiet -+# Run this connection in quiet mode when: quiet = True. -+# -+# * inherit (to be done in future) -+# -+# +----------------------+ -+# | 2. "type = settings" | -+# +----------------------+ -+# -+# Again, this lets you define PyBugz "global" settings (among all -+# connections). The name of section is not important here. Same as -+# 'connection' type, even this type of section you may define multiple -+# times — options are combined then (and the latest wins). -+# -+# There are several accepted options (now): -+# -+# * type -+# Here the type must be set to 'settings'. This is requirement for pybugz -+# to interpret this section as you want. -+# -+# * default -+# Lets you define the default connection (when the --connection option is -+# not passed). -+# -+# * homeconf -+# Let's you define where to look for user's configuration file. This is -+# by default ~/.bugzrc file. Note that this option makes sense only for -+# system-wide configuration file. -+# -+# * confdir -+# This option lets you define the configuration directory. This directory -+# is searched for *.conf files, and these files (if any) are parsed -+# immediately after specifying configuration file. -+ -+[settings] -+ -+type = settings -+homeconf = ~/.bugzrc -+confdir = /etc/pybugz/conf.d/ -+default = Gentoo -diff --git a/man/bugz.1 b/man/bugz.1 -index 628eae9..fbf2e6c 100644 ---- a/man/bugz.1 -+++ b/man/bugz.1 -@@ -1,8 +1,8 @@ - .\" Hey, Emacs! This is an -*- nroff -*- source file. --.\" Copyright (c) 2011 William Hubbs -+.\" Copyright (c) 2011, 2012, 2013 William Hubbs - .\" This is free software; see the GNU General Public Licence version 2 - .\" or later for copying conditions. There is NO warranty. --.TH bugz 1 "17 Feb 2011" "0.9.0" -+.TH bugz 1 "20 Jan 2013" "0.10.2" - .nh - .SH NAME - bugz \(em command line interface to bugzilla -@@ -20,10 +20,10 @@ bugz \(em command line interface to bugzilla - .\" .B \-o value, \-\^\-long=value - .\" Describe the option. - .SH DESCRIPTION --Bugz is a cprogram which gives you access to the features of the -+Bugz is a program which gives you access to the features of the - bugzilla bug tracking system from the command line. - .PP --This man page is a stub; the bugs program has extensive built in help. -+This man page is a stub; the bugz program has extensive built in help. - .B bugz -h - will show the help for the global options and - .B bugz [subcommand] -h -@@ -32,8 +32,14 @@ will show the help for a specific subcommand. - .PP - The home page of this project is http://www.github.com/williamh/pybugz. - Bugs should be reported to the bug tracker there. --.\" .SH SEE ALSO --.\" .PP -+.SH SEE ALSO -+.PP -+For documentation how to configure PyBugz take a look into distributed -+.B pybugz.conf -+file. User specific configuration may be defined in -+.B -+~/.bugzrc -+file. - .SH AUTHOR - .PP - The original author is Alastair Tse . -diff --git a/setup.py b/setup.py -index e9a8a52..15f004c 100644 ---- a/setup.py -+++ b/setup.py -@@ -18,5 +18,9 @@ setup( - platforms = ['any'], - packages = ['bugz'], - scripts = ['bin/bugz'], -+ data_files = [ -+ ('/etc/pybugz', ['conf/pybugz.conf']), -+ ('/etc/pybugz/conf.d', ['conf/conf.d/redhat.conf', 'conf/conf.d/gentoo.conf']), -+ ], - cmdclass = {'build_py': build_py, 'build_scripts': build_scripts}, - ) --- -1.7.11.7 - diff --git a/pybugz-0.10-git89df2-rhel-fedora-cust.patch b/pybugz-0.10-git89df2-rhel-fedora-cust.patch deleted file mode 100644 index 8a05846..0000000 --- a/pybugz-0.10-git89df2-rhel-fedora-cust.patch +++ /dev/null @@ -1,22 +0,0 @@ -From e6ceb40155df600cbc2bba6a593c55fe327a3987 Mon Sep 17 00:00:00 2001 -From: Pavel Raiskup -Date: Sun, 20 Jan 2013 15:20:45 +0100 -Subject: [PATCH] Refine for Fedora purposes - ---- - conf/pybugz.conf | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/conf/pybugz.conf b/conf/pybugz.conf -index 3a486b4..1bd5176 100644 ---- a/conf/pybugz.conf -+++ b/conf/pybugz.conf -@@ -123,4 +123,4 @@ - type = settings - homeconf = ~/.bugzrc - confdir = /etc/pybugz/conf.d/ --default = Gentoo -+default = RedHat --- -1.7.11.7 - diff --git a/pybugz.spec b/pybugz.spec index 9037eb4..58fc588 100644 --- a/pybugz.spec +++ b/pybugz.spec @@ -1,11 +1,11 @@ -%global gitrev 89df2 +%global gitrev 683dd %global posttag git%{gitrev} %global snapshot %{version}-%{posttag} Name: pybugz Summary: Command line interface for Bugzilla written in Python Version: 0.10 -Release: 2.%{posttag}%{?dist} +Release: 3.%{posttag}%{?dist} Group: Applications/Communications License: GPLv2 URL: https://github.com/williamh/pybugz @@ -28,10 +28,9 @@ BuildRequires: bash-completion pkgconfig # script (in dist-git). Source0: %{name}-%{snapshot}.tar.gz -# follow https://github.com/praiskup/pybugz changes (until accepted by upstream) -Patch0: %{name}-%{snapshot}-downstream.patch -# make the installation better satisfy RHEL / Fedora purposes -Patch1: %{name}-%{snapshot}-rhel-fedora-cust.patch +# make the installation better useful for bugzilla.redhat.com users +# ~> downstream +Patch0: %{name}-0.10-git683dd-rh-specific.patch %description Pybugz was conceived as a tool to speed up the work-flow for Gentoo Linux @@ -42,8 +41,7 @@ comfortably from the command line. %prep %setup -q -n %{name}-%{snapshot} -%patch0 -p1 -b .downstream -%patch1 -p1 -b .rhel-fedora-cust +%patch0 -p1 -b .rh-specific %build %{__python} setup.py build @@ -74,10 +72,13 @@ mkdir -p %{buildroot}%{_docdir} %endif %{python_sitelib}/%{name}-*.egg-info %{_mandir}/man1/bugz.1.gz -%config(noreplace) %{_sysconfdir}/pybugz -%doc README LICENSE +%doc README LICENSE bugzrc.example %changelog +* Wed Mar 13 2013 Pavel Raiskup - 0.10-3.git683dd +- remove downstream patches for now - I'll reapply again if it becomes upstream +- package the latest git version + * Thu Feb 14 2013 Fedora Release Engineering - 0.10-2.git89df2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild diff --git a/sources b/sources index 8410195..5d175a3 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -1b92781ac57ad7cd8b967a8a0ced3557 pybugz-0.10-git89df2.tar.gz +8bc25cc5d117e6e6a4801dcf8089c8a6 pybugz-0.10-git683dd.tar.gz