Exemplo n.º 1
0
def commit_message_filter(msg):
    # type: (bytes) -> Tuple[bytes, Dict[Text, Text]]
    metadata = {}
    m = commitparser.BUG_RE.match(msg)
    if m:
        bug_bytes, bug_number = m.groups()[:2]
        if msg.startswith(bug_bytes):
            prefix = re.compile(br"^%s[^\w\d\[\(]*" % bug_bytes)
            msg = prefix.sub(b"", msg)
        metadata[u"bugzilla-url"] = env.bz.bugzilla_url(int(bug_number))

    reviewers = u", ".join(
        item.decode("utf8", "replace")
        for item in commitparser.parse_reviewers(msg))
    if reviewers:
        metadata["gecko-reviewers"] = reviewers
    msg = commitparser.replace_reviewers(msg, "")
    msg = commitparser.strip_commit_metadata(msg)
    description = msg.splitlines()
    if description:
        summary = description.pop(0)
        summary = summary.rstrip(b"!#$%&(*+,-/:;<=>@[\\^_`{|~").rstrip()
        msg = summary + (b"\n" +
                         b"\n".join(description) if description else b"")

    return msg, metadata
    def get(self, request, *args, **kwargs):
        try:
            parent_request = get_parent_rr(ReviewRequest.objects.get(
                id=kwargs[self.uri_object_key]))
        except ReviewRequest.DoesNotExist:
            return DOES_NOT_EXIST
        if parent_request is None:
            return DOES_NOT_EXIST
        if not is_parent(parent_request):
            return NOT_PARENT
        if not parent_request.is_accessible_by(request.user):
            return PERMISSION_DENIED
        if COMMITS_KEY not in parent_request.extra_data:
            logging.error('Parent review request %s missing COMMIT_KEY'
                          % parent_request.id)
            return NOT_PARENT

        result = []
        children = json.loads(parent_request.extra_data[COMMITS_KEY])
        for child in children:
            try:
                child_request = ReviewRequest.objects.get(id=child[1])
            except ReviewRequest.DoesNotExist:
                return DOES_NOT_EXIST
            if not child_request.approved:
                return AUTOLAND_REVIEW_NOT_APPROVED

            reviewers = [
                r.user.username for r in gen_latest_reviews(child_request) if
                r.ship_it and
                r.user != child_request.submitter
            ]

            if not reviewers and child_request.approved:
                # This review request is approved (the repeated check is
                # to ensure this is guaranteed if other parts of the code
                # change) but we have an empty list of reviewers. We'll
                # assume the author has just approved this themself and
                # set r=me
                reviewers.append('me')

            result.append({
                'commit': child[0],
                'id': child[1],
                'reviewers': reviewers,
                'summary': replace_reviewers(child_request.description,
                                             reviewers)
            })

        return 200, {
            'commits': result,
            'total_results': len(result),
            'links': self.get_links(request=request),
        }
    def get(self, request, *args, **kwargs):
        try:
            parent_request = get_parent_rr(ReviewRequest.objects.get(
                id=kwargs[self.uri_object_key]))
        except ReviewRequest.DoesNotExist:
            return DOES_NOT_EXIST
        if parent_request is None:
            return DOES_NOT_EXIST
        if not is_parent(parent_request):
            return NOT_PARENT
        if not parent_request.is_accessible_by(request.user):
            return PERMISSION_DENIED
        if COMMITS_KEY not in parent_request.extra_data:
            logging.error('Parent review request %s missing COMMIT_KEY'
                          % parent_request.id)
            return NOT_PARENT

        result = []
        children = json.loads(parent_request.extra_data[COMMITS_KEY])
        for child in children:
            try:
                child_request = ReviewRequest.objects.get(id=child[1])
            except ReviewRequest.DoesNotExist:
                return DOES_NOT_EXIST
            if not child_request.approved:
                return AUTOLAND_REVIEW_NOT_APPROVED

            reviewers = map(lambda review: review.user.username,
                            gen_latest_reviews(child_request))
            result.append({
                'commit': child[0],
                'id': child[1],
                'reviewers': reviewers,
                'summary': replace_reviewers(child_request.description,
                                             reviewers)
            })

        return 200, {
            'commits': result,
            'total_results': len(result),
            'links': self.get_links(request=request),
        }
