~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""Bugs support for the scanner."""

__metaclass__ = type
__all__ = [
    'BugBranchLinker',
    ]

import urlparse

from bzrlib.errors import InvalidBugStatus
from zope.component import getUtility

from lp.app.errors import NotFoundError
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
from lp.bugs.interfaces.bug import IBugSet


class BugBranchLinker:
    """Links branches to bugs based on revision metadata."""

    def __init__(self, db_branch):
        self.db_branch = db_branch

    def _getBugFromUrl(self, url):
        protocol, host, path, ignored, ignored = urlparse.urlsplit(url)

        # Skip URLs that don't point to Launchpad.
        if host != 'launchpad.net':
            return None

        # Remove empty path segments.
        segments = [
            segment for segment in path.split('/') if len(segment) > 0]
        # Don't allow Launchpad URLs that aren't /bugs/<integer>.
        try:
            bug_segment, bug_id = segments
        except ValueError:
            return None
        if bug_segment != 'bugs':
            return None
        try:
            return int(bug_id)
        except ValueError:
            return None

    def _getBugStatus(self, bzr_status):
        # Make sure the status is acceptable.
        valid_statuses = {'fixed': 'fixed'}
        return valid_statuses.get(bzr_status.lower(), None)

    def extractBugInfo(self, bzr_revision):
        """Parse bug information out of the given revision property.

        :param bug_status_prop: A string containing lines of
            '<bug_url> <status>'.
        :return: dict mapping bug IDs to BugBranchStatuses.
        """
        bug_statuses = {}
        for url, status in bzr_revision.iter_bugs():
            bug = self._getBugFromUrl(url)
            status = self._getBugStatus(status)
            if bug is None or status is None:
                continue
            bug_statuses[bug] = status
        return bug_statuses

    def createBugBranchLinksForRevision(self, bzr_revision):
        """Create bug-branch links for a revision.

        This looks inside the 'bugs' property of the given Bazaar revision and
        creates a BugBranch record for each bug mentioned.
        """
        try:
            bug_info = self.extractBugInfo(bzr_revision)
        except InvalidBugStatus:
            return
        bug_set = getUtility(IBugSet)
        for bug_id, status in bug_info.iteritems():
            try:
                bug = bug_set.get(bug_id)
            except NotFoundError:
                pass
            else:
                bug.linkBranch(
                    branch=self.db_branch,
                    registrant=getUtility(ILaunchpadCelebrities).janitor)


def got_new_revision(new_revision):
    if new_revision.isMainline():
        linker = BugBranchLinker(new_revision.db_branch)
        linker.createBugBranchLinksForRevision(new_revision.bzr_revision)