Ejemplo n.º 1
0
class InboundHandler(BisectorHandler):
    create_range = staticmethod(range_for_inbounds)

    def _print_progress(self, new_data):
        LOG.info("Narrowed inbound regression window from [%s, %s]"
                 " (%d builds) to [%s, %s] (%d builds)"
                 " (~%d steps left)" %
                 (self.build_range[0].short_changeset,
                  self.build_range[-1].short_changeset, len(self.build_range),
                  new_data[0].short_changeset, new_data[-1].short_changeset,
                  len(new_data), compute_steps_left(len(new_data))))

    def user_exit(self, mid):
        words = self._reverse_if_find_fix('Newest', 'Oldest')
        LOG.info('%s known good inbound revision: %s' %
                 (words[0], self.good_revision))
        LOG.info('%s known bad inbound revision: %s' %
                 (words[1], self.bad_revision))

    def _choose_integration_branch(self, changeset):
        """
        Tries to determine which integration branch the given changeset
        originated from by checking the date the changeset first showed up
        in each repo. The repo with the earliest date is chosen.
        """
        landings = {}
        for k in ("autoland", "mozilla-inbound"):
            jp = JsonPushes(k)

            try:
                push = jp.push(changeset, full='1')
                landings[k] = push.timestamp
            except EmptyPushlogError:
                LOG.debug("Didn't find %s in %s" % (changeset, k))

        repo = min(landings, key=landings.get)
        LOG.debug("Repo '%s' seems to have the earliest push" % repo)
        return repo

    def handle_merge(self):
        # let's check if we are facing a merge, and in that case,
        # continue the bisection from the merged branch.
        result = None

        LOG.debug("Starting merge handling...")
        # we have to check the commit of the most recent push
        most_recent_push = self.build_range[1]
        jp = JsonPushes(most_recent_push.repo_name)
        push = jp.push(most_recent_push.changeset, full='1')
        msg = push.changeset['desc']
        LOG.debug("Found commit message:\n%s\n" % msg)
        branch = find_branch_in_merge_commit(msg, most_recent_push.repo_name)
        if not (branch and len(push.changesets) >= 2):
            # We did not find a branch, lets check the integration branches if we are bisecting m-c
            LOG.debug(
                "Did not find a branch, checking all integration branches")
            if get_name(most_recent_push.repo_name) == 'mozilla-central' and \
               len(push.changesets) >= 2:
                branch = self._choose_integration_branch(
                    most_recent_push.changeset)
                jp2 = JsonPushes(branch)
                try:
                    data = jp2.pushes_within_changes(
                        push.changesets[0]['node'],
                        push.changesets[-1]['node'])
                except MozRegressionError, exc:
                    LOG.error(
                        "Failed to find changes in branch '%s' (error: %s)" %
                        (branch, exc))
                    raise
                LOG.info("************* Switching to %s by"
                         " process of elimination (no branch detected in"
                         " commit message)" % branch)
                gr, br = self._reverse_if_find_fix(data[0].changeset,
                                                   data[-1].changeset)
                return (branch, gr, br)
            else:
                return
        try:
            # so, this is a merge. see how many changesets are in it, if it
            # is just one, we have our answer
            if len(push.changesets) == 2:
                LOG.info("Merge commit has only two revisions (one of which "
                         "is the merge): we are done")
                return

            # Otherwise, we can find the oldest and youngest
            # changesets, and the branch where the merge comes from.
            oldest = push.changesets[0]['node']
            # exclude the merge commit
            youngest = push.changesets[-2]['node']
            LOG.debug("This is a merge from %s" % branch)

            # we can't use directly the youngest changeset because we
            # don't know yet if it is good.
            #
            # PUSH1    PUSH2
            # [1 2] [3 4 5 6 7]
            #    G    MERGE  B
            #
            # so first, grab it. This needs to be done on the right branch.
            jp2 = JsonPushes(branch)
            raw = [
                int(p.push_id)
                for p in jp2.pushes_within_changes(oldest, youngest)
            ]
            data = jp2.pushes(
                startID=str(min(raw) - 2),
                endID=str(max(raw)),
            )

            oldest = data[0].changesets[0]
            youngest = data[-1].changesets[-1]

            # we are ready to bisect further
            LOG.info("************* Switching to %s" % branch)
            gr, br = self._reverse_if_find_fix(oldest, youngest)
            result = (branch, gr, br)
        except MozRegressionError:
            LOG.debug("Got exception", exc_info=True)
            raise MozRegressionError(
                "Unable to exploit the merge commit. Origin branch is {}, and"
                " the commit message for {} was:\n{}".format(
                    most_recent_push.repo_name,
                    most_recent_push.short_changeset, msg))
        LOG.debug('End merge handling')
        return result