Exemplo n.º 4
0
def commit_message_filter(msg):
    metadata = {}
    m = commitparser.BUG_RE.match(msg)
    if m:
        bug_str, bug_number = m.groups()[:2]
        if msg.startswith(bug_str):
            prefix = re.compile(r"^%s[^\w\d\[\(]*" % bug_str)
            msg = prefix.sub("", msg)
        metadata["bugzilla-url"] = env.bz.bugzilla_url(bug_number)

    reviewers = ", ".join(commitparser.parse_reviewers(msg))
    if reviewers:
        metadata["gecko-reviewers"] = reviewers
    msg = commitparser.replace_reviewers(msg, "")
    msg = commitparser.strip_commit_metadata(msg)
    description = msg.splitlines()
    if description:
        summary = description.pop(0)
        summary = summary.rstrip("!#$%&(*+,-/:;<=>@[\\^_`{|~").rstrip()
        description = "\n".join(description)
        msg = summary + ("\n" + description if description else "")

    return msg, metadata
    def test_replace_reviewers(self):
        # first with r? reviewer request syntax
        self.assertEqual(replace_reviewers('Bug 1 - some stuff; r?romulus', ['remus']), 'Bug 1 - some stuff; r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; r?romulus, r?remus', ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; r?romulus,r?remus', ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; r?romulus, remus', ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; r?romulus,remus', ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; (r?romulus)', ['remus']), 'Bug 1 - More stuff; (r=remus)')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; (r?romulus,remus)', ['remus']), 'Bug 1 - More stuff; (r=remus)')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; [r?romulus]', ['remus']), 'Bug 1 - More stuff; [r=remus]')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; [r?remus, r?romulus]', ['remus']), 'Bug 1 - More stuff; [r=remus]')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; r?romulus, a=test-only', ['remus']), 'Bug 1 - More stuff; r=remus, a=test-only')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; r?romulus, ux-r=test-only', ['remus', 'romulus']), 'Bug 1 - More stuff; r=remus,romulus, ux-r=test-only')

        # now with r= review granted syntax
        self.assertEqual(replace_reviewers('Bug 1 - some stuff; r=romulus', ['remus']), 'Bug 1 - some stuff; r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; r=romulus, r=remus', ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; r=romulus,r=remus', ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; r=romulus, remus', ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; r=romulus,remus', ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; (r=romulus)',['remus']), 'Bug 1 - More stuff; (r=remus)')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; (r=romulus,remus)', ['remus']), 'Bug 1 - More stuff; (r=remus)')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; [r=romulus]', ['remus']), 'Bug 1 - More stuff; [r=remus]')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; [r=remus, r=romulus]', ['remus']), 'Bug 1 - More stuff; [r=remus]')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff; r=romulus, a=test-only', ['remus']), 'Bug 1 - More stuff; r=remus, a=test-only')

        # try some other separators than ;
        self.assertEqual(replace_reviewers('Bug 1 - some stuff r=romulus', ['remus']), 'Bug 1 - some stuff r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff. r=romulus, r=remus', ['remus']), 'Bug 1 - More stuff. r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff - r=romulus,r=remus', ['remus']), 'Bug 1 - More stuff - r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff, r=romulus, remus', ['remus']), 'Bug 1 - More stuff, r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff.. r=romulus,remus', ['remus']), 'Bug 1 - More stuff.. r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff | (r=romulus)',['remus']), 'Bug 1 - More stuff | (r=remus)')

        # make sure things work with different spacing
        self.assertEqual(replace_reviewers('Bug 1 - some stuff;r=romulus,r=remus', ['remus']), 'Bug 1 - some stuff;r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff.r=romulus, r=remus', ['remus']), 'Bug 1 - More stuff.r=remus')
        self.assertEqual(replace_reviewers('Bug 1 - More stuff,r=romulus, remus', ['remus']), 'Bug 1 - More stuff,r=remus')

        self.assertEqual(replace_reviewers(
            'Bug 1094764 - Implement AudioContext.suspend and friends.  r=roc,ehsan\n'
            '- Relevant spec text:\n'
            '- http://webaudio.github.io/web-audio-api/#widl-AudioContext-suspend-Promise\n'
            '- http://webaudio.github.io/web-audio-api/#widl-AudioContext-resume-Promise\n',
            ['remus']),
            'Bug 1094764 - Implement AudioContext.suspend and friends.  r=remus\n'
            '- Relevant spec text:\n'
            '- http://webaudio.github.io/web-audio-api/#widl-AudioContext-suspend-Promise\n'
            '- http://webaudio.github.io/web-audio-api/#widl-AudioContext-resume-Promise')

        self.assertEqual(replace_reviewers(
            'Bug 380783 - nsStringAPI.h: no equivalent of IsVoid (tell if '
            'string is null), patch by Mook <*****@*****.**>, '
            'r=bsmedberg/dbaron, sr=dbaron, a1.9=bz',
            ['remus']),
            'Bug 380783 - nsStringAPI.h: no equivalent of IsVoid (tell if '
            'string is null), patch by Mook <*****@*****.**>, '
            'r=remus, sr=dbaron, a1.9=bz')

        self.assertEqual(replace_reviewers(
            'Bug 1 - blah r?dminor, r?gps, r?abc, sr=abc',
            ['dminor', 'glob', 'gps', 'abc']),
            'Bug 1 - blah r=dminor,glob,gps,abc, sr=abc')

        self.assertEqual(replace_reviewers(
            'Bug 1 - blah r?dminor r?gps r?abc sr=abc',
            ['dminor', 'glob', 'gps', 'abc']),
            'Bug 1 - blah r=dminor,glob,gps,abc sr=abc')

        self.assertEqual(replace_reviewers(
            'Bug 1 - blah r?dminor,r?gps,r?abc,sr=abc',
            ['dminor', 'glob', 'gps', 'abc']),
            'Bug 1 - blah r=dminor,glob,gps,abc,sr=abc')

        self.assertEqual(replace_reviewers('Bug 123 - Blah blah; r=gps DONTBUILD (NPOTB)', ['remus']), 'Bug 123 - Blah blah; r=remus DONTBUILD (NPOTB)')
        self.assertEqual(replace_reviewers('Bug 123 - Blah blah; r=gps DONTBUILD', ['remus']), 'Bug 123 - Blah blah; r=remus DONTBUILD')
        self.assertEqual(replace_reviewers('Bug 123 - Blah blah; r=gps (DONTBUILD)', ['remus']), 'Bug 123 - Blah blah; r=remus (DONTBUILD)')
    def test_replace_reviewers(self):
        # empty
        self.assertEqual(replace_reviewers('', ['remus']), 'r=remus')

        # first with r? reviewer request syntax
        self.assertEqual(
            replace_reviewers('Bug 1 - some stuff; r?romulus', ['remus']),
            'Bug 1 - some stuff; r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; r?romulus, r?remus',
                              ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; r?romulus,r?remus',
                              ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; r?romulus, remus',
                              ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; r?romulus,remus',
                              ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; (r?romulus)', ['remus']),
            'Bug 1 - More stuff; (r=remus)')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; (r?romulus,remus)',
                              ['remus']), 'Bug 1 - More stuff; (r=remus)')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; [r?romulus]', ['remus']),
            'Bug 1 - More stuff; [r=remus]')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; [r?remus, r?romulus]',
                              ['remus']), 'Bug 1 - More stuff; [r=remus]')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; r?romulus, a=test-only',
                              ['remus']),
            'Bug 1 - More stuff; r=remus, a=test-only')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; r?romulus, ux-r=test-only',
                              ['remus', 'romulus']),
            'Bug 1 - More stuff; r=remus,romulus, ux-r=test-only')

        # now with r= review granted syntax
        self.assertEqual(
            replace_reviewers('Bug 1 - some stuff; r=romulus', ['remus']),
            'Bug 1 - some stuff; r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; r=romulus, r=remus',
                              ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; r=romulus,r=remus',
                              ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; r=romulus, remus',
                              ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; r=romulus,remus',
                              ['remus']), 'Bug 1 - More stuff; r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; (r=romulus)', ['remus']),
            'Bug 1 - More stuff; (r=remus)')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; (r=romulus,remus)',
                              ['remus']), 'Bug 1 - More stuff; (r=remus)')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; [r=romulus]', ['remus']),
            'Bug 1 - More stuff; [r=remus]')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; [r=remus, r=romulus]',
                              ['remus']), 'Bug 1 - More stuff; [r=remus]')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff; r=romulus, a=test-only',
                              ['remus']),
            'Bug 1 - More stuff; r=remus, a=test-only')

        # try some other separators than ;
        self.assertEqual(
            replace_reviewers('Bug 1 - some stuff r=romulus', ['remus']),
            'Bug 1 - some stuff r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff. r=romulus, r=remus',
                              ['remus']), 'Bug 1 - More stuff. r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff - r=romulus,r=remus',
                              ['remus']), 'Bug 1 - More stuff - r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff, r=romulus, remus',
                              ['remus']), 'Bug 1 - More stuff, r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff.. r=romulus,remus',
                              ['remus']), 'Bug 1 - More stuff.. r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff | (r=romulus)', ['remus']),
            'Bug 1 - More stuff | (r=remus)')

        # make sure things work with different spacing
        self.assertEqual(
            replace_reviewers('Bug 1 - some stuff;r=romulus,r=remus',
                              ['remus']), 'Bug 1 - some stuff;r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff.r=romulus, r=remus',
                              ['remus']), 'Bug 1 - More stuff.r=remus')
        self.assertEqual(
            replace_reviewers('Bug 1 - More stuff,r=romulus, remus',
                              ['remus']), 'Bug 1 - More stuff,r=remus')

        self.assertEqual(
            replace_reviewers(
                'Bug 1094764 - Implement AudioContext.suspend and friends.  r=roc,ehsan\n'
                '- Relevant spec text:\n'
                '- http://webaudio.github.io/web-audio-api/#widl-AudioContext-suspend-Promise\n'
                '- http://webaudio.github.io/web-audio-api/#widl-AudioContext-resume-Promise\n',
                ['remus']),
            'Bug 1094764 - Implement AudioContext.suspend and friends.  r=remus\n'
            '- Relevant spec text:\n'
            '- http://webaudio.github.io/web-audio-api/#widl-AudioContext-suspend-Promise\n'
            '- http://webaudio.github.io/web-audio-api/#widl-AudioContext-resume-Promise'
        )

        self.assertEqual(
            replace_reviewers(
                'Bug 380783 - nsStringAPI.h: no equivalent of IsVoid (tell if '
                'string is null), patch by Mook <*****@*****.**>, '
                'r=bsmedberg/dbaron, sr=dbaron, a1.9=bz', ['remus']),
            'Bug 380783 - nsStringAPI.h: no equivalent of IsVoid (tell if '
            'string is null), patch by Mook <*****@*****.**>, '
            'r=remus, sr=dbaron, a1.9=bz')

        self.assertEqual(
            replace_reviewers('Bug 1 - blah r?dminor, r?gps, r?abc, sr=abc',
                              ['dminor', 'glob', 'gps', 'abc']),
            'Bug 1 - blah r=dminor,glob,gps,abc, sr=abc')

        self.assertEqual(
            replace_reviewers('Bug 1 - blah r?dminor r?gps r?abc sr=abc',
                              ['dminor', 'glob', 'gps', 'abc']),
            'Bug 1 - blah r=dminor,glob,gps,abc sr=abc')

        self.assertEqual(
            replace_reviewers('Bug 1 - blah r?dminor,r?gps,r?abc,sr=abc',
                              ['dminor', 'glob', 'gps', 'abc']),
            'Bug 1 - blah r=dminor,glob,gps,abc,sr=abc')

        self.assertEqual(
            replace_reviewers('Bug 123 - Blah blah; r=gps DONTBUILD (NPOTB)',
                              ['remus']),
            'Bug 123 - Blah blah; r=remus DONTBUILD (NPOTB)')
        self.assertEqual(
            replace_reviewers('Bug 123 - Blah blah; r=gps DONTBUILD',
                              ['remus']),
            'Bug 123 - Blah blah; r=remus DONTBUILD')
        self.assertEqual(
            replace_reviewers('Bug 123 - Blah blah; r=gps (DONTBUILD)',
                              ['remus']),
            'Bug 123 - Blah blah; r=remus (DONTBUILD)')

        self.assertEqual(
            replace_reviewers('Bug 123 - Blah blah; r?', ['remus']),
            'Bug 123 - Blah blah; r=remus')
        self.assertEqual(
            replace_reviewers('Bug 123 - Blah blah; r? DONTBUILD', ['remus']),
            'Bug 123 - Blah blah; r=remus DONTBUILD')
