Beispiel #1
0
def get_needs_revision_facts(triager, issuewrapper, meta, shippable=None):
    # Thanks @adityacs for this PR. This PR requires revisions, either
    # because it fails to build or by reviewer request. Please make the
    # suggested revisions. When you are done, please comment with text
    # 'ready_for_review' and we will put this PR back into review.

    # a "dirty" mergeable_state can exist with "successfull" ci_state.

    # short alias
    iw = issuewrapper

    committer_count = None
    needs_revision = False
    needs_revision_msgs = []
    merge_commits = []
    has_merge_commit_notification = False
    needs_rebase = False
    needs_rebase_msgs = []
    has_shippable = False
    has_landscape = False
    has_travis = False
    has_travis_notification = False
    ci_state = None
    ci_stale = None
    mstate = None
    change_requested = None
    ready_for_review = None
    has_commit_mention = False
    has_commit_mention_notification = False

    has_shippable_yaml = None
    has_shippable_yaml_notification = None

    has_remote_repo = None

    user_reviews = None
    stale_reviews = {}

    # https://github.com/ansible/ansibullbot/issues/302
    has_multiple_modules = False
    needs_multiple_new_modules_notification = False

    rmeta = {
        'committer_count':
        committer_count,
        'is_needs_revision':
        needs_revision,
        'is_needs_revision_msgs':
        needs_revision_msgs,
        'is_needs_rebase':
        needs_rebase,
        'is_needs_rebase_msgs':
        needs_rebase_msgs,
        'has_commit_mention':
        has_commit_mention,
        'has_commit_mention_notification':
        has_commit_mention_notification,
        'has_shippable':
        has_shippable,
        'has_landscape':
        has_landscape,
        'has_travis':
        has_travis,
        'has_travis_notification':
        has_travis_notification,
        'merge_commits':
        merge_commits,
        'has_merge_commit_notification':
        has_merge_commit_notification,
        'mergeable':
        None,
        'mergeable_state':
        mstate,
        'change_requested':
        change_requested,
        'ci_state':
        ci_state,
        'ci_stale':
        ci_stale,
        'reviews':
        None,
        #'www_reviews': None,
        #'www_summary': None,
        'ready_for_review':
        ready_for_review,
        'has_shippable_yaml':
        has_shippable_yaml,
        'has_shippable_yaml_notification':
        has_shippable_yaml_notification,
        'has_remote_repo':
        has_remote_repo,
        'stale_reviews':
        stale_reviews,
        'has_multiple_modules':
        has_multiple_modules,
        'needs_multiple_new_modules_notification':
        needs_multiple_new_modules_notification
    }

    if not iw.is_pullrequest():
        return rmeta

    bpcs = iw.history.get_boilerplate_comments()
    bpcs = [x[0] for x in bpcs]

    # Scrape web data for debug purposes
    #rfn = iw.repo_full_name
    #www_summary = triager.gws.get_single_issue_summary(rfn, iw.number)
    #www_reviews = triager.gws.scrape_pullrequest_review(rfn, iw.number)

    maintainers = [
        x for x in triager.ansible_core_team if x not in triager.BOTNAMES
    ]

    #if meta.get('module_match'):
    #    maintainers += meta['module_match'].get('maintainers', [])
    maintainers += meta.get('component_maintainers', [])

    # get the exact state from shippable ...
    #   success/pending/failure/... ?
    ci_status = iw.pullrequest_status

    # code quality hooks
    if [
            x for x in ci_status
            if isinstance(x, dict) and 'landscape.io' in x['target_url']
    ]:
        has_landscape = True

    ci_states = [
        x['state'] for x in ci_status
        if isinstance(x, dict) and 'shippable.com' in x['target_url']
    ]

    if not ci_states:
        ci_state = None
    else:
        ci_state = ci_states[0]
    logging.info('ci_state == %s' % ci_state)

    # https://github.com/ansible/ansibullbot/issues/458
    ci_dates = [x['created_at'] for x in ci_status]
    ci_dates = sorted(set(ci_dates))
    if ci_dates:
        last_ci_date = ci_dates[-1]
        last_ci_date = datetime.datetime.strptime(last_ci_date,
                                                  '%Y-%m-%dT%H:%M:%SZ')
        ci_delta = (datetime.datetime.now() - last_ci_date).days
        if ci_delta > 7:
            ci_stale = True
        else:
            ci_stale = False
    else:
        ci_stale = False

    # clean/unstable/dirty/unknown
    mstate = iw.mergeable_state
    if not mstate:
        mstate = 'unknown'
    logging.info('mergeable_state == %s' % mstate)

    # clean/unstable/dirty/unknown
    if mstate != 'clean':

        if ci_state == 'failure':
            needs_revision = True
            needs_revision_msgs.append('ci failure')

        if mstate == 'dirty':
            needs_revision = True
            needs_rebase = True
            needs_revision_msgs.append('mergeable state is dirty')
            needs_rebase_msgs.append('mergeable state is dirty')

        elif mstate == 'unknown':
            # if tests are still running, this needs to be ignored.
            if ci_state not in ['pending']:
                needs_revision = True
                needs_revision_msgs.append('mergeable state is unknown')
                needs_rebase = True
                needs_rebase_msgs.append('mergeable state is unknown')

        elif mstate == 'unstable':
            # reduce the label churn
            if ci_state == 'pending' and 'needs_revision' in iw.labels:
                needs_revision = True
                needs_rebase_msgs.append('keep label till test finished')

    else:

        #current_hash = None
        #pending_reviews = []
        #hash_reviews = {}
        user_reviews = {}
        shipits = {}  # key: actor, value: created_at

        for event in iw.history.history:

            if event['actor'] in triager.BOTNAMES:
                continue

            if event['actor'] in maintainers and \
                    event['actor'] != iw.submitter:

                if event['event'] == 'labeled':
                    if event['label'] == 'needs_revision':
                        needs_revision = True
                        needs_revision_msgs.append('[%s] labeled' %
                                                   event['actor'])
                        continue

                if event['event'] == 'unlabeled':
                    if event['label'] == 'needs_revision':
                        needs_revision = False
                        needs_revision_msgs.append('[%s] unlabeled' %
                                                   event['actor'])
                        continue

                if event['event'] == 'commented':

                    if is_approval(event['body']):
                        shipits[event['actor']] = event['created_at']

                    if '!needs_revision' in event['body']:
                        needs_revision = False
                        needs_revision_msgs.append('[%s] !needs_revision' %
                                                   event['actor'])
                        continue

                    if 'needs_revision' in event['body'] and \
                            '!needs_revision' not in event['body']:
                        needs_revision = True
                        needs_revision_msgs.append('[%s] needs_revision' %
                                                   event['actor'])
                        continue

            if event['actor'] == iw.submitter:
                if event['event'] == 'commented':
                    if 'ready_for_review' in event['body']:
                        if ready_for_review is None or event[
                                'created_at'] > ready_for_review:
                            ready_for_review = event['created_at']
                        needs_revision = False
                        needs_revision_msgs.append('[%s] ready_for_review' %
                                                   event['actor'])
                        continue
                    if 'shipit' in event['body'].lower():
                        #ready_for_review = True
                        if ready_for_review is None or event[
                                'created_at'] > ready_for_review:
                            ready_for_review = event['created_at']
                        needs_revision = False
                        needs_revision_msgs.append('[%s] shipit' %
                                                   event['actor'])
                        continue

        # This is a complicated algo ... sigh
        user_reviews = get_review_state(iw.reviews,
                                        iw.submitter,
                                        number=iw.number,
                                        store=True)

        if user_reviews:
            last_commit = iw.commits[-1].sha
            change_requested = changes_requested_by(user_reviews, shipits,
                                                    last_commit,
                                                    ready_for_review)
            if change_requested:
                needs_revision = True
                needs_revision_msgs.append('outstanding reviews: %s' %
                                           ','.join(change_requested))
        #import pprint; pprint.pprint(www_reviews)
        #import pprint; pprint.pprint(change_requested)
        #import epdb; epdb.st()

    # Merge commits are bad, force a rebase
    if iw.merge_commits:
        needs_rebase = True

        for mc in iw.merge_commits:
            merge_commits.append(mc.html_url)
            needs_rebase_msgs.append('merge commit %s' % mc.commit.sha)

        if 'merge_commit_notify' not in bpcs:
            has_merge_commit_notification = False
        else:
            mc_comments = iw.history.search_user_comments(
                triager.BOTNAMES, 'boilerplate: merge_commit_notify')
            last_mc_comment = mc_comments[-1]
            mc_missing = []
            for mc in iw.merge_commits:
                if mc.html_url not in last_mc_comment:
                    mc_missing.append(mc)
            if mc_missing:
                has_merge_commit_notification = False
            else:
                has_merge_commit_notification = True

    # Count committers
    committer_count = len(sorted(set(iw.committer_emails)))

    if ci_status:
        for x in ci_status:
            if 'travis-ci.org' in x['target_url']:
                has_travis = True
                continue
            if 'shippable.com' in x['target_url']:
                has_shippable = True
                continue

    # we don't like @you in the commit messages
    # https://github.com/ansible/ansibullbot/issues/375
    for x in iw.commits:
        words = x.commit.message.split()
        if not words:
            continue
        if [x for x in words if x.startswith('@') and not x.endswith('@')]:
            has_commit_mention = True
            needs_revision = True
            needs_revision_msgs.append('@ in commit message')
            break

    # make sure they're notified about the problem
    if has_commit_mention:
        if 'commit_msg_mentions' in bpcs:
            has_commit_mention_notification = True

    if has_travis:
        needs_rebase = True
        needs_rebase_msgs.append('travis-ci found in status')

        # 'has_travis_notification': has_travis_notification,
        if 'travis_notify' in bpcs:
            has_travis_notification = True
        else:
            has_travis_notification = False

    # keep track of who deleted their repo/branch
    if iw.pullrequest.head.repo:
        has_remote_repo = True
    else:
        has_remote_repo = False

    # https://github.com/ansible/ansibullbot/issues/406
    has_shippable_yaml = iw.pullrequest_filepath_exists('shippable.yml')
    if not has_shippable_yaml:
        needs_rebase = True
        needs_rebase_msgs.append('missing shippable.yml')
        if 'no_shippable_yaml' in bpcs:
            has_shippable_yaml_notification = True
        else:
            has_shippable_yaml_notification = False

    # stale reviews
    if user_reviews:

        now = pytz.utc.localize(datetime.datetime.now())
        commits = [x for x in iw.history.history if x['event'] == 'committed']
        lc_date = commits[-1]['created_at']

        stale_reviews = {}
        for actor, review in user_reviews.items():
            if review['state'] != 'CHANGES_REQUESTED':
                continue
            lrd = None
            for x in iw.history.history:
                if x['actor'] != actor:
                    continue
                if x['event'] == 'review_changes_requested':
                    if not lrd or lrd < x['created_at']:
                        lrd = x['created_at']
                elif x['event'] == 'commented' and is_approval(x['body']):
                    if lrd and lrd < x['created_at']:
                        lrd = None

            if lrd:

                age = (now - lc_date).days
                delta = (lc_date - lrd).days
                if (lc_date > lrd) and (age > 7):
                    stale_reviews[actor] = {
                        'age': age,
                        'delta': delta,
                        'review_date': lrd.isoformat(),
                        'commit_date': lc_date.isoformat()
                    }

    # https://github.com/ansible/ansibullbot/issues/302
    if len(iw.new_modules) > 1:
        has_multiple_modules = True
        if 'multiple_module_notify' not in bpcs:
            needs_multiple_new_modules_notification = True
        needs_revision = True
        needs_revision_msgs.append('multiple new modules')

    logging.info('mergeable_state is %s' % mstate)
    logging.info('needs_rebase is %s' % needs_rebase)
    logging.info('needs_revision is %s' % needs_revision)
    logging.info('ready_for_review is %s' % ready_for_review)

    rmeta = {
        'committer_count':
        committer_count,
        'is_needs_revision':
        needs_revision,
        'is_needs_revision_msgs':
        needs_revision_msgs,
        'is_needs_rebase':
        needs_rebase,
        'is_needs_rebase_msgs':
        needs_rebase_msgs,
        'has_shippable':
        has_shippable,
        'has_landscape':
        has_landscape,
        'has_travis':
        has_travis,
        'has_travis_notification':
        has_travis_notification,
        'has_commit_mention':
        has_commit_mention,
        'has_commit_mention_notification':
        has_commit_mention_notification,
        'merge_commits':
        merge_commits,
        'has_merge_commit_notification':
        has_merge_commit_notification,
        'mergeable':
        iw.pullrequest.mergeable,
        'mergeable_state':
        mstate,
        'change_requested':
        change_requested,
        'ci_state':
        ci_state,
        'ci_stale':
        ci_stale,
        'reviews':
        iw.reviews,
        #'www_summary': www_summary,
        #'www_reviews': www_reviews,
        'ready_for_review_date':
        ready_for_review,
        'ready_for_review':
        bool(ready_for_review),
        'has_shippable_yaml':
        has_shippable_yaml,
        'has_shippable_yaml_notification':
        has_shippable_yaml_notification,
        'has_remote_repo':
        has_remote_repo,
        'stale_reviews':
        stale_reviews,
        'has_multiple_modules':
        has_multiple_modules,
        'needs_multiple_new_modules_notification':
        needs_multiple_new_modules_notification
    }
    if rmeta['ready_for_review_date']:
        rmeta['ready_for_review_date'] = rmeta[
            'ready_for_review_date'].isoformat()

    return rmeta