Ejemplo n.º 2
0
 def __init__(self, fetch_config):
     InfoFetcher.__init__(self, fetch_config)
     options = fetch_config.tk_options()
     self.index = taskcluster.client.Index(options)
     self.queue = taskcluster.Queue(options)
     self.jpushes = JsonPushes(branch=fetch_config.inbound_branch)
Ejemplo n.º 3
0
    def handle_merge(self):
        # let's check if we are facing a merge, and in that case,
        # continue the bisection from the merged branch.
        result = None

        LOG.debug("Starting merge handling...")
        # we have to check the commit of the most recent push
        most_recent_push = self.build_range[1]
        jp = JsonPushes(most_recent_push.repo_name)
        push = jp.push(most_recent_push.changeset, full='1')
        msg = push.changeset['desc']
        LOG.debug("Found commit message:\n%s\n" % msg)
        branch = find_branch_in_merge_commit(msg, most_recent_push.repo_name)
        if not (branch and len(push.changesets) >= 2):
            # We did not find a branch, lets check the integration branches if we are bisecting m-c
            LOG.debug(
                "Did not find a branch, checking all integration branches")
            if get_name(most_recent_push.repo_name) == 'mozilla-central' and \
               len(push.changesets) >= 2:
                branch = self._choose_integration_branch(
                    most_recent_push.changeset)
                oldest = push.changesets[0]['node']
                youngest = push.changesets[-1]['node']
                LOG.info("************* Switching to %s by"
                         " process of elimination (no branch detected in"
                         " commit message)" % branch)
            else:
                return
        else:
            # so, this is a merge. see how many changesets are in it, if it
            # is just one, we have our answer
            if len(push.changesets) == 2:
                LOG.info("Merge commit has only two revisions (one of which "
                         "is the merge): we are done")
                return

            # Otherwise, we can find the oldest and youngest
            # changesets, and the branch where the merge comes from.
            oldest = push.changesets[0]['node']
            # exclude the merge commit
            youngest = push.changesets[-2]['node']
            LOG.info("************* Switching to %s" % branch)

        # we can't use directly the oldest changeset because we
        # don't know yet if it is good.
        #
        # PUSH1    PUSH2
        # [1 2] [3 4 5 6 7]
        #    G    MERGE  B
        #
        # so first grab the previous push to get the last known good
        # changeset. This needs to be done on the right branch.
        try:
            jp2 = JsonPushes(branch)
            raw = [
                int(p.push_id)
                for p in jp2.pushes_within_changes(oldest, youngest)
            ]
            data = jp2.pushes(
                startID=str(min(raw) - 2),
                endID=str(max(raw)),
            )

            older = data[0].changeset
            youngest = data[-1].changeset

            # we are ready to bisect further
            gr, br = self._reverse_if_find_fix(older, youngest)
            result = (branch, gr, br)
        except MozRegressionError:
            LOG.debug("Got exception", exc_info=True)
            raise MozRegressionError(
                "Unable to exploit the merge commit. Origin branch is {}, and"
                " the commit message for {} was:\n{}".format(
                    most_recent_push.repo_name,
                    most_recent_push.short_changeset, msg))
        LOG.debug('End merge handling')
        return result