Exemplo n.º 7
0
def main(node):
    # hg
    rev = http_get(f'https://hg.mozilla.org/mozilla-central/json-rev/{node}',
                   f'{node}-hg')
    rev['summary'] = rev['desc'].splitlines()[0]
    rev['user'] = parseaddr(rev['user'])[1]

    if rev['backedoutby']:
        backedout_by = rev['backedoutby'][:12]
        backout = http_get(
            f'https://hg.mozilla.org/mozilla-central/json-rev/{backedout_by}',
            f'{backedout_by}-hg')
        backout['summary'] = backout['desc'].splitlines()[0]
        backout['user'] = parseaddr(backout['user'])[1]
    else:
        backout = None

    # patch
    patch = http_get(f'https://hg.mozilla.org/mozilla-central/raw-rev/{node}',
                     f'{node}-patch',
                     is_json=False)

    # bug-id
    bug_ids = parse_bug_ids(rev['summary'])
    if len(bug_ids) == 0:
        raise CommitException(f'failed to find bug-id in: {rev["summary"]}')
    if len(bug_ids) > 1:
        raise CommitException(f'found multiple bug-ids in: {rev["summary"]}')
    bug_id = bug_ids[0]

    # bug - meta
    bmo = 'https://bugzilla.mozilla.org/rest'
    bug = http_get(f'{bmo}/bug/{bug_id}', f'{node}-bug')['bugs'][0]

    # bug - history
    bug_history = http_get(f'{bmo}/bug/{bug_id}/history',
                           f'{node}-bug_history')['bugs'][0]['history']

    # bug - attachments
    bug_attachments = list(
        http_get(f'{bmo}/bug/{bug_id}/attachment?exclude_fields=data',
                 f'{node}-bug-attachments')['bugs'][str(bug_id)])

    # calc stats

    stats = dict(
        bug_url=f'https://bugzilla.mozilla.org/{bug_id}',
        bug_comment_count=bug['comment_count'],
        bug_product=bug['product'],
        bug_component=bug['component'],
        hg_url=f'https://hg.mozilla.org/mozilla-central/rev/{rev["node"]}',
        summary=rev['summary'],
        node=rev['node'],
        author=rev['user'],
        pusher=rev['pushuser'],
        people=[],
        patch_size=len(patch),
        patch_lines_of_code=len(patch.splitlines()),
        patches=[],
        push_timestamp=datetime.datetime.fromtimestamp(
            rev['pushdate'][0]).strftime('%Y-%m-%dT%H:%M:%SZ'),
        bug_id=bug_id,
        bug_created_timestamp=bug['creation_time'],
        assigned_to=[],
        status=[],
        flags=[],
        triaged=[],
    )

    stats['people'].append(dict(user=bug['creator'], rel='reporter'))
    stats['people'].append(dict(user=rev['user'], rel='push author'))
    stats['people'].append(dict(user=rev['pushuser'], rel='push user'))

    if backout:
        stats['backout'] = dict(
            summary=backout['summary'],
            user=backout['user'],
            timestamp=datetime.datetime.fromtimestamp(
                backout['pushdate'][0]).strftime('%Y-%m-%dT%H:%M:%SZ'),
        )
        stats['people'].append(dict(user=backout['user'],
                                    rel='backout author'))

    for attachment in bug_attachments:
        if not is_patch(attachment):
            continue
        patch_data = dict(
            content_type=attachment['content_type'],
            id=attachment['id'],
            timestamp=attachment['creation_time'],
            user=attachment['creator'],
            summary=attachment['summary'],
            status=[],
        )

        if patch_data['content_type'] == ATTACHMENT_TYPE_PHABRICATOR:
            rev_url = base64.b64decode(
                http_get(
                    f'{bmo}/bug/attachment/{patch_data["id"]}'
                    '?include_fields=data',
                    f'{patch_data["id"]}-attachment-data',
                )['attachments'][str(patch_data['id'])]['data']).decode()
            revision = Revision(rev_url)
            patch_data['revision'] = dict(
                url=rev_url,
                phid=revision.phid,
                diffs=revision.diffs(),
            )

        stats['patches'].append(patch_data)
        stats['people'].append(
            dict(user=attachment['creator'], rel='patch author'))

    for change_group in bug_history:
        for change in change_group['changes']:
            # assigned_to
            if change['field_name'] == 'assigned_to':
                stats['assigned_to'].append(
                    dict(user=change['added'], when=change_group['when']))
                stats['people'].append(
                    dict(user=change['added'], rel='assigned bug'))

            # triage (look for status-flags changed, or a component change)
            if (change['field_name'].startswith('cf_status_firefox')
                    and change['added'] != '---'):
                stats['triaged'].append(
                    dict(
                        user=change_group['who'],
                        action=f'{change["field_name"]}: {change["added"]}',
                        timestamp=change_group['when'],
                    ))
                stats['people'].append(
                    dict(user=change_group['who'], rel='triaged'))

            elif (change['field_name'] == 'component'
                  and change['removed'] == 'Untriaged'):
                stats['triaged'].append(
                    dict(
                        user=change_group['who'],
                        action=f'{change["field_name"]} -> {change["added"]}',
                        timestamp=change_group['when'],
                    ))
                stats['people'].append(
                    dict(user=change_group['who'], rel='triaged'))

            # reviews
            if change['field_name'] == 'flagtypes.name':
                add_attachment_flag(stats, change_group, change, 'review')
                add_attachment_flag(stats, change_group, change, 'feedback')
                add_bug_flag(stats, change_group, change, 'needinfo')

            # attachment obsoletion
            elif change['field_name'] == 'attachments.isobsolete':
                if change['added'] == '1':
                    status = 'obsoleted'
                else:
                    status = 'unobsoleted'

                attachment = find_attachment(stats, change['attachment_id'])
                if not attachment:
                    raise CommitException(f'attach {change["attachment_id"]}')
                attachment['status'].append(
                    dict(
                        status=status,
                        timestamp=change_group['when'],
                    ))
                stats['people'].append(
                    dict(user=change_group['who'], rel='obsoleted attachment'))

            # status
            elif change['field_name'] == 'status':
                stats['status'].append(
                    dict(
                        status=change['added'],
                        user=change_group['who'],
                        timestamp=change_group['when'],
                    ))
                stats['people'].append(
                    dict(user=change_group['who'], rel='bug status'))

    # see if we can identify the attachment that landed
    # this is fairly naive, comparing patch summaries to the commit summary.
    # this could be improved in a few ways, such as by looking at push
    # comments and presuming that the order of attachments is the same.
    # however, it is nearly impossible to be sure, so we'll just do a best
    # attempt here, generally avoiding false positives.

    landed_patch = None
    active_patches = []

    # go backwards through attachment statuses.  if we most recently
    # unobsoleted it, or we never obsoleted nor unobsoleted it, then it is
    # considered active.
    for patch in stats['patches']:
        for patch_status in reversed(patch['status']):
            if patch_status['status'] == 'unobsoleted':
                active_patches.append(patch)
                break
            if patch_status['status'] == 'obsoleted':
                break
        else:
            active_patches.append(patch)

    if len(active_patches) == 1:
        # *presume* this is the landed attachment
        landed_patch = active_patches[0]
    else:
        summary_base = commitparser.replace_reviewers(stats['summary'], None)
        for patch in active_patches:
            if (commitparser.replace_reviewers(patch['summary'],
                                               None) == summary_base):
                landed_patch = patch
                break

    if landed_patch:
        stats['landed_attachment_id'] = landed_patch['id']
    else:
        print(f'could not determine landed patch', file=sys.stderr)

    # tidy up
    stats['people'] = normalize_people(stats['people'])

    # display
    print(json.dumps(stats, indent=2, sort_keys=True))
