def SetBuilderGraphURL(self, step_name, build_status):
   """Stores the graph URL used in emails for this builder."""
   builder_name = build_status.getBuilder().getName()
   builder_results = self.GetEmailResults(builder_name)
   graph_url = GetGraphURL(step_name, build_status)
   latest_revision = build_utils.getLatestRevision(build_status)
   if latest_revision:
     graph_url = SetQueryParameter(graph_url, 'rev', latest_revision)
   builder_results[GRAPH_URL] = graph_url
Example #2
0
 def SetBuilderGraphURL(self, step_name, build_status):
     """Stores the graph URL used in emails for this builder."""
     builder_name = build_status.getBuilder().getName()
     builder_results = self.GetEmailResults(builder_name)
     graph_url = GetGraphURL(step_name, build_status)
     latest_revision = build_utils.getLatestRevision(build_status)
     if latest_revision:
         graph_url = SetQueryParameter(graph_url, 'rev', latest_revision)
     builder_results[GRAPH_URL] = graph_url
 def GetEmailSubject(self, builder_name, build_status, results, step_name):
   """Returns the subject of for an email based on perf results."""
   project_name = self.master_status.getTitle()
   latest_revision = build_utils.getLatestRevision(build_status)
   result = 'changes'
   builders = [builder_name]
   if self.combine_results:
     builders = self.new_email_results.keys()
   return ('%s %s on %s, revision %s' %
           (project_name, result, ', '.join(builders), str(latest_revision)))
Example #4
0
  def getFinishedMessage(self, result, builder_name, build_status, step_name):
    """Closes the tree."""
    GateKeeper.msg('Trying to close the tree at %s.' % self.tree_status_url)
    repository_url = build_status.getProperty('repository')
    git_repo = self.getGitRepo(repository_url)
    # isInterestingStep verified that latest_revision has expected properties.
    latest_revision = build_utils.getLatestRevision(build_status, git_repo)

    if not self.tree_status_url:
      self._last_closure_revision = latest_revision
      return

    if os.path.exists('.suppress_gatekeeper'):
      GateKeeper.msg('Suppression file is in place, will not close for '
              'rev %s' % str(latest_revision))
      self._last_closure_revision = latest_revision
      return

    # Don't blame last committers if they are not to blame.
    blame_text = ''
    if self.shouldBlameCommitters(step_name):
      blame_text = (' from %s: %s' %
                    (str(latest_revision),
                     ', '.join(build_status.getResponsibleUsers())))

    # Post a request to close the tree.
    tree_message = self.tree_message % {
        'steps': step_name,
        'builder': builder_name,
        'blame': blame_text
    }
    params = urllib.urlencode(
        {
          'message': tree_message,
          'username': '******',
          'password': self.password,
        })

    def success(result):
      GateKeeper.msg('Tree closed successfully at rev %s' %
              str(latest_revision))
      self._last_closure_revision = latest_revision

    def failure(result):
      GateKeeper.msg('Failed to close the tree at rev %s' %
              str(latest_revision))

    # Trigger the HTTP POST request to update the tree status.
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    connection = client.getPage(self.tree_status_url, method='POST',
                                postdata=params, headers=headers,
                                agent='buildbot')
    connection.addCallbacks(success, failure)
    return connection
Example #5
0
 def GetEmailSubject(self, builder_name, build_status, results, step_name):
     """Returns the subject of for an email based on perf results."""
     project_name = self.master_status.getTitle()
     latest_revision = build_utils.getLatestRevision(build_status)
     result = 'changes'
     builders = [builder_name]
     if self.combine_results:
         builders = self.new_email_results.keys()
     return (
         '%s %s on %s, revision %s' %
         (project_name, result, ', '.join(builders), str(latest_revision)))
