hgext/bugzilla.py
changeset 41343 7370f302be71
parent 37463 bbd240f81ac5
child 41759 aaad36b88298
equal deleted inserted replaced
41342:fe83040400b7 41343:7370f302be71
   301 from mercurial.node import short
   301 from mercurial.node import short
   302 from mercurial import (
   302 from mercurial import (
   303     error,
   303     error,
   304     logcmdutil,
   304     logcmdutil,
   305     mail,
   305     mail,
       
   306     pycompat,
   306     registrar,
   307     registrar,
   307     url,
   308     url,
   308     util,
   309     util,
   309 )
   310 )
   310 from mercurial.utils import (
   311 from mercurial.utils import (
   340 )
   341 )
   341 configitem('bugzilla', 'db',
   342 configitem('bugzilla', 'db',
   342     default='bugs',
   343     default='bugs',
   343 )
   344 )
   344 configitem('bugzilla', 'fixregexp',
   345 configitem('bugzilla', 'fixregexp',
   345     default=(r'fix(?:es)?\s*(?:bugs?\s*)?,?\s*'
   346     default=(br'fix(?:es)?\s*(?:bugs?\s*)?,?\s*'
   346              r'(?:nos?\.?|num(?:ber)?s?)?\s*'
   347              br'(?:nos?\.?|num(?:ber)?s?)?\s*'
   347              r'(?P<ids>(?:#?\d+\s*(?:,?\s*(?:and)?)?\s*)+)'
   348              br'(?P<ids>(?:#?\d+\s*(?:,?\s*(?:and)?)?\s*)+)'
   348              r'\.?\s*(?:h(?:ours?)?\s*(?P<hours>\d*(?:\.\d+)?))?')
   349              br'\.?\s*(?:h(?:ours?)?\s*(?P<hours>\d*(?:\.\d+)?))?')
   349 )
   350 )
   350 configitem('bugzilla', 'fixresolution',
   351 configitem('bugzilla', 'fixresolution',
   351     default='FIXED',
   352     default='FIXED',
   352 )
   353 )
   353 configitem('bugzilla', 'fixstatus',
   354 configitem('bugzilla', 'fixstatus',
   361 )
   362 )
   362 configitem('bugzilla', 'password',
   363 configitem('bugzilla', 'password',
   363     default=None,
   364     default=None,
   364 )
   365 )
   365 configitem('bugzilla', 'regexp',
   366 configitem('bugzilla', 'regexp',
   366     default=(r'bugs?\s*,?\s*(?:#|nos?\.?|num(?:ber)?s?)?\s*'
   367     default=(br'bugs?\s*,?\s*(?:#|nos?\.?|num(?:ber)?s?)?\s*'
   367              r'(?P<ids>(?:\d+\s*(?:,?\s*(?:and)?)?\s*)+)'
   368              br'(?P<ids>(?:\d+\s*(?:,?\s*(?:and)?)?\s*)+)'
   368              r'\.?\s*(?:h(?:ours?)?\s*(?P<hours>\d*(?:\.\d+)?))?')
   369              br'\.?\s*(?:h(?:ours?)?\s*(?P<hours>\d*(?:\.\d+)?))?')
   369 )
   370 )
   370 configitem('bugzilla', 'strip',
   371 configitem('bugzilla', 'strip',
   371     default=0,
   372     default=0,
   372 )
   373 )
   373 configitem('bugzilla', 'style',
   374 configitem('bugzilla', 'style',
   731     def get_bug_comments(self, id):
   732     def get_bug_comments(self, id):
   732         """Return a string with all comment text for a bug."""
   733         """Return a string with all comment text for a bug."""
   733         c = self.bzproxy.Bug.comments({'ids': [id],
   734         c = self.bzproxy.Bug.comments({'ids': [id],
   734                                        'include_fields': ['text'],
   735                                        'include_fields': ['text'],
   735                                        'token': self.bztoken})
   736                                        'token': self.bztoken})
   736         return ''.join([t['text'] for t in c['bugs'][str(id)]['comments']])
   737         return ''.join([t['text'] for t in c['bugs']['%d' % id]['comments']])
   737 
   738 
   738     def filter_real_bug_ids(self, bugs):
   739     def filter_real_bug_ids(self, bugs):
   739         probe = self.bzproxy.Bug.get({'ids': sorted(bugs.keys()),
   740         probe = self.bzproxy.Bug.get({'ids': sorted(bugs.keys()),
   740                                       'include_fields': [],
   741                                       'include_fields': [],
   741                                       'permissive': True,
   742                                       'permissive': True,
   802             raise error.Abort(_("configuration 'bzemail' missing"))
   803             raise error.Abort(_("configuration 'bzemail' missing"))
   803         mail.validateconfig(self.ui)
   804         mail.validateconfig(self.ui)
   804 
   805 
   805     def makecommandline(self, fieldname, value):
   806     def makecommandline(self, fieldname, value):
   806         if self.bzvermajor >= 4:
   807         if self.bzvermajor >= 4:
   807             return "@%s %s" % (fieldname, str(value))
   808             return "@%s %s" % (fieldname, pycompat.bytestr(value))
   808         else:
   809         else:
   809             if fieldname == "id":
   810             if fieldname == "id":
   810                 fieldname = "bug_id"
   811                 fieldname = "bug_id"
   811             return "@%s = %s" % (fieldname, str(value))
   812             return "@%s = %s" % (fieldname, pycompat.bytestr(value))
   812 
   813 
   813     def send_bug_modify_email(self, bugid, commands, comment, committer):
   814     def send_bug_modify_email(self, bugid, commands, comment, committer):
   814         '''send modification message to Bugzilla bug via email.
   815         '''send modification message to Bugzilla bug via email.
   815 
   816 
   816         The message format is documented in the Bugzilla email_in.pl
   817         The message format is documented in the Bugzilla email_in.pl
   871         self.passwd = self.ui.config('bugzilla', 'password')
   872         self.passwd = self.ui.config('bugzilla', 'password')
   872         self.fixstatus = self.ui.config('bugzilla', 'fixstatus')
   873         self.fixstatus = self.ui.config('bugzilla', 'fixstatus')
   873         self.fixresolution = self.ui.config('bugzilla', 'fixresolution')
   874         self.fixresolution = self.ui.config('bugzilla', 'fixresolution')
   874 
   875 
   875     def apiurl(self, targets, include_fields=None):
   876     def apiurl(self, targets, include_fields=None):
   876         url = '/'.join([self.bzroot] + [str(t) for t in targets])
   877         url = '/'.join([self.bzroot] + [pycompat.bytestr(t) for t in targets])
   877         qv = {}
   878         qv = {}
   878         if self.apikey:
   879         if self.apikey:
   879             qv['api_key'] = self.apikey
   880             qv['api_key'] = self.apikey
   880         elif self.user and self.passwd:
   881         elif self.user and self.passwd:
   881             qv['login'] = self.user
   882             qv['login'] = self.user
   936         '''remove bug IDs where node occurs in comment text from bugs.'''
   937         '''remove bug IDs where node occurs in comment text from bugs.'''
   937         sn = short(node)
   938         sn = short(node)
   938         for bugid in bugs.keys():
   939         for bugid in bugs.keys():
   939             burl = self.apiurl(('bug', bugid, 'comment'), include_fields='text')
   940             burl = self.apiurl(('bug', bugid, 'comment'), include_fields='text')
   940             result = self._fetch(burl)
   941             result = self._fetch(burl)
   941             comments = result['bugs'][str(bugid)]['comments']
   942             comments = result['bugs'][pycompat.bytestr(bugid)]['comments']
   942             if any(sn in c['text'] for c in comments):
   943             if any(sn in c['text'] for c in comments):
   943                 self.ui.status(_('bug %d already knows about changeset %s\n') %
   944                 self.ui.status(_('bug %d already knows about changeset %s\n') %
   944                                (bugid, sn))
   945                                (bugid, sn))
   945                 del bugs[bugid]
   946                 del bugs[bugid]
   946 
   947 
  1009 
  1010 
  1010         self.bug_re = re.compile(
  1011         self.bug_re = re.compile(
  1011             self.ui.config('bugzilla', 'regexp'), re.IGNORECASE)
  1012             self.ui.config('bugzilla', 'regexp'), re.IGNORECASE)
  1012         self.fix_re = re.compile(
  1013         self.fix_re = re.compile(
  1013             self.ui.config('bugzilla', 'fixregexp'), re.IGNORECASE)
  1014             self.ui.config('bugzilla', 'fixregexp'), re.IGNORECASE)
  1014         self.split_re = re.compile(r'\D+')
  1015         self.split_re = re.compile(br'\D+')
  1015 
  1016 
  1016     def find_bugs(self, ctx):
  1017     def find_bugs(self, ctx):
  1017         '''return bugs dictionary created from commit comment.
  1018         '''return bugs dictionary created from commit comment.
  1018 
  1019 
  1019         Extract bug info from changeset comments. Filter out any that are
  1020         Extract bug info from changeset comments. Filter out any that are
  1096                      'to bug {bug}.\ndetails:\n\t{desc|tabindent}')
  1097                      'to bug {bug}.\ndetails:\n\t{desc|tabindent}')
  1097         spec = logcmdutil.templatespec(tmpl, mapfile)
  1098         spec = logcmdutil.templatespec(tmpl, mapfile)
  1098         t = logcmdutil.changesettemplater(self.ui, self.repo, spec)
  1099         t = logcmdutil.changesettemplater(self.ui, self.repo, spec)
  1099         self.ui.pushbuffer()
  1100         self.ui.pushbuffer()
  1100         t.show(ctx, changes=ctx.changeset(),
  1101         t.show(ctx, changes=ctx.changeset(),
  1101                bug=str(bugid),
  1102                bug=pycompat.bytestr(bugid),
  1102                hgweb=self.ui.config('web', 'baseurl'),
  1103                hgweb=self.ui.config('web', 'baseurl'),
  1103                root=self.repo.root,
  1104                root=self.repo.root,
  1104                webroot=webroot(self.repo.root))
  1105                webroot=webroot(self.repo.root))
  1105         data = self.ui.popbuffer()
  1106         data = self.ui.popbuffer()
  1106         self.bzdriver.updatebug(bugid, newstate, data,
  1107         self.bzdriver.updatebug(bugid, newstate, data,