Exemplo n.º 8
0
    def create_or_update_attachment(self, review_request, review_request_draft,
                                    flags):
        """Create or update the MozReview attachment using the provided flags.

        The `flags` parameter is an array of flags to set/update/clear.  This
        array matches the Bugzilla flag API:
        Setting:
            {
                'id': flag.id
                'name': 'review',
                'status': '?',
                'requestee': reviewer.email
            }
        Clearing:
            {
                'id': flag.id,
                'status': 'X'
            }
        """

        logger.info('Posting review request %s to bug %d.' %
                    (review_request.id, self.bug_id))

        rr_url = get_obj_url(review_request)
        diff_url = get_diff_url(review_request)

        # Build the comment.  Only post a comment if the diffset has
        # actually changed.
        comment = ''
        if review_request_draft.get_latest_diffset():
            diffset_count = review_request.diffset_history.diffsets.count()
            if diffset_count < 1:
                # We don't need the first line, since it is also the attachment
                # summary, which is displayed in the comment.
                full_commit_msg = review_request_draft.description.partition(
                    '\n')[2].strip()

                full_commit_msg = strip_commit_metadata(full_commit_msg)

                if full_commit_msg:
                    full_commit_msg += '\n\n'

                comment = '%sReview commit: %s\nSee other reviews: %s' % (
                    full_commit_msg, diff_url, rr_url)
            else:
                comment = ('Review request updated; see interdiff: '
                           '%sdiff/%d-%d/\n' %
                           (rr_url, diffset_count, diffset_count + 1))

        # Set up attachment metadata.
        attachment = self.get_attachment(review_request)
        params = {}
        if attachment:
            params['attachment_id'] = attachment['id']

            if attachment['is_obsolete']:
                params['is_obsolete'] = False
        else:
            params['data'] = diff_url
            params['content_type'] = 'text/x-review-board-request'

        params['file_name'] = 'reviewboard-%d-url.txt' % review_request.id
        params['summary'] = replace_reviewers(review_request_draft.summary,
                                              None)
        params['comment'] = comment
        if flags:
            params['flags'] = flags

        if attachment:
            self.updates.append(params)
        else:
            self.creates.append(params)
    def get(self, request, *args, **kwargs):
        try:
            parent_request = get_parent_rr(
                ReviewRequest.objects.get(id=kwargs[self.uri_object_key]))
        except ReviewRequest.DoesNotExist:
            return DOES_NOT_EXIST
        if parent_request is None:
            return DOES_NOT_EXIST

        commit_data = fetch_commit_data(parent_request)

        if not is_parent(parent_request, commit_data):
            return NOT_PARENT
        if not parent_request.is_accessible_by(request.user):
            return PERMISSION_DENIED
        if COMMITS_KEY not in commit_data.extra_data:
            logging.error('Parent review request %s missing COMMIT_KEY' %
                          parent_request.id)
            return NOT_PARENT

        result = []
        children = json.loads(commit_data.extra_data[COMMITS_KEY])
        for child in children:
            try:
                child_request = ReviewRequest.objects.get(id=child[1])
            except ReviewRequest.DoesNotExist:
                return DOES_NOT_EXIST
            if not child_request.approved:
                return AUTOLAND_REVIEW_NOT_APPROVED

            reviewers = [
                r.user.username for r in gen_latest_reviews(child_request)
                if r.ship_it and r.user != child_request.submitter
            ]

            if not reviewers and child_request.approved:
                # This review request is approved (the repeated check is
                # to ensure this is guaranteed if other parts of the code
                # change) but we have an empty list of reviewers. We'll
                # assume the author has just approved this themself.
                reviewers.append(child_request.submitter.username)

            # Detect if the commit has been changed since the last review.
            shipit_carryforward = has_shipit_carryforward(child_request)

            result.append({
                'commit':
                child[0],
                'id':
                child[1],
                'reviewers':
                reviewers,
                'shipit_carryforward':
                shipit_carryforward,
                'summary':
                replace_reviewers(child_request.description, reviewers)
            })

        return 200, {
            'commits': result,
            'total_results': len(result),
            'links': self.get_links(request=request),
        }