Example #6
0
  def isInterestingStep(self, build_status, step_status, results):
    """Look at most cases that could make us ignore the step results.

    Do not look at current tree status here since it's too slow."""
    # If we have not failed, or are not interested in this builder,
    # then we have nothing to do.
    if results[0] != FAILURE:
      return False

    # Check if the slave is still alive. We should not close the tree for
    # inactive slaves.
    slave_name = build_status.getSlavename()
    if slave_name in self.master_status.getSlaveNames():
      # @type self.master_status: L{buildbot.status.builder.Status}
      # @type self.parent: L{buildbot.master.BuildMaster}
      # @rtype getSlave(): L{buildbot.status.builder.SlaveStatus}
      slave_status = self.master_status.getSlave(slave_name)
      if slave_status and not slave_status.isConnected():
        GateKeeper.msg('Slave %s was disconnected, '
                'not closing the tree' % slave_name)
        return False

    # If the previous build step failed with the same result, we don't care
    # about this step.
    previous_build_status = build_status.getPreviousBuild()
    if previous_build_status:
      step_name = self.getName(step_status)
      step_type = self.getGenericName(step_name)
      previous_steps = [step for step in previous_build_status.getSteps()
                        if self.getGenericName(self.getName(step)) == step_type]
      if len(previous_steps) == 1:
        if previous_steps[0].getResults()[0] == FAILURE:
          # The previous same step failed on the previous build. Ignore.
          GateKeeper.msg('Slave %s failed, but previously failed on '
                  'the same step (%s). So not closing tree.' % (
                      (step_name, slave_name)))
          return False
      else:
        GateKeeper.msg('len(previous_steps) == %d which is weird' %
                len(previous_steps))

    # If check_revisions=False that means that the tree closure request is
    # coming from nightly scheduled bots, that need not necessarily have the
    # revision info.
    if not self.check_revisions:
      return True

    # For the rest of the checks, we care about revision numbers, so we need to
    # be aware of if we're operating in Git- or SVN-mode. getGitRepo returns
    # a GitHelper instance if the repository is Git.
    repository_url = build_status.getProperty('repository')
    git_repo = self.getGitRepo(repository_url)

    latest_revision = build_utils.getLatestRevision(build_status, git_repo)

    # If we don't have a version stamp nor a blame list, then this is most
    # likely a build started manually, and we don't want to close the
    # tree.
    if not latest_revision or not build_status.getResponsibleUsers():
      GateKeeper.msg('Slave %s failed, but no version stamp or responsible '
                     'users, so skipping.' % slave_name)
      return False

    # self._last_closure_revision can be a number (svn revision)
    # or a string (git hash). Check for non-zero or non-empty value.
    if self._last_closure_revision:
      # If the tree is open, we don't want to close it again for the same
      # revision, or an earlier one in case the build that just finished is a
      # slow one and we already fixed the problem and manually opened the tree.
      if git_repo:
        this_rev, last_rev = git_repo.number(
            latest_revision, self._last_closure_revision)
        dbg = lambda m: GateKeeper.msg(m, logLevel=logging.DEBUG)
        dbg('Git mode. Hash mapping:')
        dbg('%s -> %s' % (latest_revision, this_rev))
        dbg('%s -> %s' % (self._last_closure_revision, last_rev))
      else:
        this_rev = latest_revision
        last_rev = self._last_closure_revision
      if this_rev <= last_rev:
        GateKeeper.msg('Slave %s failed, but we already closed it '
                'for a previous revision (old=%s, new=%s)' % (
                    slave_name, str(self._last_closure_revision),
                    str(latest_revision)))
        return False

    GateKeeper.msg('Decided to close tree because of slave %s '
            'on revision %s' % (slave_name, str(latest_revision)))

    # Up to here, in theory we'd check if the tree is closed but this is too
    # slow to check here. Instead, take a look only when we want to close the
    # tree.
    return True
