775 _default_fix_re = (r'fix(?:es)?\s*(?:bugs?\s*)?,?\s*' |
775 _default_fix_re = (r'fix(?:es)?\s*(?:bugs?\s*)?,?\s*' |
776 r'(?:nos?\.?|num(?:ber)?s?)?\s*' |
776 r'(?:nos?\.?|num(?:ber)?s?)?\s*' |
777 r'(?P<ids>(?:#?\d+\s*(?:,?\s*(?:and)?)?\s*)+)' |
777 r'(?P<ids>(?:#?\d+\s*(?:,?\s*(?:and)?)?\s*)+)' |
778 r'\.?\s*(?:h(?:ours?)?\s*(?P<hours>\d*(?:\.\d+)?))?') |
778 r'\.?\s*(?:h(?:ours?)?\s*(?P<hours>\d*(?:\.\d+)?))?') |
779 |
779 |
780 _bz = None |
|
781 |
|
782 def __init__(self, ui, repo): |
780 def __init__(self, ui, repo): |
783 self.ui = ui |
781 self.ui = ui |
784 self.repo = repo |
782 self.repo = repo |
785 |
783 |
786 def bz(self): |
784 bzversion = self.ui.config('bugzilla', 'version') |
787 '''return object that knows how to talk to bugzilla version in |
785 try: |
788 use.''' |
786 bzclass = bugzilla._versions[bzversion] |
789 |
787 except KeyError: |
790 if bugzilla._bz is None: |
788 raise util.Abort(_('bugzilla version %s not supported') % |
791 bzversion = self.ui.config('bugzilla', 'version') |
789 bzversion) |
792 try: |
790 self.bzdriver = bzclass(self.ui) |
793 bzclass = bugzilla._versions[bzversion] |
791 |
794 except KeyError: |
792 self.bug_re = re.compile( |
795 raise util.Abort(_('bugzilla version %s not supported') % |
793 self.ui.config('bugzilla', 'regexp', |
796 bzversion) |
794 bugzilla._default_bug_re), re.IGNORECASE) |
797 bugzilla._bz = bzclass(self.ui) |
795 self.fix_re = re.compile( |
798 return bugzilla._bz |
796 self.ui.config('bugzilla', 'fixregexp', |
799 |
797 bugzilla._default_fix_re), re.IGNORECASE) |
800 def __getattr__(self, key): |
798 self.split_re = re.compile(r'\D+') |
801 return getattr(self.bz(), key) |
|
802 |
|
803 _bug_re = None |
|
804 _fix_re = None |
|
805 _split_re = None |
|
806 |
799 |
807 def find_bugs(self, ctx): |
800 def find_bugs(self, ctx): |
808 '''return bugs dictionary created from commit comment. |
801 '''return bugs dictionary created from commit comment. |
809 |
802 |
810 Extract bug info from changeset comments. Filter out any that are |
803 Extract bug info from changeset comments. Filter out any that are |
811 not known to Bugzilla, and any that already have a reference to |
804 not known to Bugzilla, and any that already have a reference to |
812 the given changeset in their comments. |
805 the given changeset in their comments. |
813 ''' |
806 ''' |
814 if bugzilla._bug_re is None: |
|
815 bugzilla._bug_re = re.compile( |
|
816 self.ui.config('bugzilla', 'regexp', |
|
817 bugzilla._default_bug_re), re.IGNORECASE) |
|
818 bugzilla._fix_re = re.compile( |
|
819 self.ui.config('bugzilla', 'fixregexp', |
|
820 bugzilla._default_fix_re), re.IGNORECASE) |
|
821 bugzilla._split_re = re.compile(r'\D+') |
|
822 start = 0 |
807 start = 0 |
823 hours = 0.0 |
808 hours = 0.0 |
824 bugs = {} |
809 bugs = {} |
825 bugmatch = bugzilla._bug_re.search(ctx.description(), start) |
810 bugmatch = self.bug_re.search(ctx.description(), start) |
826 fixmatch = bugzilla._fix_re.search(ctx.description(), start) |
811 fixmatch = self.fix_re.search(ctx.description(), start) |
827 while True: |
812 while True: |
828 bugattribs = {} |
813 bugattribs = {} |
829 if not bugmatch and not fixmatch: |
814 if not bugmatch and not fixmatch: |
830 break |
815 break |
831 if not bugmatch: |
816 if not bugmatch: |
858 except TypeError: |
843 except TypeError: |
859 pass |
844 pass |
860 except ValueError: |
845 except ValueError: |
861 self.ui.status(_("%s: invalid hours\n") % m.group('hours')) |
846 self.ui.status(_("%s: invalid hours\n") % m.group('hours')) |
862 |
847 |
863 for id in bugzilla._split_re.split(ids): |
848 for id in self.split_re.split(ids): |
864 if not id: |
849 if not id: |
865 continue |
850 continue |
866 bugs[int(id)] = bugattribs |
851 bugs[int(id)] = bugattribs |
867 if bugs: |
852 if bugs: |
868 self.filter_real_bug_ids(bugs) |
853 self.bzdriver.filter_real_bug_ids(bugs) |
869 if bugs: |
854 if bugs: |
870 self.filter_cset_known_bug_ids(ctx.node(), bugs) |
855 self.bzdriver.filter_cset_known_bug_ids(ctx.node(), bugs) |
871 return bugs |
856 return bugs |
872 |
857 |
873 def update(self, bugid, newstate, ctx): |
858 def update(self, bugid, newstate, ctx): |
874 '''update bugzilla bug with reference to changeset.''' |
859 '''update bugzilla bug with reference to changeset.''' |
875 |
860 |
900 bug=str(bugid), |
885 bug=str(bugid), |
901 hgweb=self.ui.config('web', 'baseurl'), |
886 hgweb=self.ui.config('web', 'baseurl'), |
902 root=self.repo.root, |
887 root=self.repo.root, |
903 webroot=webroot(self.repo.root)) |
888 webroot=webroot(self.repo.root)) |
904 data = self.ui.popbuffer() |
889 data = self.ui.popbuffer() |
905 self.updatebug(bugid, newstate, data, util.email(ctx.user())) |
890 self.bzdriver.updatebug(bugid, newstate, data, util.email(ctx.user())) |
|
891 |
|
892 def notify(self, bugs, committer): |
|
893 '''ensure Bugzilla users are notified of bug change.''' |
|
894 self.bzdriver.notify(bugs, committer) |
906 |
895 |
907 def hook(ui, repo, hooktype, node=None, **kwargs): |
896 def hook(ui, repo, hooktype, node=None, **kwargs): |
908 '''add comment to bugzilla for each changeset that refers to a |
897 '''add comment to bugzilla for each changeset that refers to a |
909 bugzilla bug id. only add a comment once per bug, so same change |
898 bugzilla bug id. only add a comment once per bug, so same change |
910 seen multiple times does not fill bug with duplicate data.''' |
899 seen multiple times does not fill bug with duplicate data.''' |