bot/code_review_bot/analysis.py (145 lines of code) (raw):
import structlog
from libmozdata.phabricator import BuildState, UnitResult, UnitResultState
from libmozevent.phabricator import PhabricatorBuild, PhabricatorBuildState
logger = structlog.get_logger(__name__)
LANDO_WARNING_MESSAGE = "Static analysis and linting are still in progress."
LANDO_FAILURE_MESSAGE = (
"Static analysis and linting did not run due to a generic failure."
)
LANDO_FAILURE_HG_MESSAGE = (
"Static analysis and linting did not run due to failure in applying the patch."
)
class RevisionBuild(PhabricatorBuild):
"""
Convert the bot revision into a libmozevent compatible build
"""
def __init__(self, revision):
# State should be updated to Public
self.state = PhabricatorBuildState.Queued
# Incremented on an unexpected failure during build's push to try
self.retries = 0
# Revision used by Phabricator updates
# Direct output of Phabricator API (not the object passed here)
self.revision_id = revision.phabricator_id
self.revision_url = None
self.revision = None
# Needed to update Phabricator Harbormaster
self.target_phid = revision.build_target_phid
# Needed to load stack of patches
self.diff = revision.diff
self.diff_id = revision.diff_id
self.stack = None
# Needed to apply patch and communicate on Phabricator
self.base_revision = None
self.actual_base_revision = None
self.missing_base_revision = False
def __str__(self):
return f"Phabricator Revision {self.revision_id} - Diff {self.diff_id}"
def __repr__(self):
return str(self)
def publish_analysis_phabricator(payload, phabricator_api):
mode, build, extras = payload
if build.target_phid is None:
logger.warning(
"No Phabricator build target, so no publication", mode=mode, build=build
)
return
logger.info("Publishing a Phabricator build update", mode=mode, build=build)
if mode == "fail:general":
failure = UnitResult(
namespace="code-review",
name="general",
result=UnitResultState.Broken,
details="WARNING: An error occurred in the code review bot.\n\n```{}```".format(
extras["message"]
),
format="remarkup",
duration=extras.get("duration", 0),
)
phabricator_api.update_build_target(
build.target_phid, BuildState.Fail, unit=[failure]
)
elif mode == "fail:mercurial":
extra_content = ""
if build.missing_base_revision:
extra_content = f" because the parent revision ({build.base_revision}) does not exist on mozilla-unified. If possible, you should publish that revision"
failure = UnitResult(
namespace="code-review",
name="mercurial",
result=UnitResultState.Fail,
details="WARNING: The code review bot failed to apply your patch{}.\n\n```{}```".format(
extra_content, extras["message"]
),
format="remarkup",
duration=extras.get("duration", 0),
)
phabricator_api.update_build_target(
build.target_phid, BuildState.Fail, unit=[failure]
)
elif mode == "test_result":
result = UnitResult(
namespace="code-review",
name=extras["name"],
result=extras["result"],
details=extras["details"],
)
phabricator_api.update_build_target(
build.target_phid, BuildState.Work, unit=[result]
)
elif mode == "success":
if build.missing_base_revision:
# Publish a warning message on Phabricator in the Unit Tests section,
# as done for other warnings/errors from the bot, since this section
# is centered and at the top of the page.
warning = UnitResult(
namespace="code-review",
name="mercurial",
result=UnitResultState.Unsound,
details=f"WARNING: The base revision of your patch is not available in the current repository.\nYour patch has been rebased on central (revision {build.actual_base_revision}): issues may be positioned at the wrong lines.",
)
phabricator_api.update_build_target(
build.target_phid, BuildState.Work, unit=[warning]
)
logger.debug(
"Missing base revision on PhabricatorBuild, adding a warning to Unit Tests section on Phabricator"
)
phabricator_api.create_harbormaster_uri(
build.target_phid,
"treeherder",
"CI (Treeherder) Jobs",
extras["treeherder_url"],
)
elif mode == "work":
phabricator_api.update_build_target(build.target_phid, BuildState.Work)
logger.info("Published public build as working", build=str(build))
else:
logger.warning("Unsupported publication", mode=mode, build=build)
def publish_analysis_lando(payload, lando_warnings):
"""
Publish result of patch application and push to try on Lando
"""
mode, build, extras = payload
assert isinstance(build, RevisionBuild), "Not a RevisionBuild"
logger.debug("Publishing a Lando build update", mode=mode, build=str(build))
if mode == "fail:general":
# Send general failure message to Lando
logger.info(
"Publishing code review failure.",
revision=build.revision["id"],
diff=build.diff_id,
)
try:
lando_warnings.add_warning(
LANDO_FAILURE_MESSAGE, build.revision["id"], build.diff_id
)
except Exception as ex:
logger.error(str(ex), exc_info=True)
elif mode == "fail:mercurial":
# Send mercurial message to Lando
logger.info(
"Publishing code review hg failure.",
revision=build.revision["id"],
diff=build.diff_id,
)
try:
lando_warnings.add_warning(
LANDO_FAILURE_HG_MESSAGE, build.revision["id"], build.diff_id
)
except Exception as ex:
logger.error(str(ex), exc_info=True)
elif mode == "success":
logger.info(
"Begin publishing init warning message to lando.",
revision=build.revision["id"],
diff=build.diff_id,
)
try:
lando_warnings.add_warning(
LANDO_WARNING_MESSAGE, build.revision["id"], build.diff_id
)
except Exception as ex:
logger.error(str(ex), exc_info=True)
else:
logger.info("Nothing to publish on Lando", mode=mode, build=build)