Example #7
0
  def isInterestingStep(self, build_status, step_status, results):
    """Look at most cases that could make us ignore the step results.
    """
    # If the base class thinks we're not interesting -> skip it.
    if not chromium_notifier.ChromiumNotifier.isInterestingStep(
        self, build_status, step_status, results):
      return False

    # Check if the slave is still alive. We should not close the tree for
    # inactive slaves.
    slave_name = build_status.getSlavename()
    if slave_name in self.master_status.getSlaveNames():
      # @type self.master_status: L{buildbot.status.builder.Status}
      # @type self.parent: L{buildbot.master.BuildMaster}
      # @rtype getSlave(): L{buildbot.status.builder.SlaveStatus}
      slave_status = self.master_status.getSlave(slave_name)
      if slave_status and not slave_status.isConnected():
        log.msg('[failurenotifier] Slave %s was disconnected, '
                'not sending a warning' % slave_name)
        return False

    # If all the failure_ids were observed in older builds then this
    # failure is not interesting. Also, store the current failure.
    has_failures_to_report = False
    for l in step_status.getLogs():
      failure = l.getName()  # stdio or suppression hash or failed test or ?

      # TODO(timurrrr): put the gtest regexp into a common place.
      if (not re.match(r'^[\dA-F]{16}$', failure) and
          not re.match(r'((\w+/)?\w+\.\w+(/\d+)?)', failure)):  # gtest name
        if failure != 'stdio':
          log.msg('[failurenotifier] Log `%s` is ignored since doesn\'t look '
                  'like a memory suppression hash or test failure.' % failure)
        continue

      if '.FLAKY_' in failure or '.FAILS_' in failure:
        log.msg('[failurenotifier] Ignoring flaky/fails tests: `%s`' % failure)
        continue

      if (self.recent_failures.GetCount(failure) <
          self._IGNORE_FAILURES_THRESHOLD):
        has_failures_to_report = True
        log.msg('[failurenotifier] Failure `%s` '
                'is interesting' % failure)
      else:
        log.msg('[failurenotifier] Failure `%s` '
                'is not interesting - happened to often recently' % failure)
      self.recent_failures.Put(failure)

    # If we don't have a version stamp nor a blame list, then this is most
    # likely a build started manually, and we don't want to issue a warning.
    #
    # This code is intentionally put after Put calls so we don't send failure
    # notifications with the wrong blamelist once bots cycle for the second time
    # after a master restart.
    latest_revision = build_utils.getLatestRevision(build_status)
    if not latest_revision or not build_status.getResponsibleUsers():
      log.msg('[failurenotifier] Slave %s failed, but no version stamp, '
              'so skipping.' % slave_name)
      return False

    if has_failures_to_report:
      log.msg('[failurenotifier] Decided to send a warning because of slave %s '
              'on revision %s' % (slave_name, str(latest_revision)))
      return True
    else:
      log.msg('[failurenotifier] Slave %s revision %s has no interesting '
              'failures' % (slave_name, str(latest_revision)))
      return False
    def isInterestingStep(self, build_status, step_status, results):
        """Look at most cases that could make us ignore the step results.
    """
        # If the base class thinks we're not interesting -> skip it.
        if not chromium_notifier.ChromiumNotifier.isInterestingStep(
                self, build_status, step_status, results):
            return False

        # Check if the slave is still alive. We should not close the tree for
        # inactive slaves.
        slave_name = build_status.getSlavename()
        if slave_name in self.master_status.getSlaveNames():
            # @type self.master_status: L{buildbot.status.builder.Status}
            # @type self.parent: L{buildbot.master.BuildMaster}
            # @rtype getSlave(): L{buildbot.status.builder.SlaveStatus}
            slave_status = self.master_status.getSlave(slave_name)
            if slave_status and not slave_status.isConnected():
                log.msg('[failurenotifier] Slave %s was disconnected, '
                        'not sending a warning' % slave_name)
                return False

        # If all the failure_ids were observed in older builds then this
        # failure is not interesting. Also, store the current failure.
        has_failures_to_report = False
        for l in step_status.getLogs():
            failure = l.getName(
            )  # stdio or suppression hash or failed test or ?

            # TODO(timurrrr): put the gtest regexp into a common place.
            if (not re.match(r'^[\dA-F]{16}$', failure) and not re.match(
                    r'((\w+/)?\w+\.\w+(/\d+)?)', failure)):  # gtest name
                if failure != 'stdio':
                    log.msg(
                        '[failurenotifier] Log `%s` is ignored since doesn\'t look '
                        'like a memory suppression hash or test failure.' %
                        failure)
                continue

            if '.FLAKY_' in failure or '.FAILS_' in failure:
                log.msg('[failurenotifier] Ignoring flaky/fails tests: `%s`' %
                        failure)
                continue

            if (self.recent_failures.GetCount(failure) <
                    self._IGNORE_FAILURES_THRESHOLD):
                has_failures_to_report = True
                log.msg('[failurenotifier] Failure `%s` '
                        'is interesting' % failure)
            else:
                log.msg('[failurenotifier] Failure `%s` '
                        'is not interesting - happened to often recently' %
                        failure)
            self.recent_failures.Put(failure)

        # If we don't have a version stamp nor a blame list, then this is most
        # likely a build started manually, and we don't want to issue a warning.
        #
        # This code is intentionally put after Put calls so we don't send failure
        # notifications with the wrong blamelist once bots cycle for the second time
        # after a master restart.
        latest_revision = build_utils.getLatestRevision(build_status)
        if not latest_revision or not build_status.getResponsibleUsers():
            log.msg('[failurenotifier] Slave %s failed, but no version stamp, '
                    'so skipping.' % slave_name)
            return False

        if has_failures_to_report:
            log.msg(
                '[failurenotifier] Decided to send a warning because of slave %s '
                'on revision %s' % (slave_name, str(latest_revision)))
            return True
        else:
            log.msg(
                '[failurenotifier] Slave %s revision %s has no interesting '
                'failures' % (slave_name, str(latest_revision)))
            return False
