1 # bugzilla.py - bugzilla integration for mercurial |
1 # bugzilla.py - bugzilla integration for mercurial |
2 # |
2 # |
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> |
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> |
4 # Copyright 2011-2 Jim Hague <jim.hague@acm.org> |
4 # Copyright 2011-4 Jim Hague <jim.hague@acm.org> |
5 # |
5 # |
6 # This software may be used and distributed according to the terms of the |
6 # This software may be used and distributed according to the terms of the |
7 # GNU General Public License version 2 or any later version. |
7 # GNU General Public License version 2 or any later version. |
8 |
8 |
9 '''hooks for integrating with the Bugzilla bug tracker |
9 '''hooks for integrating with the Bugzilla bug tracker |
521 class cookietransportrequest(object): |
521 class cookietransportrequest(object): |
522 """A Transport request method that retains cookies over its lifetime. |
522 """A Transport request method that retains cookies over its lifetime. |
523 |
523 |
524 The regular xmlrpclib transports ignore cookies. Which causes |
524 The regular xmlrpclib transports ignore cookies. Which causes |
525 a bit of a problem when you need a cookie-based login, as with |
525 a bit of a problem when you need a cookie-based login, as with |
526 the Bugzilla XMLRPC interface. |
526 the Bugzilla XMLRPC interface prior to 4.4.3. |
527 |
527 |
528 So this is a helper for defining a Transport which looks for |
528 So this is a helper for defining a Transport which looks for |
529 cookies being set in responses and saves them to add to all future |
529 cookies being set in responses and saves them to add to all future |
530 requests. |
530 requests. |
531 """ |
531 """ |
618 |
618 |
619 self.bzproxy = xmlrpclib.ServerProxy(bzweb, self.transport(bzweb)) |
619 self.bzproxy = xmlrpclib.ServerProxy(bzweb, self.transport(bzweb)) |
620 ver = self.bzproxy.Bugzilla.version()['version'].split('.') |
620 ver = self.bzproxy.Bugzilla.version()['version'].split('.') |
621 self.bzvermajor = int(ver[0]) |
621 self.bzvermajor = int(ver[0]) |
622 self.bzverminor = int(ver[1]) |
622 self.bzverminor = int(ver[1]) |
623 self.bzproxy.User.login({'login': user, 'password': passwd}) |
623 login = self.bzproxy.User.login({'login': user, 'password': passwd, |
|
624 'restrict_login': True}) |
|
625 self.bztoken = login.get('token', '') |
624 |
626 |
625 def transport(self, uri): |
627 def transport(self, uri): |
626 if urlparse.urlparse(uri, "http")[0] == "https": |
628 if urlparse.urlparse(uri, "http")[0] == "https": |
627 return cookiesafetransport() |
629 return cookiesafetransport() |
628 else: |
630 else: |
629 return cookietransport() |
631 return cookietransport() |
630 |
632 |
631 def get_bug_comments(self, id): |
633 def get_bug_comments(self, id): |
632 """Return a string with all comment text for a bug.""" |
634 """Return a string with all comment text for a bug.""" |
633 c = self.bzproxy.Bug.comments({'ids': [id], |
635 c = self.bzproxy.Bug.comments({'ids': [id], |
634 'include_fields': ['text']}) |
636 'include_fields': ['text'], |
|
637 'token': self.bztoken}) |
635 return ''.join([t['text'] for t in c['bugs'][str(id)]['comments']]) |
638 return ''.join([t['text'] for t in c['bugs'][str(id)]['comments']]) |
636 |
639 |
637 def filter_real_bug_ids(self, bugs): |
640 def filter_real_bug_ids(self, bugs): |
638 probe = self.bzproxy.Bug.get({'ids': sorted(bugs.keys()), |
641 probe = self.bzproxy.Bug.get({'ids': sorted(bugs.keys()), |
639 'include_fields': [], |
642 'include_fields': [], |
640 'permissive': True, |
643 'permissive': True, |
|
644 'token': self.bztoken, |
641 }) |
645 }) |
642 for badbug in probe['faults']: |
646 for badbug in probe['faults']: |
643 id = badbug['id'] |
647 id = badbug['id'] |
644 self.ui.status(_('bug %d does not exist\n') % id) |
648 self.ui.status(_('bug %d does not exist\n') % id) |
645 del bugs[id] |
649 del bugs[id] |
660 args['ids'] = [bugid] |
664 args['ids'] = [bugid] |
661 args['comment'] = {'body' : text} |
665 args['comment'] = {'body' : text} |
662 if 'fix' in newstate: |
666 if 'fix' in newstate: |
663 args['status'] = self.fixstatus |
667 args['status'] = self.fixstatus |
664 args['resolution'] = self.fixresolution |
668 args['resolution'] = self.fixresolution |
|
669 args['token'] = self.bztoken |
665 self.bzproxy.Bug.update(args) |
670 self.bzproxy.Bug.update(args) |
666 else: |
671 else: |
667 if 'fix' in newstate: |
672 if 'fix' in newstate: |
668 self.ui.warn(_("Bugzilla/XMLRPC needs Bugzilla 4.0 or later " |
673 self.ui.warn(_("Bugzilla/XMLRPC needs Bugzilla 4.0 or later " |
669 "to mark bugs fixed\n")) |
674 "to mark bugs fixed\n")) |
717 To stop users from crafting commit comments with |
722 To stop users from crafting commit comments with |
718 Bugzilla commands, specify the bug ID via the message body, rather |
723 Bugzilla commands, specify the bug ID via the message body, rather |
719 than the subject line, and leave a blank line after it. |
724 than the subject line, and leave a blank line after it. |
720 ''' |
725 ''' |
721 user = self.map_committer(committer) |
726 user = self.map_committer(committer) |
722 matches = self.bzproxy.User.get({'match': [user]}) |
727 matches = self.bzproxy.User.get({'match': [user], |
|
728 'token': self.bztoken}) |
723 if not matches['users']: |
729 if not matches['users']: |
724 user = self.ui.config('bugzilla', 'user', 'bugs') |
730 user = self.ui.config('bugzilla', 'user', 'bugs') |
725 matches = self.bzproxy.User.get({'match': [user]}) |
731 matches = self.bzproxy.User.get({'match': [user], |
|
732 'token': self.bztoken}) |
726 if not matches['users']: |
733 if not matches['users']: |
727 raise util.Abort(_("default bugzilla user %s email not found") % |
734 raise util.Abort(_("default bugzilla user %s email not found") % |
728 user) |
735 user) |
729 user = matches['users'][0]['email'] |
736 user = matches['users'][0]['email'] |
730 commands.append(self.makecommandline("id", bugid)) |
737 commands.append(self.makecommandline("id", bugid)) |