aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggleton <paul.eggleton@linux.intel.com>2018-03-02 09:09:35 +1300
committerPaul Eggleton <paul.eggleton@linux.intel.com>2018-05-04 23:57:53 +1200
commit227b9c65af14ab7edfa788b3b33b141a21df78c3 (patch)
treef2ff3c740996edcd5c7c6e6415683677e79481f3
parent735353ebd1171aa6e276654586e57c56f17842d5 (diff)
downloadopenembedded-core-contrib-227b9c65af14ab7edfa788b3b33b141a21df78c3.tar.gz
rrs_upstream_email: rework
* Use maintenance plans to get layerbranches * Use from/to/subject and admin contact from maintenance plan * Use an actual template to render the email (and drop tabulate dependency) * Improve grammar in the email text * Use a single line to represent the most recent commit Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
-rw-r--r--requirements.txt1
-rwxr-xr-xrrs/tools/rrs_upstream_email.py191
-rw-r--r--settings.py5
-rw-r--r--templates/rrs/report_email.txt26
4 files changed, 124 insertions, 99 deletions
diff --git a/requirements.txt b/requirements.txt
index 5aae2477db..5ca7cf6e9f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -27,4 +27,3 @@ smmap==0.9.0
smmap2==2.0.3
Unidecode==0.4.19
vine==1.1.4
-tabulate==0.7.3
diff --git a/rrs/tools/rrs_upstream_email.py b/rrs/tools/rrs_upstream_email.py
index 0dae3629ec..411cf50dda 100755
--- a/rrs/tools/rrs_upstream_email.py
+++ b/rrs/tools/rrs_upstream_email.py
@@ -3,7 +3,7 @@
# Send email to maintainers about the current status of the recipes.
#
-# Copyright (C) 2015 Intel Corporation
+# Copyright (C) 2015, 2018 Intel Corporation
# Author: Aníbal Limón <anibal.limon@linux.intel.com>
#
# Licensed under the MIT license, see COPYING.MIT for details
@@ -12,7 +12,7 @@ import sys
import os.path
import optparse
import logging
-from tabulate import tabulate
+from collections import namedtuple
sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__))))
from common import common_setup, get_pv_type, get_logger
@@ -21,43 +21,30 @@ from layerindex import utils
utils.setup_django()
from django.core.mail import EmailMessage
+from django.template.loader import get_template
+from django.template import Context, Template
+from django.core.urlresolvers import reverse
+from django.contrib.sites.models import Site
import settings
from layerindex.models import LayerItem, LayerBranch, Recipe
from rrs.models import Maintainer, RecipeMaintainerHistory, RecipeMaintainer, \
- RecipeUpstream, RecipeUpstreamHistory
+ RecipeUpstream, RecipeUpstreamHistory, MaintenancePlan
logger = get_logger('UpstreamEmail', settings)
-LAYERBRANCH_NAME = "master"
"""
Send email with Recipes that need update.
"""
-def send_email(recipes, repodir, options):
- header = """This mail was sent out by Recipe reporting system.
-
-This message list those recipes which need to be upgraded. If maintainers
-believe some of them needn't to upgrade at this time, they can fill
-RECIPE_NO_UPDATE_REASON in respective recipe file to ignore this remainder
-until newer upstream version was detected.
-
-Example:
-RECIPE_NO_UPDATE_REASON = "Version 2.0 is unstable"
-
-You can check the detail information at:
-
-http://recipes.yoctoproject.org/
-
-"""
-
+def send_email(maintplan, recipes, options):
upgradable_count = 0
no_upgradable_count = 0
maintainers = Maintainer.objects.all().order_by("name")
- table_headers = ['Package', 'Version', 'Upstream version',
- 'Maintainer', 'NoUpgradeReason']
- table = []
+ RecipeUpgradeLine = namedtuple('RecipeUpgradeLine', ['pn', 'pv', 'pv_upstream', 'maintainer', 'noupgradereason'])
+
+ recipelines = []
for m in maintainers:
for recipe in recipes.keys():
recipe_maintainer = recipes[recipe]['maintainer']
@@ -69,50 +56,66 @@ http://recipes.yoctoproject.org/
name_max_len = 20
reason_max_len = 30
- pn = recipe.pn
- if len(pn) > pn_max_len:
- pn = pn[0:pn_max_len - 3] + "..."
-
- pv = recipe.pv
- if len(pv) > pv_max_len:
- pv = pv[0:pv_max_len - 3] + "..."
-
- pv_up = recipe_upstream.version
- if len(pv_up) > pv_max_len:
- pv_up = pv_up[0:pv_max_len - 3] + "..."
-
- name = m.name
- if len(name) > name_max_len:
- name = name[0:name_max_len - 3] + "..."
-
- reason = recipe_upstream.no_update_reason
- if len(reason) > reason_max_len:
- reason = reason[0:reason_max_len - 3] + "..."
-
- table.append([pn, pv, pv_up, name, reason])
+ recipelines.append(RecipeUpgradeLine(recipe.pn, recipe.pv, recipe_upstream.version, m.name, recipe_upstream.no_update_reason))
upgradable_count = upgradable_count + 1
if recipe_upstream.no_update_reason:
no_upgradable_count = no_upgradable_count + 1
- body = tabulate(table, table_headers, tablefmt="simple")
-
- footer = """
-\nUpgradable count: %d\nUpgradable total count: %d\n
-The based commit is:
-
-%s
-Any problem, please contact Anibal Limon <anibal.limon@intel.com>
-""" % ((upgradable_count - no_upgradable_count), upgradable_count,
- utils.runcmd("git log -1", repodir))
- #
- subject = options.subject
- from_email = options._from
- to_email_list = options.to.split(';')
- text_content = header + body + footer
+ commits = []
+ fetchdir = settings.LAYER_FETCH_DIR
+ for item in maintplan.maintenanceplanlayerbranch_set.all():
+ layerbranch = item.layerbranch
+ layer = layerbranch.layer
+ urldir = layer.get_fetch_dir()
+ repodir = os.path.join(fetchdir, urldir)
+ # FIXME this assumes the correct branch is checked out
+ topcommitdesc = utils.runcmd("git log -1 --oneline", repodir).strip()
+ commits.append('%s: %s' % (layerbranch.layer.name, topcommitdesc))
+
+ # Render the subject as a template (to allow a bit of flexibility)
+ subject = options.subject or maintplan.email_subject
+ subject_template = Template(subject)
+ current_site = Site.objects.get_current()
+ d = Context({
+ 'maintplan': maintplan,
+ 'site': current_site,
+ })
+ subject_content = subject_template.render(d)
+
+ from_email = options._from or maintplan.email_from
+ if options.to:
+ to_email_list = options.to.split(';')
+ elif maintplan.email_to:
+ to_email_list = maintplan.email_to.split(';')
+ else:
+ to_email_list = []
+
+ if not subject:
+ logger.error('No subject specified in maintenance plan %s and none specified on command line' % maintplan.name)
+ sys.exit(1)
+ if not from_email:
+ logger.error('No sender email address specified in maintenance plan %s and none specified on command line' % maintplan.name)
+ sys.exit(1)
+ if not to_email_list:
+ logger.error('No recipient email address specified in maintenance plan %s and none specified on command line' % maintplan.name)
+ sys.exit(1)
- msg = EmailMessage(subject, text_content, from_email, to_email_list)
+ plaintext = get_template('rrs/report_email.txt')
+ site_url = 'http://' + current_site.domain + reverse('rrs_frontpage')
+ d = Context({
+ 'maintplan': maintplan,
+ 'site': current_site,
+ 'site_url': site_url,
+ 'upgradable_count': (upgradable_count - no_upgradable_count),
+ 'total_upgradable_count': upgradable_count,
+ 'commits': commits,
+ 'recipelines': recipelines,
+ })
+ text_content = plaintext.render(d)
+
+ msg = EmailMessage(subject_content, text_content, from_email, to_email_list)
msg.send()
def main():
@@ -120,14 +123,12 @@ def main():
usage = """
%prog [options]""")
- parser.add_option("-l", "--layername",
- action="store", dest="layername", default=settings.CORE_LAYER_NAME)
parser.add_option("-s", "--subject",
- action="store", dest="subject", default=settings.RRS_EMAIL_SUBJECT)
+ action="store", dest="subject", help='Override email subject')
parser.add_option("-f", "--from",
- action="store", dest="_from", default=settings.RRS_EMAIL_FROM)
+ action="store", dest="_from", help='Override sender address')
parser.add_option("-t", "--to",
- action="store", dest="to", default=settings.RRS_EMAIL_TO)
+ action="store", dest="to", help='Override recipient address')
parser.add_option("-d", "--debug",
help = "Enable debug output",
action="store_const", const=logging.DEBUG, dest="loglevel", default=logging.INFO)
@@ -137,36 +138,40 @@ def main():
options, args = parser.parse_args(sys.argv)
- recipes = {}
- layer = LayerItem.objects.filter(name = options.layername)[0]
-
# get recipes for send email
- layerbranch = layer.get_layerbranch(LAYERBRANCH_NAME)
- recipe_upstream_history = RecipeUpstreamHistory.get_last()
- if recipe_upstream_history is None:
- logger.warn('I don\'t have Upstream information yet, run update.py script')
- sys.exit(1)
-
- recipe_maintainer_history = RecipeMaintainerHistory.get_last()
- if recipe_maintainer_history is None:
- logger.warn('I don\'t have Maintainership information yet,' +
- ' run rrs_maintainer_history.py script')
+ maintplans = MaintenancePlan.objects.filter(email_enabled=True)
+ if not maintplans.exists():
+ logger.error('No maintenance plans with email enabled were found')
sys.exit(1)
- for recipe in Recipe.objects.filter(layerbranch = layerbranch):
- recipe_upstream_query = RecipeUpstream.objects.filter(recipe =
- recipe, history = recipe_upstream_history)
- if recipe_upstream_query and recipe_upstream_query[0].status == 'N':
- recipes[recipe] = {}
-
- recipe_maintainer = RecipeMaintainer.objects.filter(recipe =
- recipe, history = recipe_maintainer_history)[0]
- recipes[recipe]['maintainer'] = recipe_maintainer
- recipes[recipe]['upstream'] = recipe_upstream_query[0]
-
- repodir = os.path.join(settings.LAYER_FETCH_DIR, layer.get_fetch_dir())
-
- send_email(recipes, repodir, options)
+ for maintplan in maintplans:
+ recipes = {}
+ for item in maintplan.maintenanceplanlayerbranch_set.all():
+ layerbranch = item.layerbranch
+
+ recipe_upstream_history = RecipeUpstreamHistory.get_last()
+ if recipe_upstream_history is None:
+ logger.warn('I don\'t have Upstream information yet, run update.py script')
+ sys.exit(1)
+
+ recipe_maintainer_history = RecipeMaintainerHistory.get_last()
+ if recipe_maintainer_history is None:
+ logger.warn('I don\'t have Maintainership information yet,' +
+ ' run rrs_maintainer_history.py script')
+ sys.exit(1)
+
+ for recipe in layerbranch.recipe_set.all():
+ recipe_upstream_query = RecipeUpstream.objects.filter(recipe =
+ recipe, history = recipe_upstream_history)
+ if recipe_upstream_query and recipe_upstream_query[0].status == 'N':
+ recipes[recipe] = {}
+
+ recipe_maintainer = RecipeMaintainer.objects.filter(recipe =
+ recipe, history = recipe_maintainer_history)[0]
+ recipes[recipe]['maintainer'] = recipe_maintainer
+ recipes[recipe]['upstream'] = recipe_upstream_query[0]
+
+ send_email(maintplan, recipes, options)
if __name__ == "__main__":
main()
diff --git a/settings.py b/settings.py
index 65a947e05f..c3075a5e70 100644
--- a/settings.py
+++ b/settings.py
@@ -234,10 +234,5 @@ RABBIT_BACKEND = 'rpc://'
# Used for fetching repo
PARALLEL_JOBS = "4"
-# Settings for Recipe reporting system
-RRS_EMAIL_SUBJECT = '[Recipe reporting system] Upgradable recipe name list'
-RRS_EMAIL_FROM = 'recipe-report@yoctoproject.org'
-RRS_EMAIL_TO = 'list@example.com'
-
# Full path to directory where rrs tools stores logs
TOOLS_LOG_DIR = ""
diff --git a/templates/rrs/report_email.txt b/templates/rrs/report_email.txt
new file mode 100644
index 0000000000..02976fc440
--- /dev/null
+++ b/templates/rrs/report_email.txt
@@ -0,0 +1,26 @@
+This is an automated message from the Recipe Reporting System.
+
+Listed below are recipes which need to be upgraded based on the versions
+available upstream. If a maintainer believes some of them cannot be upgraded
+at this time, they can set RECIPE_NO_UPDATE_REASON in the recipe file(s) as
+a reminder of why not.
+
+Example:
+RECIPE_NO_UPDATE_REASON = "Version 2.0 is unstable"
+
+For further details please visit:
+
+ {{ site_url }}
+
+Package Version Upstream version Maintainer No upgrade reason
+-------------------- --------------- -------------------- -------------------- ------------------------------
+{% for line in recipelines %}{{ line.pn|truncatechars:20|ljust:20 }} {{ line.pv|truncatechars:15|ljust:15 }} {{ line.pv_upstream|truncatechars:20|ljust:20 }} {{ line.maintainer|truncatechars:20|ljust:20 }} {{ line.noupgradereason|truncatechars:30 }}
+{% endfor %}
+Upgradable count: {{ upgradable_count }}
+Upgradable total count: {{ total_upgradable_count }}
+
+Based upon current {{ commits|length|pluralize:"commit,commits" }}:
+
+{% for commit in commits %} {{ commit }}
+{% endfor %}
+{% if maintplan.admin %}If there are any problems with the above, please contact: {{ maintplan.admin.first_name }} {{ maintplan.admin.last_name }} <{{ maintplan.admin.email }}>{% endif %}