hgext/bugzilla.py
changeset 7618 6c89dd0a7797
parent 7504 d8cd79fbed3c
child 7762 fece056bf240
equal deleted inserted replaced
7617:7b554c6ad390 7618:6c89dd0a7797
    27     host       Hostname of the MySQL server holding the Bugzilla database.
    27     host       Hostname of the MySQL server holding the Bugzilla database.
    28     db         Name of the Bugzilla database in MySQL. Default 'bugs'.
    28     db         Name of the Bugzilla database in MySQL. Default 'bugs'.
    29     user       Username to use to access MySQL server. Default 'bugs'.
    29     user       Username to use to access MySQL server. Default 'bugs'.
    30     password   Password to use to access MySQL server.
    30     password   Password to use to access MySQL server.
    31     timeout    Database connection timeout (seconds). Default 5.
    31     timeout    Database connection timeout (seconds). Default 5.
    32     version    Bugzilla version. Specify '3.0' for Bugzilla versions from
    32     version    Bugzilla version. Specify '3.0' for Bugzilla versions 3.0 and
    33                3.0 onwards, and '2.16' for versions prior to 3.0.
    33                later, '2.18' for Bugzilla versions from 2.18 and '2.16' for
       
    34                versions prior to 2.18.
    34     bzuser     Fallback Bugzilla user name to record comments with, if
    35     bzuser     Fallback Bugzilla user name to record comments with, if
    35                changeset committer cannot be found as a Bugzilla user.
    36                changeset committer cannot be found as a Bugzilla user.
       
    37     bzdir      Bugzilla install directory. Used by default notify.
       
    38                Default '/var/www/html/bugzilla'.
    36     notify     The command to run to get Bugzilla to send bug change
    39     notify     The command to run to get Bugzilla to send bug change
    37                notification emails. Substitutes one string parameter,
    40                notification emails. Substitutes from a map with 3 keys,
    38                the bug ID. Default 'cd /var/www/html/bugzilla && '
    41                'bzdir', 'id' (bug id) and 'user' (committer bugzilla email).
    39                                    './processmail %s nobody@nowhere.com'.
    42                Default depends on version; from 2.18 it is
       
    43                "cd %(bzdir)s && perl -T contrib/sendbugmail.pl %(id)s %(user)s".
    40     regexp     Regular expression to match bug IDs in changeset commit message.
    44     regexp     Regular expression to match bug IDs in changeset commit message.
    41                Must contain one "()" group. The default expression matches
    45                Must contain one "()" group. The default expression matches
    42                'Bug 1234', 'Bug no. 1234', 'Bug number 1234',
    46                'Bug 1234', 'Bug no. 1234', 'Bug number 1234',
    43                'Bugs 1234,5678', 'Bug 1234 and 5678' and variations thereof.
    47                'Bugs 1234,5678', 'Bug 1234 and 5678' and variations thereof.
    44                Matching is case insensitive.
    48                Matching is case insensitive.
    86     [bugzilla]
    90     [bugzilla]
    87     host=localhost
    91     host=localhost
    88     password=XYZZY
    92     password=XYZZY
    89     version=3.0
    93     version=3.0
    90     bzuser=unknown@domain.com
    94     bzuser=unknown@domain.com
    91     notify=cd /opt/bugzilla-3.2 && perl -T contrib/sendbugmail.pl %%s bugmail@domain.com
    95     bzdir=/opt/bugzilla-3.2
    92     template=Changeset {node|short} in {root|basename}.\\n{hgweb}/{webroot}/rev/{node|short}\\n\\n{desc}\\n
    96     template=Changeset {node|short} in {root|basename}.\\n{hgweb}/{webroot}/rev/{node|short}\\n\\n{desc}\\n
    93     strip=5
    97     strip=5
    94 
    98 
    95     [web]
    99     [web]
    96     baseurl=http://dev.domain.com/hg
   100     baseurl=http://dev.domain.com/hg
   134         self.conn = MySQLdb.connect(host=host, user=user, passwd=passwd,
   138         self.conn = MySQLdb.connect(host=host, user=user, passwd=passwd,
   135                                     db=db, connect_timeout=timeout)
   139                                     db=db, connect_timeout=timeout)
   136         self.cursor = self.conn.cursor()
   140         self.cursor = self.conn.cursor()
   137         self.longdesc_id = self.get_longdesc_id()
   141         self.longdesc_id = self.get_longdesc_id()
   138         self.user_ids = {}
   142         self.user_ids = {}
       
   143         self.default_notify = "cd %(bzdir)s && ./processmail %(id)s %(user)s"
   139 
   144 
   140     def run(self, *args, **kwargs):
   145     def run(self, *args, **kwargs):
   141         '''run a query.'''
   146         '''run a query.'''
   142         self.ui.note(_('query: %s %s\n') % (args, kwargs))
   147         self.ui.note(_('query: %s %s\n') % (args, kwargs))
   143         try:
   148         try:
   170             self.ui.status(_('bug %d already knows about changeset %s\n') %
   175             self.ui.status(_('bug %d already knows about changeset %s\n') %
   171                            (id, short(node)))
   176                            (id, short(node)))
   172             unknown.pop(id, None)
   177             unknown.pop(id, None)
   173         return util.sort(unknown.keys())
   178         return util.sort(unknown.keys())
   174 
   179 
   175     def notify(self, ids):
   180     def notify(self, ids, committer):
   176         '''tell bugzilla to send mail.'''
   181         '''tell bugzilla to send mail.'''
   177 
   182 
   178         self.ui.status(_('telling bugzilla to send mail:\n'))
   183         self.ui.status(_('telling bugzilla to send mail:\n'))
       
   184         (user, userid) = self.get_bugzilla_user(committer)
   179         for id in ids:
   185         for id in ids:
   180             self.ui.status(_('  bug %s\n') % id)
   186             self.ui.status(_('  bug %s\n') % id)
   181             cmd = self.ui.config('bugzilla', 'notify',
   187             cmdfmt = self.ui.config('bugzilla', 'notify', self.default_notify)
   182                                'cd /var/www/html/bugzilla && '
   188             bzdir = self.ui.config('bugzilla', 'bzdir', '/var/www/html/bugzilla')
   183                                './processmail %s nobody@nowhere.com') % id
   189             try:
       
   190                 # Backwards-compatible with old notify string, which
       
   191                 # took one string. This will throw with a new format
       
   192                 # string.
       
   193                 cmd = cmdfmt % id
       
   194             except TypeError:
       
   195                 cmd = cmdfmt % {'bzdir': bzdir, 'id': id, 'user': user}
       
   196             self.ui.note(_('running notify command %s\n') % cmd)
   184             fp = util.popen('(%s) 2>&1' % cmd)
   197             fp = util.popen('(%s) 2>&1' % cmd)
   185             out = fp.read()
   198             out = fp.read()
   186             ret = fp.close()
   199             ret = fp.close()
   187             if ret:
   200             if ret:
   188                 self.ui.warn(out)
   201                 self.ui.warn(out)
   213         for committer, bzuser in self.ui.configitems('usermap'):
   226         for committer, bzuser in self.ui.configitems('usermap'):
   214             if committer.lower() == user.lower():
   227             if committer.lower() == user.lower():
   215                 return bzuser
   228                 return bzuser
   216         return user
   229         return user
   217 
   230 
   218     def add_comment(self, bugid, text, committer):
   231     def get_bugzilla_user(self, committer):
   219         '''add comment to bug. try adding comment as committer of
   232         '''see if committer is a registered bugzilla user. Return
   220         changeset, otherwise as default bugzilla user.'''
   233         bugzilla username and userid if so. If not, return default
       
   234         bugzilla username and userid.'''
   221         user = self.map_committer(committer)
   235         user = self.map_committer(committer)
   222         try:
   236         try:
   223             userid = self.get_user_id(user)
   237             userid = self.get_user_id(user)
   224         except KeyError:
   238         except KeyError:
   225             try:
   239             try:
   226                 defaultuser = self.ui.config('bugzilla', 'bzuser')
   240                 defaultuser = self.ui.config('bugzilla', 'bzuser')
   227                 if not defaultuser:
   241                 if not defaultuser:
   228                     raise util.Abort(_('cannot find bugzilla user id for %s') %
   242                     raise util.Abort(_('cannot find bugzilla user id for %s') %
   229                                      user)
   243                                      user)
   230                 userid = self.get_user_id(defaultuser)
   244                 userid = self.get_user_id(defaultuser)
       
   245                 user = defaultuser
   231             except KeyError:
   246             except KeyError:
   232                 raise util.Abort(_('cannot find bugzilla user id for %s or %s') %
   247                 raise util.Abort(_('cannot find bugzilla user id for %s or %s') %
   233                                  (user, defaultuser))
   248                                  (user, defaultuser))
       
   249         return (user, userid)
       
   250 
       
   251     def add_comment(self, bugid, text, committer):
       
   252         '''add comment to bug. try adding comment as committer of
       
   253         changeset, otherwise as default bugzilla user.'''
       
   254         (user, userid) = self.get_bugzilla_user(committer)
   234         now = time.strftime('%Y-%m-%d %H:%M:%S')
   255         now = time.strftime('%Y-%m-%d %H:%M:%S')
   235         self.run('''insert into longdescs
   256         self.run('''insert into longdescs
   236                     (bug_id, who, bug_when, thetext)
   257                     (bug_id, who, bug_when, thetext)
   237                     values (%s, %s, %s, %s)''',
   258                     values (%s, %s, %s, %s)''',
   238                  (bugid, userid, now, text))
   259                  (bugid, userid, now, text))
   239         self.run('''insert into bugs_activity (bug_id, who, bug_when, fieldid)
   260         self.run('''insert into bugs_activity (bug_id, who, bug_when, fieldid)
   240                     values (%s, %s, %s, %s)''',
   261                     values (%s, %s, %s, %s)''',
   241                  (bugid, userid, now, self.longdesc_id))
   262                  (bugid, userid, now, self.longdesc_id))
   242         self.conn.commit()
   263         self.conn.commit()
   243 
   264 
   244 class bugzilla_3_0(bugzilla_2_16):
   265 class bugzilla_2_18(bugzilla_2_16):
   245     '''support for bugzilla 3.0 series.'''
   266     '''support for bugzilla 2.18 series.'''
   246 
   267 
   247     def __init__(self, ui):
   268     def __init__(self, ui):
   248         bugzilla_2_16.__init__(self, ui)
   269         bugzilla_2_16.__init__(self, ui)
       
   270         self.default_notify = "cd %(bzdir)s && perl -T contrib/sendbugmail.pl %(id)s %(user)s"
       
   271 
       
   272 class bugzilla_3_0(bugzilla_2_18):
       
   273     '''support for bugzilla 3.0 series.'''
       
   274 
       
   275     def __init__(self, ui):
       
   276         bugzilla_2_18.__init__(self, ui)
   249 
   277 
   250     def get_longdesc_id(self):
   278     def get_longdesc_id(self):
   251         '''get identity of longdesc field'''
   279         '''get identity of longdesc field'''
   252         self.run('select id from fielddefs where name = "longdesc"')
   280         self.run('select id from fielddefs where name = "longdesc"')
   253         ids = self.cursor.fetchall()
   281         ids = self.cursor.fetchall()
   258 class bugzilla(object):
   286 class bugzilla(object):
   259     # supported versions of bugzilla. different versions have
   287     # supported versions of bugzilla. different versions have
   260     # different schemas.
   288     # different schemas.
   261     _versions = {
   289     _versions = {
   262         '2.16': bugzilla_2_16,
   290         '2.16': bugzilla_2_16,
       
   291         '2.18': bugzilla_2_18,
   263         '3.0':  bugzilla_3_0
   292         '3.0':  bugzilla_3_0
   264         }
   293         }
   265 
   294 
   266     _default_bug_re = (r'bugs?\s*,?\s*(?:#|nos?\.?|num(?:ber)?s?)?\s*'
   295     _default_bug_re = (r'bugs?\s*,?\s*(?:#|nos?\.?|num(?:ber)?s?)?\s*'
   267                        r'((?:\d+\s*(?:,?\s*(?:and)?)?\s*)+)')
   296                        r'((?:\d+\s*(?:,?\s*(?:and)?)?\s*)+)')
   373         ctx = repo[node]
   402         ctx = repo[node]
   374         ids = bz.find_bug_ids(ctx)
   403         ids = bz.find_bug_ids(ctx)
   375         if ids:
   404         if ids:
   376             for id in ids:
   405             for id in ids:
   377                 bz.update(id, ctx)
   406                 bz.update(id, ctx)
   378             bz.notify(ids)
   407             bz.notify(ids, util.email(ctx.user()))
   379     except MySQLdb.MySQLError, err:
   408     except MySQLdb.MySQLError, err:
   380         raise util.Abort(_('database error: %s') % err[1])
   409         raise util.Abort(_('database error: %s') % err[1])
   381 
   410