Example #9
0
  def isInterestingStep(self, build_status, step_status, results):
    """ Override of gatekeeper.GateKeeper.isInterestingStep:
    http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/master/gatekeeper.py?view=markup

    We modify it to comment out the SVN revision comparision to determine if the
    current build is older because Skia uses commit hashes.
    """
    # If we have not failed, or are not interested in this builder,
    # then we have nothing to do.
    if results[0] != FAILURE:
      return False

    # Check if the slave is still alive. We should not close the tree for
    # inactive slaves.
    slave_name = build_status.getSlavename()
    if slave_name in self.master_status.getSlaveNames():
      # @type self.master_status: L{buildbot.status.builder.Status}
      # @type self.parent: L{buildbot.master.BuildMaster}
      # @rtype getSlave(): L{buildbot.status.builder.SlaveStatus}
      slave_status = self.master_status.getSlave(slave_name)
      if slave_status and not slave_status.isConnected():
        log.msg('[gatekeeper] Slave %s was disconnected, '
                'not closing the tree' % slave_name)
        return False

    # If the previous build step failed with the same result, we don't care
    # about this step.
    previous_build_status = build_status.getPreviousBuild()
    if previous_build_status:
      step_name = self.getName(step_status)
      step_type = self.getGenericName(step_name)
      previous_steps = [step for step in previous_build_status.getSteps()
                        if self.getGenericName(self.getName(step)) == step_type]
      if len(previous_steps) == 1:
        if previous_steps[0].getResults()[0] == FAILURE:
          log.msg('[gatekeeper] Slave %s failed, but previously failed on '
                  'the same step (%s). So not closing tree.' % (
                      (step_name, slave_name)))
          return False
      else:
        log.msg('[gatekeeper] len(previous_steps) == %d which is weird' %
                len(previous_steps))

    # If check_revisions=False that means that the tree closure request is
    # coming from nightly scheduled bots, that need not necessarily have the
    # revision info.
    if not self.check_revisions:
      return True

    # If we don't have a version stamp nor a blame list, then this is most
    # likely a build started manually, and we don't want to close the
    # tree.
    latest_revision = build_utils.getLatestRevision(build_status)
    if not latest_revision or not build_status.getResponsibleUsers():
      log.msg('[gatekeeper] Slave %s failed, but no version stamp, '
              'so skipping.' % slave_name)
      return False

    # If the tree is open, we don't want to close it again for the same
    # revision, or an earlier one in case the build that just finished is a
    # slow one and we already fixed the problem and manually opened the tree.
    ############################### Added by rmistry ###########################
    # rmistry: Commenting out the below SVN revision comparision because Skia
    # uses commit hashes.
    # TODO(rmistry): Figure out how to ensure that previous builds do not close
    # the tree again.
    #
    # if latest_revision <= self._last_closure_revision:
    #   log.msg('[gatekeeper] Slave %s failed, but we already closed it '
    #           'for a previous revision (old=%s, new=%s)' % (
    #               slave_name, str(self._last_closure_revision),
    #               str(latest_revision)))
    #   return False
    ###########################################################################

    log.msg('[gatekeeper] Decided to close tree because of slave %s '
            'on revision %s' % (slave_name, str(latest_revision)))

    # Up to here, in theory we'd check if the tree is closed but this is too
    # slow to check here. Instead, take a look only when we want to close the
    # tree.
    return True
