blob: 945260c8f1d60511e7587bc8f8f5c4de73c001c0 [file] [log] [blame]
# Copyright 2021 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Disable pylint noise
# pylint: disable=W0621, E0401, C0415, W1632
"""Sends triage reports"""
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import pickle
import smtplib
import sys
from config import rebase_baseline_branch
from config import rebase_target
cred_error = """\
ERROR: cred.py file not found. Create it in and initialize login, passw
variables within, for example:
login = "you@gmail.com"
passw = "your application password"
It'll work only for GMail, anyway.
"""
html_header = """<html>
<head></head>
<body><pre>
"""
html_footer = """</pre>
</body>
</html>
"""
email_text = """\
This is an automatic notification about %s -> merge/continuous/chromeos-kernelupstream-%s
uprev status.
Commits in "handled automatically" column were either dropped (due to revert or upstream)
or resolved using preexisting resolutions.
Commits counted as "need manual resolution" were skipped for the purpose of triage.
topic | commits overall | handled automatically | need manual resolution*| build
---------------+-----------------+-----------------------+------------------------+------
"""
upstream_prefix = """\
upstreaming stats:
category | fromlist | fromgit
-----------+----------+--------
"""
email_subject = "%s -> %s: %d/%d commits need manual resolution"
mailing_from = "from@example.com"
recip_limited = ["recip1@example.com", "recip2@example.com"]
recip_all = [
"recip1@example.com",
"recip2@example.com",
"recip3@example.com",
"recip4@example.com",
]
class Mailing:
"""Mailing class"""
def __init__(self, rebase_base, rebase_target):
"""Initializes class members"""
self.rebase_base = rebase_base
self.rebase_target = rebase_target
self.server = None
def build_mail(
self, topic_stats, upstream_stats, total_stats, topic_errors
):
"""Formats statistics into an email"""
mail = email_text % (self.rebase_base, self.rebase_target[1:])
stats = [0, 0, 0, 0]
for topic in topic_stats:
data = topic_stats[topic]
overall = data[0]
auto = data[1]
manual = data[2]
fixup_manual = data[3]
stats[0] += overall
stats[1] += auto
stats[2] += manual
stats[3] += fixup_manual
if data[5]:
build = "OK"
else:
build = "FAIL"
val = f"{manual}+{fixup_manual}"
mail += f"{topic:14} | {overall:15d} | {auto:21d} | {val:>22s} | {build}\n"
topic = "ALL"
build = "ON HOLD"
mail += "-" * len(email_text.split("\n")[6])
mail += "\n"
val = f"{stats[2]}+{stats[3]}"
mail += f"{topic:14} | {stats[0]:15d} | {stats[1]:21d} | {val:>22s} | {build}\n"
mail += "\n"
mail += upstream_prefix
fromlist = total_stats["fromlist"]
fromgit = total_stats["fromgit"]
mail += f"total | {fromlist:8} | {fromgit:7}\n"
fromlist = upstream_stats["fromlist"]
fromgit = upstream_stats["fromgit"]
mail += f"upstreamed | {fromlist:8} | {fromgit:7}\n\n"
for topic in topic_errors:
mail += "Branch %s failed to build due to:\n" % topic
mail += topic_errors[topic]
mail += "\n\n"
mail += (
"* - is a number of commmits+fixups which need manual resolution"
)
mail += "\n"
return (mail, stats)
def send_mail(self, subject, mail, recipients):
"""Send mail"""
try:
from cred import login
from cred import passw
except: # pylint: disable=bare-except
print(cred_error)
sys.exit()
self.server = smtplib.SMTP("smtp.gmail.com", 587)
self.server.starttls()
self.server.login(login, passw)
msg = MIMEMultipart("alternative")
msg["Subject"] = subject
msg["From"] = mailing_from
msg["To"] = ", ".join(recipients)
part1 = MIMEText(
"This message should be viewed in HTML. Check your client.", "plain"
)
part2 = MIMEText(html_header + mail + html_footer, "html")
msg.attach(part1)
msg.attach(part2)
self.server.send_message(msg)
def notify(self, topic_stats, upstream_stats, total_stats, topic_errors):
"""Preview and send report"""
mail, stats = self.build_mail(
topic_stats, upstream_stats, total_stats, topic_errors
)
subject = email_subject % (
self.rebase_base,
self.rebase_target,
stats[2],
stats[0],
)
print(mail)
ans = input("Send? [y/n] ")
if ans == "y":
# Send out to limited audience first
self.send_mail(subject, mail, recip_limited)
ans = input("Resend to all? [y/n] ")
if ans == "y":
# Send out to all
self.send_mail(subject, mail, recip_all)
def load_and_notify():
"""Creates a report, shows preview and sends it if ok"""
m = Mailing(rebase_baseline_branch, rebase_target)
topic_stats = pickle.load(open("topic_stats.bin", "rb"))
topic_stderr = pickle.load(open("topic_stderr.bin", "rb"))
upstream_stats = pickle.load(open("upstream_stats.bin", "rb"))
total_stats = pickle.load(open("total_stats.bin", "rb"))
m.notify(topic_stats, upstream_stats, total_stats, topic_stderr)