Beispiel #2
0
def get_needs_revision_facts(triager, issuewrapper, meta, shippable):
    # Thanks @adityacs for this PR. This PR requires revisions, either
    # because it fails to build or by reviewer request. Please make the
    # suggested revisions. When you are done, please comment with text
    # 'ready_for_review' and we will put this PR back into review.

    # a "dirty" mergeable_state can exist with "successfull" ci_state.

    # short alias
    iw = issuewrapper

    committer_count = None
    needs_revision = False
    needs_revision_msgs = []
    merge_commits = []
    has_merge_commit_notification = False
    needs_rebase = False
    needs_rebase_msgs = []
    ci_state = None
    ci_stale = True
    mstate = None
    change_requested = None
    ready_for_review = None
    has_commit_mention = False
    has_commit_mention_notification = False

    has_shippable = False
    has_shippable_yaml = None
    has_shippable_yaml_notification = None

    has_remote_repo = None

    user_reviews = None
    stale_reviews = {}

    # https://github.com/ansible/ansibullbot/issues/302
    has_multiple_modules = False
    needs_multiple_new_modules_notification = False

    rmeta = {
        u'committer_count':
        committer_count,
        u'is_needs_revision':
        needs_revision,
        u'is_needs_revision_msgs':
        needs_revision_msgs,
        u'is_needs_rebase':
        needs_rebase,
        u'is_needs_rebase_msgs':
        needs_rebase_msgs,
        u'has_commit_mention':
        has_commit_mention,
        u'has_commit_mention_notification':
        has_commit_mention_notification,
        u'has_shippable':
        has_shippable,
        u'merge_commits':
        merge_commits,
        u'has_merge_commit_notification':
        has_merge_commit_notification,
        u'mergeable':
        None,
        u'mergeable_state':
        mstate,
        u'change_requested':
        change_requested,
        u'ci_state':
        ci_state,
        u'ci_stale':
        ci_stale,
        u'reviews':
        None,
        u'ready_for_review':
        ready_for_review,
        u'has_shippable_yaml':
        has_shippable_yaml,
        u'has_shippable_yaml_notification':
        has_shippable_yaml_notification,
        u'has_remote_repo':
        has_remote_repo,
        u'stale_reviews':
        stale_reviews,
        u'has_multiple_modules':
        has_multiple_modules,
        u'needs_multiple_new_modules_notification':
        needs_multiple_new_modules_notification
    }

    if not iw.is_pullrequest():
        return rmeta

    bpcs = iw.history.get_boilerplate_comments()
    bpcs = [x[0] for x in bpcs]

    maintainers = [
        x for x in triager.ansible_core_team if x not in triager.BOTNAMES
    ]

    maintainers += meta.get(u'component_maintainers', [])

    ci_states = shippable.get_states(iw.pullrequest_status)
    if ci_states:
        has_shippable = True
        ci_stale = shippable.is_stale(ci_states)
        ci_state = ci_states[0].get('state')

    logging.info(u'ci_state == %s' % ci_state)

    # clean/unstable/dirty/unknown
    mstate = iw.mergeable_state
    if not mstate:
        mstate = u'unknown'
    logging.info(u'mergeable_state == %s' % mstate)

    # clean/unstable/dirty/unknown
    # FIXME ci_state related to shippable? make it general?
    if mstate != u'clean':
        if ci_state == u'failure':
            needs_revision = True
            needs_revision_msgs.append(u'ci failure')

        if mstate == u'dirty':
            needs_revision = True
            needs_rebase = True
            needs_revision_msgs.append(u'mergeable state is dirty')
            needs_rebase_msgs.append(u'mergeable state is dirty')

        elif mstate == u'unknown':
            # if tests are still running, this needs to be ignored.
            if ci_state not in [u'pending']:
                needs_revision = True
                needs_revision_msgs.append(u'mergeable state is unknown')
                needs_rebase = True
                needs_rebase_msgs.append(u'mergeable state is unknown')

        elif mstate == u'unstable':
            # reduce the label churn
            if ci_state == u'pending' and u'needs_revision' in iw.labels:
                needs_revision = True
                needs_rebase_msgs.append(u'keep label till test finished')
    else:
        user_reviews = {}
        shipits = {}  # key: actor, value: created_at

        has_set_needs_revision = set()

        for event in iw.history.history:

            if event[u'actor'] in triager.BOTNAMES:
                continue

            if event[u'actor'] in maintainers and \
                    event[u'actor'] != iw.submitter:

                if event[u'event'] == u'labeled':
                    if event[u'label'] == u'needs_revision':
                        needs_revision = True
                        needs_revision_msgs.append(u'[%s] labeled' %
                                                   event[u'actor'])
                        has_set_needs_revision.add(event[u'actor'])
                        continue

                if event[u'event'] == u'unlabeled':
                    if event[u'label'] == u'needs_revision':
                        needs_revision = False
                        needs_revision_msgs.append(u'[%s] unlabeled' %
                                                   event[u'actor'])
                        continue

                if event[u'event'] == u'commented':

                    if is_approval(event[u'body']):
                        shipits[event[u'actor']] = event[u'created_at']

                    if u'!needs_revision' in event[u'body']:
                        needs_revision = False
                        needs_revision_msgs.append(u'[%s] !needs_revision' %
                                                   event[u'actor'])
                        continue

                    if u'needs_revision' in event[u'body'] and \
                            u'!needs_revision' not in event[u'body']:
                        needs_revision = True
                        needs_revision_msgs.append(u'[%s] needs_revision' %
                                                   event[u'actor'])
                        has_set_needs_revision.add(event[u'actor'])
                        continue

                    if u'shipit' in event[u'body'].lower():
                        if event[u'actor'] in has_set_needs_revision:
                            has_set_needs_revision.remove(event[u'actor'])
                            if not has_set_needs_revision:
                                needs_revision = False
                                continue

            if event[u'actor'] == iw.submitter:
                if event[u'event'] == u'commented':
                    if u'ready_for_review' in event[u'body']:
                        if ready_for_review is None or event[
                                u'created_at'] > ready_for_review:
                            ready_for_review = event[u'created_at']
                        needs_revision = False
                        needs_revision_msgs.append(u'[%s] ready_for_review' %
                                                   event[u'actor'])
                        continue
                    if u'shipit' in event[u'body'].lower():
                        #ready_for_review = True
                        if ready_for_review is None or event[
                                u'created_at'] > ready_for_review:
                            ready_for_review = event[u'created_at']
                        needs_revision = False
                        needs_revision_msgs.append(u'[%s] shipit' %
                                                   event[u'actor'])
                        continue

        # This is a complicated algo ... sigh
        user_reviews = _get_review_state(
            iw.reviews,
            iw.submitter,
            number=iw.number,
        )

        if user_reviews:
            last_commit = iw.commits[-1].sha
            change_requested = _changes_requested_by(user_reviews, shipits,
                                                     last_commit,
                                                     ready_for_review)
            if change_requested:
                needs_revision = True
                needs_revision_msgs.append(u'outstanding reviews: %s' %
                                           u','.join(change_requested))

    # Merge commits are bad, force a rebase
    if iw.merge_commits:
        needs_rebase = True

        for mc in iw.merge_commits:
            merge_commits.append(mc.html_url)
            needs_rebase_msgs.append(u'merge commit %s' % mc.commit.sha)

        if u'merge_commit_notify' not in bpcs:
            has_merge_commit_notification = False
        else:
            mc_comments = iw.history.search_user_comments(
                triager.BOTNAMES, u'boilerplate: merge_commit_notify')
            last_mc_comment = mc_comments[-1]
            mc_missing = []
            for mc in iw.merge_commits:
                if mc.html_url not in last_mc_comment:
                    mc_missing.append(mc)
            if mc_missing:
                has_merge_commit_notification = False
            else:
                has_merge_commit_notification = True

    # Count committers
    committer_count = len(sorted(set(iw.committer_emails)))

    # we don't like @you in the commit messages
    # https://github.com/ansible/ansibullbot/issues/375
    for x in iw.commits:
        words = x.commit.message.split()
        if not words:
            continue
        if [x for x in words if x.startswith(u'@') and not x.endswith(u'@')]:
            has_commit_mention = True
            needs_revision = True
            needs_revision_msgs.append(u'@ in commit message')
            break

    # make sure they're notified about the problem
    if has_commit_mention:
        if u'commit_msg_mentions' in bpcs:
            has_commit_mention_notification = True

    # keep track of who deleted their repo/branch
    if iw.pullrequest.head.repo:
        has_remote_repo = True
    else:
        has_remote_repo = False

    # https://github.com/ansible/ansibullbot/issues/406
    has_shippable_yaml = iw.pullrequest_filepath_exists(
        shippable.required_file)
    if not has_shippable_yaml:
        needs_rebase = True
        needs_rebase_msgs.append(u'missing shippable.yml')
        if u'no_shippable_yaml' in bpcs:
            has_shippable_yaml_notification = True
        else:
            has_shippable_yaml_notification = False

    # stale reviews
    if user_reviews:

        now = pytz.utc.localize(datetime.datetime.now())
        commits = [
            x for x in iw.history.history if x[u'event'] == u'committed'
        ]
        lc_date = commits[-1][u'created_at']

        stale_reviews = {}
        for actor, review in user_reviews.items():
            if review[u'state'] != u'CHANGES_REQUESTED':
                continue
            lrd = None
            for x in iw.history.history:
                if x[u'actor'] != actor:
                    continue
                if x[u'event'] == u'review_changes_requested':
                    if not lrd or lrd < x[u'created_at']:
                        lrd = x[u'created_at']
                elif x[u'event'] == u'commented' and is_approval(x[u'body']):
                    if lrd and lrd < x[u'created_at']:
                        lrd = None

            if lrd:

                age = (now - lc_date).days
                delta = (lc_date - lrd).days
                if (lc_date > lrd) and (age > 7):
                    stale_reviews[actor] = {
                        u'age': age,
                        u'delta': delta,
                        u'review_date': lrd.isoformat(),
                        u'commit_date': lc_date.isoformat()
                    }

    # https://github.com/ansible/ansibullbot/issues/302
    if len(iw.new_modules) > 1:
        has_multiple_modules = True
        if u'multiple_module_notify' not in bpcs:
            needs_multiple_new_modules_notification = True
        needs_revision = True
        needs_revision_msgs.append(u'multiple new modules')

    logging.info(u'mergeable_state is %s' % mstate)
    logging.info(u'needs_rebase is %s' % needs_rebase)
    logging.info(u'needs_revision is %s' % needs_revision)
    logging.info(u'ready_for_review is %s' % ready_for_review)

    rmeta = {
        u'committer_count':
        committer_count,
        u'is_needs_revision':
        needs_revision,
        u'is_needs_revision_msgs':
        needs_revision_msgs,
        u'is_needs_rebase':
        needs_rebase,
        u'is_needs_rebase_msgs':
        needs_rebase_msgs,
        u'has_shippable':
        has_shippable,
        u'has_commit_mention':
        has_commit_mention,
        u'has_commit_mention_notification':
        has_commit_mention_notification,
        u'merge_commits':
        merge_commits,
        u'has_merge_commit_notification':
        has_merge_commit_notification,
        u'mergeable':
        iw.mergeable,
        u'mergeable_state':
        mstate,
        u'change_requested':
        change_requested,
        u'ci_state':
        ci_state,
        u'ci_stale':
        ci_stale,
        u'reviews':
        iw.reviews,
        u'ready_for_review_date':
        ready_for_review,
        u'ready_for_review':
        bool(ready_for_review),
        u'has_shippable_yaml':
        has_shippable_yaml,
        u'has_shippable_yaml_notification':
        has_shippable_yaml_notification,
        u'has_remote_repo':
        has_remote_repo,
        u'stale_reviews':
        stale_reviews,
        u'has_multiple_modules':
        has_multiple_modules,
        u'needs_multiple_new_modules_notification':
        needs_multiple_new_modules_notification
    }
    if rmeta[u'ready_for_review_date']:
        rmeta[u'ready_for_review_date'] = rmeta[
            u'ready_for_review_date'].isoformat()

    return rmeta
