blob: d6befe7ee90859f431ec087c6a322981ff94cc25 [file] [log] [blame]
# Copyright 2018 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Functions for forgiving false Pre-CQ rejections."""
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
from chromite.lib import cros_logging as logging
from exonerator import gerrit_cq
from exonerator import constants
_TRYBOT_READY_LABEL = 'Trybot-Ready'
_EXONERATOR_PRECQ_MESSAGE = """\
CL-Exonerator has triggered a Pre-CQ retry on this CL due to a Pre-CQ sanity
failure.
"""
_REPOSITORY_WHITELIST = frozenset(['infra/dummies/general-sandbox'])
def MaybeExonerate(change, build_id):
"""Mark a CL as Trybot-Ready only if it hasn't already been.
Args:
change: A GerritPatchTuple.
build_id: The CIDB id for the build which the change was kicked out of.
"""
helper = gerrit_cq.GetHelper(internal=change.internal)
details = helper.GetChangeDetail(change.gerrit_number)
if not details:
logging.warning('Change details was None for %s', str(change))
return False
if not _ShouldBeMarkedPreCQReady(details, change.patch_number):
logging.info(
"Change %s was not ready to exonerate %d", str(change), build_id)
return False
if not details['project'] in _REPOSITORY_WHITELIST:
logging.info(
'Change %s has project %s, which is not whitelisted.',
details, details['project'])
try:
helper.SetReview(
change.gerrit_number,
msg=_EXONERATOR_PRECQ_MESSAGE,
labels={_TRYBOT_READY_LABEL: 1})
return True
# Network errors or permission errors won't get retried. That's ok for now.
except Exception:
logging.exception('Encountered an exception while marking %s with CQ+1',
str(change))
return False
def CanBeMarkedReady(change):
"""Whether a change is ready to be exonerated.
Args:
change: A GerritPatchTuple to test.
"""
helper = gerrit_cq.GetHelper(internal=change.internal)
details = helper.GetChangeDetail(change.gerrit_number)
if details:
return _ShouldBeMarkedPreCQReady(details, change.patch_number)
else:
logging.warning('Change details was None for %s', str(change))
return False
def _ShouldBeMarkedPreCQReady(change_details, known_patch_number):
"""Whether a CL should be re-marked as Trybot-Ready.
We only want to send CLs back into the pre-cq if they haven't already been
marked Trybot-Ready+1, CQ+1, and it's not already on a newer patchset.
Args:
change_details: The change details response from Gerrit.
known_patch_number: The revision number which we want to forgive.
"""
return (
gerrit_cq.SanityCheckChange(change_details, known_patch_number)
# It shouldn't already have Trybot-Ready+1, CQ+1 or CQ-1
and not (gerrit_cq.LabelValues(
change_details, gerrit_cq.COMMIT_QUEUE_LABEL) - {0})
and not (gerrit_cq.LabelValues(
change_details, _TRYBOT_READY_LABEL) - {0})
# Don't exonerate CLs with Code-Review-1 or Code-Review-2
and all(label >= 0
for label in gerrit_cq.LabelValues(
change_details, gerrit_cq.CODE_REVIEW_LABEL))
and _PreCQApprovalWasRemovedByBot(change_details)
)
def _PreCQApprovalWasRemovedByBot(change_details):
"""Whether the latest Pre-CQ Approval was removed by a bot.
Either Trybot-Ready+1 or Commit-Queue+1 labels can kick off pre-cq. To
check whether the change was kicked out by a bot, check the latest message
which removes either Trybot-Ready or CQ+1, and see whether it was made by
chromeos-commit-bot@chromium.org.
Args:
change_details: The response from gerrit with the change's details.
"""
for message in reversed(change_details['messages']):
if (('-' + _TRYBOT_READY_LABEL) in message['message']
or '-Commit-Queue' in message['message']):
return (message['real_author']['email']
== constants.CHROMEOS_COMMIT_BOT_EMAIL)
return False