hgext/bugzilla.py
changeset 26587 56b2bcea2529
parent 25660 328739ea70c3
child 28091 2f0384242b35
equal deleted inserted replaced
26586:d51c658d3f04 26587:56b2bcea2529
   277     Changeset commit comment. Bug 1234.
   277     Changeset commit comment. Bug 1234.
   278 '''
   278 '''
   279 
   279 
   280 from mercurial.i18n import _
   280 from mercurial.i18n import _
   281 from mercurial.node import short
   281 from mercurial.node import short
   282 from mercurial import cmdutil, mail, util
   282 from mercurial import cmdutil, mail, util, error
   283 import re, time, urlparse, xmlrpclib
   283 import re, time, urlparse, xmlrpclib
   284 
   284 
   285 # Note for extension authors: ONLY specify testedwith = 'internal' for
   285 # Note for extension authors: ONLY specify testedwith = 'internal' for
   286 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
   286 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
   287 # be specifying the version(s) of Mercurial they are tested with, or
   287 # be specifying the version(s) of Mercurial they are tested with, or
   356     def __init__(self, ui):
   356     def __init__(self, ui):
   357         try:
   357         try:
   358             import MySQLdb as mysql
   358             import MySQLdb as mysql
   359             bzmysql._MySQLdb = mysql
   359             bzmysql._MySQLdb = mysql
   360         except ImportError as err:
   360         except ImportError as err:
   361             raise util.Abort(_('python mysql support not available: %s') % err)
   361             raise error.Abort(_('python mysql support not available: %s') % err)
   362 
   362 
   363         bzaccess.__init__(self, ui)
   363         bzaccess.__init__(self, ui)
   364 
   364 
   365         host = self.ui.config('bugzilla', 'host', 'localhost')
   365         host = self.ui.config('bugzilla', 'host', 'localhost')
   366         user = self.ui.config('bugzilla', 'user', 'bugs')
   366         user = self.ui.config('bugzilla', 'user', 'bugs')
   390     def get_longdesc_id(self):
   390     def get_longdesc_id(self):
   391         '''get identity of longdesc field'''
   391         '''get identity of longdesc field'''
   392         self.run('select fieldid from fielddefs where name = "longdesc"')
   392         self.run('select fieldid from fielddefs where name = "longdesc"')
   393         ids = self.cursor.fetchall()
   393         ids = self.cursor.fetchall()
   394         if len(ids) != 1:
   394         if len(ids) != 1:
   395             raise util.Abort(_('unknown database schema'))
   395             raise error.Abort(_('unknown database schema'))
   396         return ids[0][0]
   396         return ids[0][0]
   397 
   397 
   398     def filter_real_bug_ids(self, bugs):
   398     def filter_real_bug_ids(self, bugs):
   399         '''filter not-existing bugs from set.'''
   399         '''filter not-existing bugs from set.'''
   400         self.run('select bug_id from bugs where bug_id in %s' %
   400         self.run('select bug_id from bugs where bug_id in %s' %
   435             fp = util.popen('(%s) 2>&1' % cmd)
   435             fp = util.popen('(%s) 2>&1' % cmd)
   436             out = fp.read()
   436             out = fp.read()
   437             ret = fp.close()
   437             ret = fp.close()
   438             if ret:
   438             if ret:
   439                 self.ui.warn(out)
   439                 self.ui.warn(out)
   440                 raise util.Abort(_('bugzilla notify command %s') %
   440                 raise error.Abort(_('bugzilla notify command %s') %
   441                                  util.explainexit(ret)[0])
   441                                  util.explainexit(ret)[0])
   442         self.ui.status(_('done\n'))
   442         self.ui.status(_('done\n'))
   443 
   443 
   444     def get_user_id(self, user):
   444     def get_user_id(self, user):
   445         '''look up numeric bugzilla user id.'''
   445         '''look up numeric bugzilla user id.'''
   468             userid = self.get_user_id(user)
   468             userid = self.get_user_id(user)
   469         except KeyError:
   469         except KeyError:
   470             try:
   470             try:
   471                 defaultuser = self.ui.config('bugzilla', 'bzuser')
   471                 defaultuser = self.ui.config('bugzilla', 'bzuser')
   472                 if not defaultuser:
   472                 if not defaultuser:
   473                     raise util.Abort(_('cannot find bugzilla user id for %s') %
   473                     raise error.Abort(_('cannot find bugzilla user id for %s') %
   474                                      user)
   474                                      user)
   475                 userid = self.get_user_id(defaultuser)
   475                 userid = self.get_user_id(defaultuser)
   476                 user = defaultuser
   476                 user = defaultuser
   477             except KeyError:
   477             except KeyError:
   478                 raise util.Abort(_('cannot find bugzilla user id for %s or %s')
   478                 raise error.Abort(_('cannot find bugzilla user id for %s or %s')
   479                                  % (user, defaultuser))
   479                                  % (user, defaultuser))
   480         return (user, userid)
   480         return (user, userid)
   481 
   481 
   482     def updatebug(self, bugid, newstate, text, committer):
   482     def updatebug(self, bugid, newstate, text, committer):
   483         '''update bug state with comment text.
   483         '''update bug state with comment text.
   515     def get_longdesc_id(self):
   515     def get_longdesc_id(self):
   516         '''get identity of longdesc field'''
   516         '''get identity of longdesc field'''
   517         self.run('select id from fielddefs where name = "longdesc"')
   517         self.run('select id from fielddefs where name = "longdesc"')
   518         ids = self.cursor.fetchall()
   518         ids = self.cursor.fetchall()
   519         if len(ids) != 1:
   519         if len(ids) != 1:
   520             raise util.Abort(_('unknown database schema'))
   520             raise error.Abort(_('unknown database schema'))
   521         return ids[0][0]
   521         return ids[0][0]
   522 
   522 
   523 # Bugzilla via XMLRPC interface.
   523 # Bugzilla via XMLRPC interface.
   524 
   524 
   525 class cookietransportrequest(object):
   525 class cookietransportrequest(object):
   703     def __init__(self, ui):
   703     def __init__(self, ui):
   704         bzxmlrpc.__init__(self, ui)
   704         bzxmlrpc.__init__(self, ui)
   705 
   705 
   706         self.bzemail = self.ui.config('bugzilla', 'bzemail')
   706         self.bzemail = self.ui.config('bugzilla', 'bzemail')
   707         if not self.bzemail:
   707         if not self.bzemail:
   708             raise util.Abort(_("configuration 'bzemail' missing"))
   708             raise error.Abort(_("configuration 'bzemail' missing"))
   709         mail.validateconfig(self.ui)
   709         mail.validateconfig(self.ui)
   710 
   710 
   711     def makecommandline(self, fieldname, value):
   711     def makecommandline(self, fieldname, value):
   712         if self.bzvermajor >= 4:
   712         if self.bzvermajor >= 4:
   713             return "@%s %s" % (fieldname, str(value))
   713             return "@%s %s" % (fieldname, str(value))
   733         if not matches['users']:
   733         if not matches['users']:
   734             user = self.ui.config('bugzilla', 'user', 'bugs')
   734             user = self.ui.config('bugzilla', 'user', 'bugs')
   735             matches = self.bzproxy.User.get({'match': [user],
   735             matches = self.bzproxy.User.get({'match': [user],
   736                                              'token': self.bztoken})
   736                                              'token': self.bztoken})
   737             if not matches['users']:
   737             if not matches['users']:
   738                 raise util.Abort(_("default bugzilla user %s email not found") %
   738                 raise error.Abort(_("default bugzilla user %s email not found")
   739                                  user)
   739                                   % user)
   740         user = matches['users'][0]['email']
   740         user = matches['users'][0]['email']
   741         commands.append(self.makecommandline("id", bugid))
   741         commands.append(self.makecommandline("id", bugid))
   742 
   742 
   743         text = "\n".join(commands) + "\n\n" + comment
   743         text = "\n".join(commands) + "\n\n" + comment
   744 
   744 
   787 
   787 
   788         bzversion = self.ui.config('bugzilla', 'version')
   788         bzversion = self.ui.config('bugzilla', 'version')
   789         try:
   789         try:
   790             bzclass = bugzilla._versions[bzversion]
   790             bzclass = bugzilla._versions[bzversion]
   791         except KeyError:
   791         except KeyError:
   792             raise util.Abort(_('bugzilla version %s not supported') %
   792             raise error.Abort(_('bugzilla version %s not supported') %
   793                              bzversion)
   793                              bzversion)
   794         self.bzdriver = bzclass(self.ui)
   794         self.bzdriver = bzclass(self.ui)
   795 
   795 
   796         self.bug_re = re.compile(
   796         self.bug_re = re.compile(
   797             self.ui.config('bugzilla', 'regexp',
   797             self.ui.config('bugzilla', 'regexp',
   898 def hook(ui, repo, hooktype, node=None, **kwargs):
   898 def hook(ui, repo, hooktype, node=None, **kwargs):
   899     '''add comment to bugzilla for each changeset that refers to a
   899     '''add comment to bugzilla for each changeset that refers to a
   900     bugzilla bug id. only add a comment once per bug, so same change
   900     bugzilla bug id. only add a comment once per bug, so same change
   901     seen multiple times does not fill bug with duplicate data.'''
   901     seen multiple times does not fill bug with duplicate data.'''
   902     if node is None:
   902     if node is None:
   903         raise util.Abort(_('hook type %s does not pass a changeset id') %
   903         raise error.Abort(_('hook type %s does not pass a changeset id') %
   904                          hooktype)
   904                          hooktype)
   905     try:
   905     try:
   906         bz = bugzilla(ui, repo)
   906         bz = bugzilla(ui, repo)
   907         ctx = repo[node]
   907         ctx = repo[node]
   908         bugs = bz.find_bugs(ctx)
   908         bugs = bz.find_bugs(ctx)
   909         if bugs:
   909         if bugs:
   910             for bug in bugs:
   910             for bug in bugs:
   911                 bz.update(bug, bugs[bug], ctx)
   911                 bz.update(bug, bugs[bug], ctx)
   912             bz.notify(bugs, util.email(ctx.user()))
   912             bz.notify(bugs, util.email(ctx.user()))
   913     except Exception as e:
   913     except Exception as e:
   914         raise util.Abort(_('Bugzilla error: %s') % e)
   914         raise error.Abort(_('Bugzilla error: %s') % e)