Example #10
0
  def stepFinished(self, build, step, results):
    """A build step has just finished."""
    builder_name = build.getBuilder().getName()

    # For some reason we sometimes get called even if we didn't subscribe.
    if not self.isInterestingBuilder(builder_name):
      log.msg('Was called for %s even if not subscribed' % builder_name)
      return

    if self.use_getname:
      step_text = step.getName()
    else:
      step_text = step.getText()[0]
    # We only need to deal with interesting steps.
    if not self.isInterestingBuildStep(builder_name, build, step_text):
      log.msg('not interested in step %s' % step_text)
      return

    # TODO(maruel): Support git.
    latest_revision = build_utils.getLatestRevision(build)
    if not latest_revision:
      log.msg('no lastest revision for build %s' % build.asDict())
      return

    # If check_revisions=False that means that the tree closure request is
    # coming from nightly scheduled bots or a git poller, that need not
    # necessarily have the revision info or the revision is a hash that cannot
    # be compared.
    if self.check_revisions:
      latest_revision = int(latest_revision)

      # If we already succeeded for a more recent revision,
      # let's just forget about this one.
      if latest_revision <= self.last_known_good_revision:
        log.msg('revision too old')
        return

    # If we already failed for this revision,
    # there is nothing else we need to do.
    if latest_revision in self.failed_revisions:
      assert latest_revision not in self.succeeded_steps
      log.msg('revision already failed')
      return

    # If we have failed, we add this revision to our failure list and flush it
    # from the success dict, if it is there. We also store it on the status
    # server.
    if results[0] == FAILURE:
      log.msg('%s is a failed revision.' % str(latest_revision))
      self.failed_revisions.append(latest_revision)
      # pop() with a default value allows us to remove an element
      # without having to test if it is there in the first place.
      self.succeeded_steps.pop(latest_revision, None)
      self.PostData(revision=latest_revision, success=0,
                    steps_text=step.getText())
      return

    # Now let's add the succeeded steps to our success dict.
    self.succeeded_steps.setdefault(latest_revision, {})
    revision_status = self.succeeded_steps[latest_revision]
    revision_status.setdefault(builder_name, [])
    revision_status[builder_name].append(step_text)

    # We must complete all the requested steps for all builds, before we can
    # store this revision as a successful one and then forget about all
    # previous revisions info.
    for builder in self.getInterestingBuilders():
      if builder not in revision_status:
        log.msg('Still missing builder %s to declare %s a good revision' %
                (builder, str(latest_revision)))
        return
      succeeded_steps = revision_status[builder]
      for required_step in self.getInterestingBuildSteps(builder, build):
        if required_step not in succeeded_steps:
          log.msg('Still missing step %s/%s to declare %s a good revision' %
                  (builder, required_step, str(latest_revision)))
          return

    # Start by remembering this success.
    log.msg('Found LKGR = %s' % latest_revision)
    self.last_known_good_revision = latest_revision

    # Store it on the status server.
    self.PostData(revision=latest_revision, success=1)

    if self.check_revisions:
      # And now cleanup residual information from earlier revisions
      # Iterate through a list of keys to allow removal while we iterate.
      for revision in list(self.succeeded_steps.keys()):
        if revision <= latest_revision:
          del self.succeeded_steps[revision]
      for revision in self.failed_revisions:
        assert revision != latest_revision
        if revision < latest_revision:
          self.failed_revisions.remove(revision)
    else:
      # TODO(maruel): Use LRU discarding. Right now it's a memory leak.
      pass