Ejemplo n.º 4
0
 def _bisect_integration(self, good_rev, bad_rev, ensure_good_and_bad=False, expand=0):
     LOG.info(
         "Getting %s builds between %s and %s"
         % (self.fetch_config.integration_branch, good_rev, bad_rev)
     )
     handler = IntegrationHandler(
         find_fix=self.options.find_fix, ensure_good_and_bad=ensure_good_and_bad
     )
     result = self._do_bisect(handler, good_rev, bad_rev, expand=expand)
     if result == Bisection.FINISHED:
         LOG.info("No more integration revisions, bisection finished.")
         handler.print_range()
         if handler.good_revision == handler.bad_revision:
             LOG.warning(
                 "It seems that you used two changesets that are in"
                 " the same push. Check the pushlog url."
             )
         elif len(handler.build_range) == 2:
             # range reduced to 2 pushes (at least ones with builds):
             # one good, one bad.
             result = handler.handle_merge()
             if result:
                 branch, good_rev, bad_rev = result
                 self.fetch_config.set_repo(branch)
                 return self._bisect_integration(good_rev, bad_rev, expand=DEFAULT_EXPAND)
             else:
                 # This code is broken, it prints out the message even when
                 # there are multiple bug numbers or commits in the range.
                 # Somebody should fix it before re-enabling it.
                 return 0
                 # print a bug if:
                 # (1) there really is only one bad push (and we're not
                 # just missing the builds for some intermediate builds)
                 # (2) there is only one bug number in that push
                 jp = JsonPushes(handler.build_range[1].repo_name)
                 num_pushes = len(
                     jp.pushes_within_changes(
                         handler.build_range[0].changeset, handler.build_range[1].changeset,
                     )
                 )
                 if num_pushes == 2:
                     bugids = find_bugids_in_push(
                         handler.build_range[1].repo_name, handler.build_range[1].changeset,
                     )
                     if len(bugids) == 1:
                         word = "fix" if handler.find_fix else "regression"
                         LOG.info(
                             "Looks like the following bug has the "
                             " changes which introduced the"
                             " {}:\n{}".format(word, bug_url(bugids[0]))
                         )
     elif result == Bisection.USER_EXIT:
         self._print_resume_info(handler)
     else:
         # NO_DATA. With integration branches, this can not happen if changesets
         # are incorrect - so builds are probably too old
         LOG.info(
             "There are no build artifacts for these changesets (they are probably too old)."
         )
         return 1
     return 0
Ejemplo n.º 5
0
 def __init__(self, fetch_config):
     InfoFetcher.__init__(self, fetch_config)
     self.jpushes = JsonPushes(branch=fetch_config.integration_branch)
Ejemplo n.º 6
0
    def handle_merge(self):
        # let's check if we are facing a merge, and in that case,
        # continue the bisection from the merged branch.
        result = None

        LOG.debug("Starting merge handling...")
        # we have to check the commit of the most recent push
        most_recent_push = self.build_range[1]
        jp = JsonPushes(most_recent_push.repo_name)
        push = jp.push(most_recent_push.changeset, full='1')
        msg = push.changeset['desc']
        LOG.debug("Found commit message:\n%s\n" % msg)
        branch = find_branch_in_merge_commit(msg)
        if not (branch and len(push.changesets) >= 2):
            # So we did not found a branch. Let's try with inbound anyway
            if get_category(most_recent_push.repo_name) != 'integration' and \
               len(push.changesets) >= 2:
                jp2 = JsonPushes("mozilla-inbound")
                try:
                    data = jp2.pushes_within_changes(
                        push.changesets[0]['node'],
                        push.changesets[-1]['node'])
                except MozRegressionError:
                    return
                LOG.info("************* Switching to mozilla-inbound by"
                         " default (no branch detected in commit message)")
                return ('mozilla-inbound', data[0].changeset,
                        data[-1].changeset)
            else:
                return
        try:
            # so, this is a merge. We can find the oldest and youngest
            # changesets, and the branch where the merge comes from.
            oldest = push.changesets[0]['node']
            # exclude the merge commit
            youngest = push.changesets[-2]['node']
            LOG.debug("This is a merge from %s" % branch)

            # we can't use directly the youngest changeset because we
            # don't know yet if it is good.
            #
            # PUSH1    PUSH2
            # [1 2] [3 4 5 6 7]
            #    G    MERGE  B
            #
            # so first, grab it. This needs to be done on the right branch.
            jp2 = JsonPushes(branch)
            raw = [
                int(p.push_id)
                for p in jp2.pushes_within_changes(oldest, youngest)
            ]
            data = jp2.pushes(
                startID=str(min(raw) - 2),
                endID=str(max(raw)),
            )

            oldest = data[0].changesets[0]
            youngest = data[-1].changesets[-1]

            # we are ready to bisect further
            LOG.info("************* Switching to %s" % branch)
            gr, br = self._reverse_if_find_fix(oldest, youngest)
            result = (branch, gr, br)
        except MozRegressionError:
            LOG.debug("Got exception", exc_info=True)
            raise MozRegressionError(
                "Unable to exploit the merge commit. Origin branch is {}, and"
                " the commit message for {} was:\n{}".format(
                    most_recent_push.repo_name,
                    most_recent_push.short_changeset, msg))
        LOG.debug('End merge handling')
        return result