Exemplo n.º 10
0
    def create_or_update_attachment(self, review_request_id, summary,
                                    comment, url, carry_forward):
        """Creates or updates an attachment containing a review-request URL.

        The carry_forward argument should be a dictionary mapping reviewer
        email to a boolean indicating if that reviewer's flag should be left
        untouched.
        """

        logger.info('Posting review request %s to bug %d.' %
                    (review_request_id, self.bug_id))
        # Copy because we modify it.
        carry_forward = carry_forward.copy()
        params = {}
        flags = []
        rb_attachment = None

        self._update_attachments()

        # Find the associated attachment, then go through the review flags.
        for a in self.attachments:

            # Make sure we check for old-style URLs as well.
            if not self.bugzilla._rb_attach_url_matches(a['data'], url):
                continue

            rb_attachment = a

            # FIXME: There's some redundancy between here and
            # attachments.update_bugzilla_attachment().  The latter decides
            # which flags to carry forward based on their type and value, but
            # we also check flag types and variables here to decide, in turn,
            # if we should even check the carry_forward dict.  This should be
            # unified and clarified somehow.

            for f in a.get('flags', []):
                if f['name'] not in ['review', 'feedback']:
                    # We only care about review and feedback flags.
                    continue
                elif f['name'] == 'feedback':
                    # We always clear feedback flags.
                    flags.append({'id': f['id'], 'status': 'X'})
                elif f['status'] == '+' or f['status'] == '-':
                    # A reviewer has left a review, either in Review Board or
                    # in Bugzilla.
                    if f['setter'] not in carry_forward:
                        # This flag was set manually in Bugzilla rather
                        # then through a review on Review Board. Always
                        # clear these flags.
                        flags.append({'id': f['id'], 'status': 'X'})
                    else:
                        # This flag was set through Review Board; see if
                        # we should carry it forward.
                        if not carry_forward[f['setter']]:
                            # We should not carry this r+/r- forward so
                            # re-request review.
                            flags.append({
                                'id': f['id'],
                                'name': 'review',
                                'status': '?',
                                'requestee': f['setter']
                            })
                        # else we leave the flag alone, carrying it forward.

                        # In either case, we've dealt with this reviewer, so
                        # remove it from the carry_forward dict.
                        carry_forward.pop(f['setter'])
                elif ('requestee' not in f or
                      f['requestee'] not in carry_forward):
                    # We clear review flags where the requestee is not
                    # a reviewer, or if there is some (possibly future) flag
                    # other than + or - that does not have a 'requestee' field.
                    flags.append({'id': f['id'], 'status': 'X'})
                elif f['requestee'] in carry_forward:
                    # We're already waiting for a review from this user
                    # so don't touch the flag.
                    carry_forward.pop(f['requestee'])

            break

        # Add flags for new reviewers.
        # We can't set a missing r+ (if it was manually removed) except in the
        # trivial (and useless) case that the setter and the requestee are the
        # same person.  We could set r? again, but in the event that the
        # reviewer is not accepting review requests, this will block
        # publishing, with no way for the author to fix it.  So we'll just
        # ignore manually removed r+s.
        # This is sorted so behavior is deterministic (this mucks with test
        # output otherwise).
        for reviewer, keep in sorted(carry_forward.iteritems()):
            if not keep:
                flags.append({
                    'name': 'review',
                    'status': '?',
                    'requestee': reviewer,
                    'new': True
                })

        if rb_attachment:
            params['attachment_id'] = rb_attachment['id']

            if rb_attachment['is_obsolete']:
                params['is_obsolete'] = False
        else:
            params['data'] = url
            params['content_type'] = 'text/x-review-board-request'

        params['file_name'] = 'reviewboard-%d-url.txt' % review_request_id
        params['summary'] = replace_reviewers(summary, None)
        params['comment'] = comment
        if flags:
            params['flags'] = flags

        if rb_attachment:
            self.updates.append(params)
        else:
            self.creates.append(params)