Example #11
0
  def isInterestingStep(self, build_status, step_status, results):
    """Look at most cases that could make us ignore the step results.

    Do not look at current tree status here since it's too slow."""
    # If we have not failed, or are not interested in this builder,
    # then we have nothing to do.
    if results[0] != FAILURE:
      return False

    # Check if the slave is still alive. We should not close the tree for
    # inactive slaves.
    slave_name = build_status.getSlavename()
    if slave_name in self.master_status.getSlaveNames():
      # @type self.master_status: L{buildbot.status.builder.Status}
      # @type self.parent: L{buildbot.master.BuildMaster}
      # @rtype getSlave(): L{buildbot.status.builder.SlaveStatus}
      slave_status = self.master_status.getSlave(slave_name)
      if slave_status and not slave_status.isConnected():
        log.msg('[gatekeeper] Slave %s was disconnected, '
                'not closing the tree' % slave_name)
        return False

    # If the previous build step failed with the same result, we don't care
    # about this step.
    previous_build_status = build_status.getPreviousBuild()
    if previous_build_status:
      step_name = self.getName(step_status)
      step_type = self.getGenericName(step_name)
      previous_steps = [step for step in previous_build_status.getSteps()
                        if self.getGenericName(self.getName(step)) == step_type]
      if len(previous_steps) == 1:
        if previous_steps[0].getResults()[0] == FAILURE:
          # The previous same step failed on the previous build. Ignore.
          log.msg('[gatekeeper] Slave %s failed, but previously failed on '
                  'the same step (%s). So not closing tree.' % (
                      (step_name, slave_name)))
          return False
      else:
        log.msg('[gatekeeper] len(previous_steps) == %d which is weird' %
                len(previous_steps))

    # If check_revisions=False that means that the tree closure request is
    # coming from nightly scheduled bots, that need not necessarily have the
    # revision info.
    if not self.check_revisions:
      return True

    # If we don't have a version stamp nor a blame list, then this is most
    # likely a build started manually, and we don't want to close the
    # tree.
    latest_revision = build_utils.getLatestRevision(build_status)
    if not latest_revision or not build_status.getResponsibleUsers():
      log.msg('[gatekeeper] Slave %s failed, but no version stamp, '
              'so skipping.' % slave_name)
      return False

    # If the tree is open, we don't want to close it again for the same
    # revision, or an earlier one in case the build that just finished is a
    # slow one and we already fixed the problem and manually opened the tree.
    # TODO(maruel): This is not git-friendly.
    if latest_revision <= self._last_closure_revision:
      log.msg('[gatekeeper] Slave %s failed, but we already closed it '
              'for a previous revision (old=%s, new=%s)' % (
                  slave_name, str(self._last_closure_revision),
                  str(latest_revision)))
      return False

    log.msg('[gatekeeper] Decided to close tree because of slave %s '
            'on revision %s' % (slave_name, str(latest_revision)))

    # Up to here, in theory we'd check if the tree is closed but this is too
    # slow to check here. Instead, take a look only when we want to close the
    # tree.
    return True
Example #12
0
    def isInterestingStep(self, build_status, step_status, results):
        """ Override of gatekeeper.GateKeeper.isInterestingStep:
    http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/master/gatekeeper.py?view=markup

    We modify it to comment out the SVN revision comparision to determine if the
    current build is older because Skia uses commit hashes.
    """
        # If we have not failed, or are not interested in this builder,
        # then we have nothing to do.
        if results[0] != FAILURE:
            return False

        # Check if the slave is still alive. We should not close the tree for
        # inactive slaves.
        slave_name = build_status.getSlavename()
        if slave_name in self.master_status.getSlaveNames():
            # @type self.master_status: L{buildbot.status.builder.Status}
            # @type self.parent: L{buildbot.master.BuildMaster}
            # @rtype getSlave(): L{buildbot.status.builder.SlaveStatus}
            slave_status = self.master_status.getSlave(slave_name)
            if slave_status and not slave_status.isConnected():
                log.msg('[gatekeeper] Slave %s was disconnected, '
                        'not closing the tree' % slave_name)
                return False

        # If the previous build step failed with the same result, we don't care
        # about this step.
        previous_build_status = build_status.getPreviousBuild()
        if previous_build_status:
            step_name = self.getName(step_status)
            step_type = self.getGenericName(step_name)
            previous_steps = [
                step for step in previous_build_status.getSteps()
                if self.getGenericName(self.getName(step)) == step_type
            ]
            if len(previous_steps) == 1:
                if previous_steps[0].getResults()[0] == FAILURE:
                    log.msg(
                        '[gatekeeper] Slave %s failed, but previously failed on '
                        'the same step (%s). So not closing tree.' %
                        ((step_name, slave_name)))
                    return False
            else:
                log.msg(
                    '[gatekeeper] len(previous_steps) == %d which is weird' %
                    len(previous_steps))

        # If check_revisions=False that means that the tree closure request is
        # coming from nightly scheduled bots, that need not necessarily have the
        # revision info.
        if not self.check_revisions:
            return True

        # If we don't have a version stamp nor a blame list, then this is most
        # likely a build started manually, and we don't want to close the
        # tree.
        latest_revision = build_utils.getLatestRevision(build_status)
        if not latest_revision or not build_status.getResponsibleUsers():
            log.msg('[gatekeeper] Slave %s failed, but no version stamp, '
                    'so skipping.' % slave_name)
            return False

        # If the tree is open, we don't want to close it again for the same
        # revision, or an earlier one in case the build that just finished is a
        # slow one and we already fixed the problem and manually opened the tree.
        ############################### Added by rmistry ###########################
        # rmistry: Commenting out the below SVN revision comparision because Skia
        # uses commit hashes.
        # TODO(rmistry): Figure out how to ensure that previous builds do not close
        # the tree again.
        #
        # if latest_revision <= self._last_closure_revision:
        #   log.msg('[gatekeeper] Slave %s failed, but we already closed it '
        #           'for a previous revision (old=%s, new=%s)' % (
        #               slave_name, str(self._last_closure_revision),
        #               str(latest_revision)))
        #   return False
        ###########################################################################

        log.msg('[gatekeeper] Decided to close tree because of slave %s '
                'on revision %s' % (slave_name, str(latest_revision)))

        # Up to here, in theory we'd check if the tree is closed but this is too
        # slow to check here. Instead, take a look only when we want to close the
        # tree.
        return True