Beispiel #3
0
    def test_is_approval(self):
        self.assertTrue(is_approval('shipit'))
        self.assertTrue(is_approval('+1'))
        self.assertTrue(is_approval('LGTM'))

        self.assertTrue(is_approval(' shipit '))
        self.assertTrue(is_approval("\tshipit\t"))
        self.assertTrue(is_approval("\tshipit\n"))
        self.assertTrue(is_approval('Hey, LGTM !'))

        self.assertFalse(is_approval(':+1:'))
        self.assertFalse(is_approval('lgtm'))
        self.assertFalse(is_approval('Shipit'))
        self.assertFalse(is_approval('shipit!'))

        self.assertFalse(is_approval('shipits'))
        self.assertFalse(is_approval('LGTM.'))
        self.assertFalse(is_approval('Looks good to me'))
Beispiel #4
0
    def test_is_approval(self):
        self.assertTrue(is_approval(u'shipit'))
        self.assertTrue(is_approval(u'+1'))
        self.assertTrue(is_approval(u'LGTM'))

        self.assertTrue(is_approval(u' shipit '))
        self.assertTrue(is_approval(u"\tshipit\t"))
        self.assertTrue(is_approval(u"\tshipit\n"))
        self.assertTrue(is_approval(u'Hey, LGTM !'))

        self.assertFalse(is_approval(u':+1:'))
        self.assertFalse(is_approval(u'lgtm'))
        self.assertFalse(is_approval(u'Shipit'))
        self.assertFalse(is_approval(u'shipit!'))

        self.assertFalse(is_approval(u'shipits'))
        self.assertFalse(is_approval(u'LGTM.'))
        self.assertFalse(is_approval(u'Looks good to me'))