Example #13
0
  def stepFinished(self, build, step, results):
    """A build step has just finished."""
    builder_name = build.getBuilder().getName()

    # For some reason we sometimes get called even if we didn't subscribe.
    if not self.isInterestingBuilder(builder_name):
      log.msg('Was called for %s even if not subscribed' % builder_name)
      return

    if self.use_getname:
      step_text = step.getName()
    else:
      step_text = step.getText()[0]
    # We only need to deal with interesting steps.
    if not self.isInterestingBuildStep(builder_name, build, step_text):
      log.msg('not interested in step %s' % step_text)
      return

    # TODO(maruel): Support git.
    latest_revision = build_utils.getLatestRevision(build)
    if not latest_revision:
      log.msg('no lastest revision for build %s' % build.asDict())
      return

    # If check_revisions=False that means that the tree closure request is
    # coming from nightly scheduled bots or a git poller, that need not
    # necessarily have the revision info or the revision is a hash that cannot
    # be compared.
    if self.check_revisions:
      latest_revision = int(latest_revision)

      # If we already succeeded for a more recent revision,
      # let's just forget about this one.
      if latest_revision <= self.last_known_good_revision:
        log.msg('revision too old')
        return

    # If we already failed for this revision,
    # there is nothing else we need to do.
    if latest_revision in self.failed_revisions:
      assert latest_revision not in self.succeeded_steps
      log.msg('revision already failed')
      return

    # If we have failed, we add this revision to our failure list and flush it
    # from the success dict, if it is there. We also store it on the status
    # server.
    if results[0] == FAILURE:
      log.msg('%s is a failed revision.' % str(latest_revision))
      self.failed_revisions.append(latest_revision)
      # pop() with a default value allows us to remove an element
      # without having to test if it is there in the first place.
      self.succeeded_steps.pop(latest_revision, None)
      self.PostData(revision=latest_revision, success=0,
                    steps_text=step.getText())
      return

    # Now let's add the succeeded steps to our success dict.
    self.succeeded_steps.setdefault(latest_revision, {})
    revision_status = self.succeeded_steps[latest_revision]
    revision_status.setdefault(builder_name, [])
    revision_status[builder_name].append(step_text)

    # We must complete all the requested steps for all builds, before we can
    # store this revision as a successful one and then forget about all
    # previous revisions info.
    for builder in self.getInterestingBuilders():
      if builder not in revision_status:
        log.msg('Still missing builder %s to declare %s a good revision' %
                (builder, str(latest_revision)))
        return
      succeeded_steps = revision_status[builder]
      for required_step in self.getInterestingBuildSteps(builder, build):
        if required_step not in succeeded_steps:
          log.msg('Still missing step %s\%s to declare %s a good revision' %
                  (builder, required_step, str(latest_revision)))
          return

    # Start by remembering this success.
    log.msg('Found LKGR = %s' % latest_revision)
    self.last_known_good_revision = latest_revision

    # Store it on the status server.
    self.PostData(revision=latest_revision, success=1)

    if self.check_revisions:
      # And now cleanup residual information from earlier revisions
      # Iterate through a list of keys to allow removal while we iterate.
      for revision in list(self.succeeded_steps.keys()):
        if revision <= latest_revision:
          del self.succeeded_steps[revision]
      for revision in self.failed_revisions:
        assert revision != latest_revision
        if revision < latest_revision:
          self.failed_revisions.remove(revision)
    else:
      # TODO(maruel): Use LRU discarding. Right now it's a memory leak.
      pass