Beispiel #5
0
def get_needs_revision_facts(iw,
                             meta,
                             ci,
                             maintainer_team=None,
                             botnames=None):
    # Thanks @adityacs for this PR. This PR requires revisions, either
    # because it fails to build or by reviewer request. Please make the
    # suggested revisions. When you are done, please comment with text
    # 'ready_for_review' and we will put this PR back into review.

    # a "dirty" mergeable_state can exist with "successfull" ci_state.

    if maintainer_team is None:
        maintainer_team = []
    if botnames is None:
        botnames = []

    committer_count = None
    needs_revision = False
    needs_revision_msgs = []
    merge_commits = []
    has_merge_commit_notification = False
    needs_rebase = False
    needs_rebase_msgs = []
    ci_state = None
    ci_stale = False
    mstate = None
    change_requested = None
    ready_for_review = None
    has_commit_mention = False
    has_commit_mention_notification = False

    has_ci = False

    has_remote_repo = None

    user_reviews = None
    stale_reviews = {}

    # https://github.com/ansible/ansibullbot/issues/302
    has_multiple_modules = False
    needs_multiple_new_modules_notification = False

    rmeta = {
        'committer_count':
        committer_count,
        'is_needs_revision':
        needs_revision,
        'is_needs_revision_msgs':
        needs_revision_msgs,
        'is_needs_rebase':
        needs_rebase,
        'is_needs_rebase_msgs':
        needs_rebase_msgs,
        'has_commit_mention':
        has_commit_mention,
        'has_commit_mention_notification':
        has_commit_mention_notification,
        'has_ci':
        has_ci,
        'merge_commits':
        merge_commits,
        'has_merge_commit_notification':
        has_merge_commit_notification,
        'mergeable':
        None,
        'mergeable_state':
        mstate,
        'change_requested':
        change_requested,
        'ci_state':
        ci_state,
        'ci_stale':
        ci_stale,
        'reviews':
        None,
        'ready_for_review':
        ready_for_review,
        'has_remote_repo':
        has_remote_repo,
        'stale_reviews':
        stale_reviews,
        'has_multiple_modules':
        has_multiple_modules,
        'needs_multiple_new_modules_notification':
        needs_multiple_new_modules_notification
    }

    if not iw.is_pullrequest():
        return rmeta

    bpcs = iw.history.get_boilerplate_comments()
    bpcs = [x[0] for x in bpcs]

    maintainers = [x for x in maintainer_team if x not in botnames]

    maintainers += meta.get('component_maintainers', [])

    try:
        ci_date = ci.get_last_full_run_date()
    except NoCIError:
        pass
    else:
        has_ci = True
        if ci_date:
            ci_stale = (datetime.datetime.now() - ci_date).days > CI_STALE_DAYS
        ci_state = ci.state

    logging.info('ci_state == %s' % ci_state)

    # clean/unstable/dirty/unknown
    mstate = iw.mergeable_state
    if not mstate:
        mstate = 'unknown'
    logging.info('mergeable_state == %s' % mstate)

    # clean/unstable/dirty/unknown
    if mstate != 'clean':
        if ci_state == 'failure':
            needs_revision = True
            needs_revision_msgs.append('ci failure')

        if mstate == 'dirty':
            needs_revision = True
            needs_rebase = True
            needs_revision_msgs.append('mergeable state is dirty')
            needs_rebase_msgs.append('mergeable state is dirty')
        elif mstate == 'unknown':
            # if tests are still running, this needs to be ignored.
            if ci_state not in ['pending']:
                needs_revision = True
                needs_revision_msgs.append('mergeable state is unknown')
                needs_rebase = True
                needs_rebase_msgs.append('mergeable state is unknown')
        elif mstate == 'unstable':
            # reduce the label churn
            if ci_state == 'pending' and 'needs_revision' in iw.labels:
                needs_revision = True
                needs_rebase_msgs.append('keep label till test finished')
        if ci_state is None:
            needs_revision = True

        # FIXME mstate == 'draft'
    else:
        shipits = {}  # key: actor, value: created_at

        has_set_needs_revision = set()

        for event in iw.history.history:

            if event['actor'] in botnames:
                continue

            if event['actor'] in maintainers and \
                    event['actor'] != iw.submitter:

                if event['event'] == 'labeled':
                    if event['label'] == 'needs_revision':
                        needs_revision = True
                        needs_revision_msgs.append('[%s] labeled' %
                                                   event['actor'])
                        has_set_needs_revision.add(event['actor'])
                        continue

                if event['event'] == 'unlabeled':
                    if event['label'] == 'needs_revision':
                        needs_revision = False
                        needs_revision_msgs.append('[%s] unlabeled' %
                                                   event['actor'])
                        continue

                if event['event'] == 'commented':
                    if is_approval(event['body']):
                        shipits[event['actor']] = event['created_at']
                    if '!needs_revision' in event['body']:
                        needs_revision = False
                        needs_revision_msgs.append('[%s] !needs_revision' %
                                                   event['actor'])
                        continue
                    if any(line.startswith('needs_revision') for line in event['body'].splitlines()) and \
                            '!needs_revision' not in event['body']:
                        needs_revision = True
                        needs_revision_msgs.append('[%s] needs_revision' %
                                                   event['actor'])
                        has_set_needs_revision.add(event['actor'])
                        continue

                    if 'shipit' in event['body'].lower():
                        if event['actor'] in has_set_needs_revision:
                            has_set_needs_revision.remove(event['actor'])
                            if not has_set_needs_revision:
                                needs_revision = False
                                continue

            if event['actor'] == iw.submitter:
                if event['event'] == 'commented':
                    if 'ready_for_review' in event['body']:
                        if ready_for_review is None or event[
                                'created_at'] > ready_for_review:
                            ready_for_review = event['created_at']
                        needs_revision = False
                        needs_revision_msgs.append('[%s] ready_for_review' %
                                                   event['actor'])
                        continue
                    if 'shipit' in event['body'].lower():
                        if ready_for_review is None or event[
                                'created_at'] > ready_for_review:
                            ready_for_review = event['created_at']
                        needs_revision = False
                        needs_revision_msgs.append('[%s] shipit' %
                                                   event['actor'])
                        continue

        # This is a complicated algo ... sigh
        user_reviews = _get_review_state(
            iw.reviews,
            iw.submitter,
        )

        if user_reviews:
            last_commit = iw.commits[-1].sha
            change_requested = _changes_requested_by(user_reviews, shipits,
                                                     last_commit,
                                                     ready_for_review)
            if change_requested:
                needs_revision = True
                needs_revision_msgs.append('outstanding reviews: %s' %
                                           ','.join(change_requested))

    # Merge commits are bad, force a rebase
    if iw.merge_commits:
        needs_rebase = True

        for mc in iw.merge_commits:
            merge_commits.append(mc.html_url)
            needs_rebase_msgs.append('merge commit %s' % mc.commit.sha)

        if 'merge_commit_notify' not in bpcs:
            has_merge_commit_notification = False
        else:
            mc_comments = iw.history.search_user_comments(
                botnames, 'boilerplate: merge_commit_notify')
            last_mc_comment = mc_comments[-1]
            mc_missing = []
            for mc in iw.merge_commits:
                if mc.html_url not in last_mc_comment:
                    mc_missing.append(mc)
            if mc_missing:
                has_merge_commit_notification = False
            else:
                has_merge_commit_notification = True

    # Count committers
    committer_count = len(sorted(set(iw.committer_emails)))

    # we don't like @you in the commit messages
    # https://github.com/ansible/ansibullbot/issues/375
    for x in iw.commits:
        words = x.commit.message.split()
        if not words:
            continue
        if [x for x in words if x.startswith('@') and not x.endswith('@')]:
            has_commit_mention = True
            needs_revision = True
            needs_revision_msgs.append('@ in commit message')
            break

    # make sure they're notified about the problem
    if has_commit_mention:
        if 'commit_msg_mentions' in bpcs:
            has_commit_mention_notification = True

    # keep track of who deleted their repo/branch
    has_remote_repo = bool(iw.pullrequest.head.repo)

    # stale reviews
    if user_reviews:

        now = pytz.utc.localize(datetime.datetime.now())
        commits = [x for x in iw.history.history if x['event'] == 'committed']
        lc_date = commits[-1]['created_at']

        stale_reviews = {}
        for actor, review in user_reviews.items():
            if review['state'] != 'CHANGES_REQUESTED':
                continue
            lrd = None
            for x in iw.history.history:
                if x['actor'] != actor:
                    continue
                if x['event'] == 'review_changes_requested':
                    if not lrd or lrd < x['created_at']:
                        lrd = x['created_at']
                elif x['event'] == 'commented' and is_approval(x['body']):
                    if lrd and lrd < x['created_at']:
                        lrd = None

            if lrd:

                age = (now - lc_date).days
                delta = (lc_date - lrd).days
                if (lc_date > lrd) and (age > 7):
                    stale_reviews[actor] = {
                        'age': age,
                        'delta': delta,
                        'review_date': lrd.isoformat(),
                        'commit_date': lc_date.isoformat()
                    }

    # https://github.com/ansible/ansibullbot/issues/302
    if len(iw.new_modules) > 1:
        has_multiple_modules = True
        if 'multiple_module_notify' not in bpcs:
            needs_multiple_new_modules_notification = True
        needs_revision = True
        needs_revision_msgs.append('multiple new modules')

    logging.info('mergeable_state is %s' % mstate)
    logging.info('needs_rebase is %s' % needs_rebase)
    logging.info('needs_revision is %s' % needs_revision)
    logging.info('ready_for_review is %s' % ready_for_review)

    rmeta = {
        'committer_count':
        committer_count,
        'is_needs_revision':
        needs_revision,
        'is_needs_revision_msgs':
        needs_revision_msgs,
        'is_needs_rebase':
        needs_rebase,
        'is_needs_rebase_msgs':
        needs_rebase_msgs,
        'has_ci':
        has_ci,
        'has_commit_mention':
        has_commit_mention,
        'has_commit_mention_notification':
        has_commit_mention_notification,
        'merge_commits':
        merge_commits,
        'has_merge_commit_notification':
        has_merge_commit_notification,
        'mergeable':
        iw.mergeable,
        'mergeable_state':
        mstate,
        'change_requested':
        change_requested,
        'ci_state':
        ci_state,
        'ci_stale':
        ci_stale,
        'reviews':
        iw.reviews,
        'ready_for_review_date':
        ready_for_review,
        'ready_for_review':
        bool(ready_for_review),
        'has_remote_repo':
        has_remote_repo,
        'stale_reviews':
        stale_reviews,
        'has_multiple_modules':
        has_multiple_modules,
        'needs_multiple_new_modules_notification':
        needs_multiple_new_modules_notification
    }
    if rmeta['ready_for_review_date']:
        rmeta['ready_for_review_date'] = rmeta[
            'ready_for_review_date'].isoformat()

    return rmeta
Beispiel #6
0
def get_needs_revision_facts(triager, issuewrapper, meta, shippable=None):
    # Thanks @adityacs for this PR. This PR requires revisions, either
    # because it fails to build or by reviewer request. Please make the
    # suggested revisions. When you are done, please comment with text
    # 'ready_for_review' and we will put this PR back into review.

    # a "dirty" mergeable_state can exist with "successfull" ci_state.

    # short alias
    iw = issuewrapper

    committer_count = None
    needs_revision = False
    needs_revision_msgs = []
    merge_commits = []
    has_merge_commit_notification = False
    needs_rebase = False
    needs_rebase_msgs = []
    has_shippable = False
    has_landscape = False
    has_travis = False
    has_travis_notification = False
    has_zuul = False
    ci_state = None
    ci_stale = None
    mstate = None
    change_requested = None
    ready_for_review = None
    has_commit_mention = False
    has_commit_mention_notification = False

    has_shippable_yaml = None
    has_shippable_yaml_notification = None

    has_remote_repo = None

    user_reviews = None
    stale_reviews = {}

    # https://github.com/ansible/ansibullbot/issues/302
    has_multiple_modules = False
    needs_multiple_new_modules_notification = False

    rmeta = {
        u'committer_count': committer_count,
        u'is_needs_revision': needs_revision,
        u'is_needs_revision_msgs': needs_revision_msgs,
        u'is_needs_rebase': needs_rebase,
        u'is_needs_rebase_msgs': needs_rebase_msgs,
        u'has_commit_mention': has_commit_mention,
        u'has_commit_mention_notification': has_commit_mention_notification,
        u'has_shippable': has_shippable,
        u'has_landscape': has_landscape,
        u'has_travis': has_travis,
        u'has_travis_notification': has_travis_notification,
        u'has_zuul': has_zuul,
        u'merge_commits': merge_commits,
        u'has_merge_commit_notification': has_merge_commit_notification,
        u'mergeable': None,
        u'mergeable_state': mstate,
        u'change_requested': change_requested,
        u'ci_state': ci_state,
        u'ci_stale': ci_stale,
        u'reviews': None,
        #'www_reviews': None,
        #'www_summary': None,
        u'ready_for_review': ready_for_review,
        u'has_shippable_yaml': has_shippable_yaml,
        u'has_shippable_yaml_notification': has_shippable_yaml_notification,
        u'has_remote_repo': has_remote_repo,
        u'stale_reviews': stale_reviews,
        u'has_multiple_modules': has_multiple_modules,
        u'needs_multiple_new_modules_notification': needs_multiple_new_modules_notification
    }

    if not iw.is_pullrequest():
        return rmeta

    bpcs = iw.history.get_boilerplate_comments()
    bpcs = [x[0] for x in bpcs]

    # Scrape web data for debug purposes
    #rfn = iw.repo_full_name
    #www_summary = triager.gws.get_single_issue_summary(rfn, iw.number)
    #www_reviews = triager.gws.scrape_pullrequest_review(rfn, iw.number)

    maintainers = [x for x in triager.ansible_core_team
                   if x not in triager.BOTNAMES]

    #if meta.get('module_match'):
    #    maintainers += meta['module_match'].get('maintainers', [])
    maintainers += meta.get(u'component_maintainers', [])

    # get the exact state from shippable ...
    #   success/pending/failure/... ?
    ci_status = iw.pullrequest_status

    # check if this has shippable and or travis
    if ci_status:
        for x in ci_status:
            if u'travis-ci.org' in x[u'target_url']:
                has_travis = True
                continue
            if x.get('context') == 'Shippable':
                has_shippable = True
                continue

    # code quality hooks
    if [x for x in ci_status if isinstance(x, dict) and
            u'landscape.io' in x[u'target_url']]:
        has_landscape = True

    if [x for x in ci_status if isinstance(x, dict) and
            u'zuul' in x[u'target_url']]:
        has_zuul = True

    ci_states = [x[u'state'] for x in ci_status
                 if isinstance(x, dict) and x.get('context') == 'Shippable']

    if not ci_states:
        ci_state = None
    else:
        ci_state = ci_states[0]
    logging.info(u'ci_state == %s' % ci_state)

    # decide if the CI run is "stale"
    if not has_shippable:
        ci_stale = True
    else:
        # https://github.com/ansible/ansibullbot/issues/935
        ci_date = get_last_shippable_full_run_date(ci_status, shippable)

        # https://github.com/ansible/ansibullbot/issues/458
        if ci_date:
            ci_date = datetime.datetime.strptime(ci_date, u'%Y-%m-%dT%H:%M:%S.%fZ')
            ci_delta = (datetime.datetime.now() - ci_date).days
            if ci_delta > 7:
                ci_stale = True
            else:
                ci_stale = False
        else:
            ci_stale = False

    # clean/unstable/dirty/unknown
    mstate = iw.mergeable_state
    if not mstate:
        mstate = u'unknown'
    logging.info(u'mergeable_state == %s' % mstate)

    # clean/unstable/dirty/unknown
    if mstate != u'clean':

        if ci_state == u'failure':
            needs_revision = True
            needs_revision_msgs.append(u'ci failure')

        if mstate == u'dirty':
            needs_revision = True
            needs_rebase = True
            needs_revision_msgs.append(u'mergeable state is dirty')
            needs_rebase_msgs.append(u'mergeable state is dirty')

        elif mstate == u'unknown':
            # if tests are still running, this needs to be ignored.
            if ci_state not in [u'pending']:
                needs_revision = True
                needs_revision_msgs.append(u'mergeable state is unknown')
                needs_rebase = True
                needs_rebase_msgs.append(u'mergeable state is unknown')

        elif mstate == u'unstable':
            # reduce the label churn
            if ci_state == u'pending' and u'needs_revision' in iw.labels:
                needs_revision = True
                needs_rebase_msgs.append(u'keep label till test finished')

    else:

        #current_hash = None
        #pending_reviews = []
        #hash_reviews = {}
        user_reviews = {}
        shipits = {}  # key: actor, value: created_at

        has_set_needs_revision = set()

        for event in iw.history.history:

            if event[u'actor'] in triager.BOTNAMES:
                continue

            if event[u'actor'] in maintainers and \
                    event[u'actor'] != iw.submitter:

                if event[u'event'] == u'labeled':
                    if event[u'label'] == u'needs_revision':
                        needs_revision = True
                        needs_revision_msgs.append(
                            u'[%s] labeled' % event[u'actor']
                        )
                        has_set_needs_revision.add(event[u'actor'])
                        continue

                if event[u'event'] == u'unlabeled':
                    if event[u'label'] == u'needs_revision':
                        needs_revision = False
                        needs_revision_msgs.append(
                            u'[%s] unlabeled' % event[u'actor']
                        )
                        continue

                if event[u'event'] == u'commented':

                    if is_approval(event[u'body']):
                        shipits[event[u'actor']] = event[u'created_at']

                    if u'!needs_revision' in event[u'body']:
                        needs_revision = False
                        needs_revision_msgs.append(
                            u'[%s] !needs_revision' % event[u'actor']
                        )
                        continue

                    if u'needs_revision' in event[u'body'] and \
                            u'!needs_revision' not in event[u'body']:
                        needs_revision = True
                        needs_revision_msgs.append(
                            u'[%s] needs_revision' % event[u'actor']
                        )
                        has_set_needs_revision.add(event[u'actor'])
                        continue

                    if u'shipit' in event[u'body'].lower():
                        if event[u'actor'] in has_set_needs_revision:
                            has_set_needs_revision.remove(event[u'actor'])
                            if not has_set_needs_revision:
                                needs_revision = False
                                continue

            if event[u'actor'] == iw.submitter:
                if event[u'event'] == u'commented':
                    if u'ready_for_review' in event[u'body']:
                        if ready_for_review is None or event[u'created_at'] > ready_for_review:
                            ready_for_review = event[u'created_at']
                        needs_revision = False
                        needs_revision_msgs.append(
                            u'[%s] ready_for_review' % event[u'actor']
                        )
                        continue
                    if u'shipit' in event[u'body'].lower():
                        #ready_for_review = True
                        if ready_for_review is None or event[u'created_at'] > ready_for_review:
                            ready_for_review = event[u'created_at']
                        needs_revision = False
                        needs_revision_msgs.append(
                            u'[%s] shipit' % event[u'actor']
                        )
                        continue

        # This is a complicated algo ... sigh
        user_reviews = get_review_state(
            iw.reviews,
            iw.submitter,
            number=iw.number,
            store=True
        )

        if user_reviews:
            last_commit = iw.commits[-1].sha
            change_requested = changes_requested_by(user_reviews, shipits, last_commit, ready_for_review)
            if change_requested:
                needs_revision = True
                needs_revision_msgs.append(
                    u'outstanding reviews: %s' % u','.join(change_requested)
                )

    # Merge commits are bad, force a rebase
    if iw.merge_commits:
        needs_rebase = True

        for mc in iw.merge_commits:
            merge_commits.append(mc.html_url)
            needs_rebase_msgs.append(u'merge commit %s' % mc.commit.sha)

        if u'merge_commit_notify' not in bpcs:
            has_merge_commit_notification = False
        else:
            mc_comments = iw.history.search_user_comments(
                triager.BOTNAMES,
                u'boilerplate: merge_commit_notify'
            )
            last_mc_comment = mc_comments[-1]
            mc_missing = []
            for mc in iw.merge_commits:
                if mc.html_url not in last_mc_comment:
                    mc_missing.append(mc)
            if mc_missing:
                has_merge_commit_notification = False
            else:
                has_merge_commit_notification = True

    # Count committers
    committer_count = len(sorted(set(iw.committer_emails)))

    # we don't like @you in the commit messages
    # https://github.com/ansible/ansibullbot/issues/375
    for x in iw.commits:
        words = x.commit.message.split()
        if not words:
            continue
        if [x for x in words if x.startswith(u'@') and not x.endswith(u'@')]:
            has_commit_mention = True
            needs_revision = True
            needs_revision_msgs.append(u'@ in commit message')
            break

    # make sure they're notified about the problem
    if has_commit_mention:
        if u'commit_msg_mentions' in bpcs:
            has_commit_mention_notification = True

    if has_travis:
        needs_rebase = True
        needs_rebase_msgs.append(u'travis-ci found in status')

        # 'has_travis_notification': has_travis_notification,
        if u'travis_notify' in bpcs:
            has_travis_notification = True
        else:
            has_travis_notification = False

    # keep track of who deleted their repo/branch
    if iw.pullrequest.head.repo:
        has_remote_repo = True
    else:
        has_remote_repo = False

    # https://github.com/ansible/ansibullbot/issues/406
    has_shippable_yaml = iw.pullrequest_filepath_exists(u'shippable.yml')
    if not has_shippable_yaml:
        needs_rebase = True
        needs_rebase_msgs.append(u'missing shippable.yml')
        if u'no_shippable_yaml' in bpcs:
            has_shippable_yaml_notification = True
        else:
            has_shippable_yaml_notification = False

    # stale reviews
    if user_reviews:

        now = pytz.utc.localize(datetime.datetime.now())
        commits = [x for x in iw.history.history if x[u'event'] == u'committed']
        lc_date = commits[-1][u'created_at']

        stale_reviews = {}
        for actor, review in user_reviews.items():
            if review[u'state'] != u'CHANGES_REQUESTED':
                continue
            lrd = None
            for x in iw.history.history:
                if x[u'actor'] != actor:
                    continue
                if x[u'event'] == u'review_changes_requested':
                    if not lrd or lrd < x[u'created_at']:
                        lrd = x[u'created_at']
                elif x[u'event'] == u'commented' and is_approval(x[u'body']):
                    if lrd and lrd < x[u'created_at']:
                        lrd = None

            if lrd:

                age = (now - lc_date).days
                delta = (lc_date - lrd).days
                if (lc_date > lrd) and (age > 7):
                    stale_reviews[actor] = {
                        u'age': age,
                        u'delta': delta,
                        u'review_date': lrd.isoformat(),
                        u'commit_date': lc_date.isoformat()
                    }

    # https://github.com/ansible/ansibullbot/issues/302
    if len(iw.new_modules) > 1:
        has_multiple_modules = True
        if u'multiple_module_notify' not in bpcs:
            needs_multiple_new_modules_notification = True
        needs_revision = True
        needs_revision_msgs.append(u'multiple new modules')

    logging.info(u'mergeable_state is %s' % mstate)
    logging.info(u'needs_rebase is %s' % needs_rebase)
    logging.info(u'needs_revision is %s' % needs_revision)
    logging.info(u'ready_for_review is %s' % ready_for_review)

    rmeta = {
        u'committer_count': committer_count,
        u'is_needs_revision': needs_revision,
        u'is_needs_revision_msgs': needs_revision_msgs,
        u'is_needs_rebase': needs_rebase,
        u'is_needs_rebase_msgs': needs_rebase_msgs,
        u'has_shippable': has_shippable,
        u'has_landscape': has_landscape,
        u'has_travis': has_travis,
        u'has_travis_notification': has_travis_notification,
        u'has_commit_mention': has_commit_mention,
        u'has_commit_mention_notification': has_commit_mention_notification,
        u'merge_commits': merge_commits,
        u'has_merge_commit_notification': has_merge_commit_notification,
        u'mergeable': iw.pullrequest.mergeable,
        u'mergeable_state': mstate,
        u'change_requested': change_requested,
        u'ci_state': ci_state,
        u'ci_stale': ci_stale,
        u'reviews': iw.reviews,
        #'www_summary': www_summary,
        #'www_reviews': www_reviews,
        u'ready_for_review_date': ready_for_review,
        u'ready_for_review': bool(ready_for_review),
        u'has_shippable_yaml': has_shippable_yaml,
        u'has_shippable_yaml_notification': has_shippable_yaml_notification,
        u'has_remote_repo': has_remote_repo,
        u'stale_reviews': stale_reviews,
        u'has_multiple_modules': has_multiple_modules,
        u'needs_multiple_new_modules_notification': needs_multiple_new_modules_notification
    }
    if rmeta[u'ready_for_review_date']:
        rmeta[u'ready_for_review_date'] = rmeta[u'ready_for_review_date'].isoformat()

    return rmeta