From 63d74636c9c18b3243c8ea688ab5a769aa8abb95 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Fri, 21 Oct 2016 10:26:18 +0200
Subject: [PATCH 01/19] MessageFormatter: store the template instead of the
environment
---
master/buildbot/reporters/message.py | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/master/buildbot/reporters/message.py b/master/buildbot/reporters/message.py
index 270a6850380..883f55f8d70 100644
--- a/master/buildbot/reporters/message.py
+++ b/master/buildbot/reporters/message.py
@@ -23,11 +23,14 @@ def __init__(self, template_name=None, template_dir=None, template_type=None):
template_dir = os.path.join(os.path.dirname(__file__), "templates")
loader = jinja2.FileSystemLoader(template_dir)
- self.env = jinja2.Environment(
+ env = jinja2.Environment(
loader=loader, undefined=jinja2.StrictUndefined)
if template_name is not None:
self.template_name = template_name
+
+ self.template = env.get_template(self.template_name)
+
if template_type is not None:
self.template_type = template_type
@@ -114,7 +117,6 @@ def __call__(self, mode, buildername, buildset, build, master, previous_results,
ss_list = buildset['sourcestamps']
results = build['results']
- tpl = self.env.get_template(self.template_name)
cxt = dict(results=build['results'],
mode=mode,
buildername=buildername,
@@ -133,5 +135,5 @@ def __call__(self, mode, buildername, buildset, build, master, previous_results,
summary=self.messageSummary(build, results),
sourcestamps=self.messageSourceStamps(ss_list)
)
- contents = tpl.render(cxt)
+ contents = self.template.render(cxt)
return {'body': contents, 'type': self.template_type}
From eb6ee847193f981e3987e5327cb824b404a3b3eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Fri, 21 Oct 2016 10:35:17 +0200
Subject: [PATCH 02/19] b/r/message.py: Add license header
---
master/buildbot/reporters/message.py | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/master/buildbot/reporters/message.py b/master/buildbot/reporters/message.py
index 883f55f8d70..8cd1e1ac7eb 100644
--- a/master/buildbot/reporters/message.py
+++ b/master/buildbot/reporters/message.py
@@ -1,7 +1,23 @@
+# This file is part of Buildbot. Buildbot is free software: you can
+# redistribute it and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation, version 2.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright Buildbot Team Members
+
import os
import jinja2
+
from buildbot.process.results import CANCELLED
from buildbot.process.results import EXCEPTION
from buildbot.process.results import FAILURE
From e821c63c1c8a8e97768209cac2ec61faca9efb63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Fri, 21 Oct 2016 10:54:30 +0200
Subject: [PATCH 03/19] MessageFormatter: Allow to give the template inline
---
master/buildbot/reporters/message.py | 26 ++++++++++++-------
.../test/unit/test_reporters_message.py | 7 +++++
2 files changed, 23 insertions(+), 10 deletions(-)
diff --git a/master/buildbot/reporters/message.py b/master/buildbot/reporters/message.py
index 8cd1e1ac7eb..67ae0fe3bd7 100644
--- a/master/buildbot/reporters/message.py
+++ b/master/buildbot/reporters/message.py
@@ -17,7 +17,7 @@
import jinja2
-
+from buildbot import config
from buildbot.process.results import CANCELLED
from buildbot.process.results import EXCEPTION
from buildbot.process.results import FAILURE
@@ -33,19 +33,25 @@ class MessageFormatter(object):
wantProperties = True
wantSteps = False
- def __init__(self, template_name=None, template_dir=None, template_type=None):
+ def __init__(self, template_name=None, template_dir=None, template=None, template_type=None):
+
+ if (template is not None) and ((template_name is not None) or (template_dir is not None)):
+ config.error("Only one of template or template path can be given")
- if template_dir is None:
- template_dir = os.path.join(os.path.dirname(__file__), "templates")
+ if template is None:
+ if template_dir is None:
+ template_dir = os.path.join(os.path.dirname(__file__), "templates")
- loader = jinja2.FileSystemLoader(template_dir)
- env = jinja2.Environment(
- loader=loader, undefined=jinja2.StrictUndefined)
+ loader = jinja2.FileSystemLoader(template_dir)
+ env = jinja2.Environment(
+ loader=loader, undefined=jinja2.StrictUndefined)
- if template_name is not None:
- self.template_name = template_name
+ if template_name is not None:
+ self.template_name = template_name
- self.template = env.get_template(self.template_name)
+ self.template = env.get_template(self.template_name)
+ else:
+ self.template = jinja2.Template(template)
if template_type is not None:
self.template_type = template_type
diff --git a/master/buildbot/test/unit/test_reporters_message.py b/master/buildbot/test/unit/test_reporters_message.py
index ac29d3bcc1b..10455177b8c 100644
--- a/master/buildbot/test/unit/test_reporters_message.py
+++ b/master/buildbot/test/unit/test_reporters_message.py
@@ -88,6 +88,13 @@ def test_message_success(self):
Sincerely,
-The Buildbot'''))
+ @defer.inlineCallbacks
+ def test_inline_template(self):
+ self.message = message.MessageFormatter(template="URL: {{ build_url }} -- {{ summary }}")
+ res = yield self.doOneTest(SUCCESS, SUCCESS)
+ self.assertEqual(res['type'], "plain")
+ self.assertEqual(res['body'], "URL: http://localhost:8080/#builders/80/builds/1 -- Build succeeded!")
+
@defer.inlineCallbacks
def test_message_failure(self):
res = yield self.doOneTest(SUCCESS, FAILURE)
From 7e91117f7973b6687f39efc101d5c89efdebf9e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Fri, 21 Oct 2016 10:55:53 +0200
Subject: [PATCH 04/19] Update the release note
---
master/docs/relnotes/index.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/master/docs/relnotes/index.rst b/master/docs/relnotes/index.rst
index 41e634c22e7..fd837ae8740 100644
--- a/master/docs/relnotes/index.rst
+++ b/master/docs/relnotes/index.rst
@@ -66,6 +66,7 @@ Features
Now, builds started with a :class:`Triggereable` scheduler will be cancelled, while other builds will be retried.
The master will make sure that all latent workers are stopped.
+* The ``MessageFormatter`` class also allows inline-templates with the ``template`` parameter.
.. _Hyper: https://hyper.sh
From 4b085b8a9e7ba83e96a4c6bceba9d5d598dc62db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Fri, 21 Oct 2016 11:23:48 +0200
Subject: [PATCH 05/19] Put the MessageFormatter class in the plugins
---
master/setup.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/master/setup.py b/master/setup.py
index cd7847215f5..a948ccd077c 100755
--- a/master/setup.py
+++ b/master/setup.py
@@ -299,13 +299,13 @@ def define_plugin_entries(groups):
]),
('buildbot.reporters', [
('buildbot.reporters.mail', ['MailNotifier']),
+ ('buildbot.reporters.message', ['MessageFormatter']),
('buildbot.reporters.gerrit', ['GerritStatusPush']),
('buildbot.reporters.http', ['HttpStatusPush']),
('buildbot.reporters.github', ['GitHubStatusPush']),
('buildbot.reporters.stash', ['StashStatusPush']),
('buildbot.reporters.bitbucket', ['BitbucketStatusPush']),
('buildbot.reporters.irc', ['IRC']),
-
]),
('buildbot.util', [
# Connection seems to be a way too generic name, though
From 1dbfd8fac8c0cd5091c94b35400e39bed7326845 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Fri, 21 Oct 2016 11:30:35 +0200
Subject: [PATCH 06/19] Start fixing the doc about messageFormatter
---
master/docs/manual/cfg-reporters.rst | 16 +++-------------
1 file changed, 3 insertions(+), 13 deletions(-)
diff --git a/master/docs/manual/cfg-reporters.rst b/master/docs/manual/cfg-reporters.rst
index 5e9d2550a60..7c65f039687 100644
--- a/master/docs/manual/cfg-reporters.rst
+++ b/master/docs/manual/cfg-reporters.rst
@@ -109,26 +109,16 @@ If you want to require Transport Layer Security (TLS), then you can also set ``u
If you see ``twisted.mail.smtp.TLSRequiredError`` exceptions in the log while using TLS, this can be due *either* to the server not supporting TLS or to a missing `PyOpenSSL`_ package on the BuildMaster system.
In some cases it is desirable to have different information then what is provided in a standard MailNotifier message.
-For this purpose MailNotifier provides the argument ``messageFormatter`` (a function) which allows for the creation of messages with unique content.
+For this purpose MailNotifier provides the argument ``messageFormatter`` (an instance of ``MessageFormatter``) which allows for the creation of messages with unique content.
For example, if only short emails are desired (e.g., for delivery to phones)::
- from buildbot.plugins import reporters, util
- def messageFormatter(mode, name, build, results, master_status):
- result = util.Results[results]
-
- text = list()
- text.append("STATUS: %s" % result.title())
- return {
- 'body' : "\n".join(text),
- 'type' : 'plain'
- }
-
+ from buildbot.plugins import reporters
mn = reporters.MailNotifier(fromaddr="buildbot@example.org",
sendToInterestedUsers=False,
mode=('problem',),
extraRecipients=['listaddr@example.org'],
- messageFormatter=messageFormatter)
+ messageFormatter=reporters.MessageFormatter(template="STATUS: {{ summary }}"))
Another example of a function delivering a customized html email containing the last 80 log lines of logs of the last build step is given below::
From 2d1a8c2b965ed229741ea0adee961dc6b1e72f4c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Fri, 21 Oct 2016 11:53:45 +0200
Subject: [PATCH 07/19] Replace the broken HTML formatting example with a
minimal one
---
master/docs/manual/cfg-reporters.rst | 123 ++++-----------------------
1 file changed, 18 insertions(+), 105 deletions(-)
diff --git a/master/docs/manual/cfg-reporters.rst b/master/docs/manual/cfg-reporters.rst
index 7c65f039687..54a0e53c57a 100644
--- a/master/docs/manual/cfg-reporters.rst
+++ b/master/docs/manual/cfg-reporters.rst
@@ -120,116 +120,29 @@ For example, if only short emails are desired (e.g., for delivery to phones)::
extraRecipients=['listaddr@example.org'],
messageFormatter=reporters.MessageFormatter(template="STATUS: {{ summary }}"))
-Another example of a function delivering a customized html email containing the last 80 log lines of logs of the last build step is given below::
-
- from future.utils import text_type
- from buildbot.plugins import util, reporters
-
- import cgi, datetime
-
- # FIXME: this code is barely readable, we should provide a better example with use of jinja templates
- #
- def html_message_formatter(mode, name, build, results, master_status):
- """Provide a customized message to Buildbot's MailNotifier.
-
- The last 80 lines of the log are provided as well as the changes
- relevant to the build. Message content is formatted as html.
- """
- result = util.Results[results]
-
- limit_lines = 80
- text = list()
- text.append(u'Build status: %s
' % result.upper())
- text.append(u'')
- text.append(u"Worker for this Build: | %s |
" % build.getWorkername())
- if master_status.getURLForThing(build):
- text.append(u'Complete logs for all build steps: | %s |
'
- % (master_status.getURLForThing(build),
- master_status.getURLForThing(build))
- )
- text.append(u'Build Reason: | %s |
' % build.getReason())
- source = u""
- for ss in build.getSourceStamps():
- if ss.codebase:
- source += u'%s: ' % ss.codebase
- if ss.branch:
- source += u"[branch %s] " % ss.branch
- if ss.revision:
- source += ss.revision
- else:
- source += u"HEAD"
- if ss.patch:
- source += u" (plus patch)"
- if ss.patch_info: # add patch comment
- source += u" (%s)" % ss.patch_info[1]
- text.append(u"Build Source Stamp: | %s |
" % source)
- text.append(u"Blamelist: | %s |
" % ",".join(build.getResponsibleUsers()))
- text.append(u'
')
- if ss.changes:
- text.append(u'Recent Changes:
')
- for c in ss.changes:
- cd = c.asDict()
- when = datetime.datetime.fromtimestamp(cd['when'] ).ctime()
- text.append(u'')
- text.append(u'Repository: | %s |
' % cd['repository'] )
- text.append(u'Project: | %s |
' % cd['project'] )
- text.append(u'Time: | %s |
' % when)
- text.append(u'Changed by: | %s |
' % cd['who'] )
- text.append(u'Comments: | %s |
' % cd['comments'] )
- text.append(u'
')
- files = cd['files']
- if files:
- text.append(u'Files |
')
- for file in files:
- text.append(u'%s: |
' % file['name'] )
- text.append(u'
')
- text.append(u'
')
- # get all the steps in build in reversed order
- rev_steps = reversed(build.getSteps())
- # find the last step that finished
- for step in rev_steps:
- if step.isFinished():
- break
- # get logs for the last finished step
- if step.isFinished():
- logs = step.getLogs()
- # No step finished, loop just exhausted itself; so as a special case we fetch all logs
- else:
- logs = build.getLogs()
- # logs within a step are in reverse order. Search back until we find stdio
- for log in reversed(logs):
- if log.getName() == 'stdio':
- break
- name = "%s.%s" % (log.getStep().getName(), log.getName())
- status, dummy = log.getStep().getResults()
- # XXX logs no longer have getText methods!!
- content = log.getText().splitlines() # Note: can be VERY LARGE
- url = u'%s/steps/%s/logs/%s' % (master_status.getURLForThing(build),
- log.getStep().getName(),
- log.getName())
-
- text.append(u'Detailed log of last build step: %s'
- % (url, url))
- text.append(u'
')
- text.append(u'Last %d lines of "%s"
' % (limit_lines, name))
- unilist = list()
- for line in content[len(content)-limit_lines:]:
- unilist.append(cgi.escape(text_type(line,'utf-8')))
- text.append(u'')
- text.extend(unilist)
- text.append(u'
')
- text.append(u'
')
- text.append(u'-The Buildbot')
- return {
- 'body': u"\n".join(text),
- 'type': 'html'
- }
+Another example of a function delivering a customized html email is given below::
+
+ from buildbot.plugins import reporters
+
+ class DetailledMessageFormatter(reporters.MessageFormatter):
+ wantSteps = True
+ wantLogs = True
+
+ # XXX: This is work in progress, use with care.
+ template=u'''\
+ Build status: {{ summary }}
+ Worker used: {{ workername }}
+ {% for step in build['steps'] %}
+ {{ step['name'] }}: {{ step['result'] }}
+ {% endfor %}
+ -- The Buildbot
+ '''
mn = reporters.MailNotifier(fromaddr="buildbot@example.org",
sendToInterestedUsers=False,
mode=('failing',),
extraRecipients=['listaddr@example.org'],
- messageFormatter=html_message_formatter)
+ messageFormatter=DetailledMessageFormatter(template=template, template_type='html'))
.. _PyOpenSSL: http://pyopenssl.sourceforge.net/
From 4a24a9a97ce3858761ce1b145e3e4d4bb7d76637 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Fri, 21 Oct 2016 12:12:32 +0200
Subject: [PATCH 08/19] Allow messageFormatters to request the logs
---
master/buildbot/reporters/mail.py | 6 ++++--
master/buildbot/reporters/message.py | 1 +
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/master/buildbot/reporters/mail.py b/master/buildbot/reporters/mail.py
index 5d8eb7ee0a3..028ebda990b 100644
--- a/master/buildbot/reporters/mail.py
+++ b/master/buildbot/reporters/mail.py
@@ -242,7 +242,8 @@ def buildsetComplete(self, key, msg):
self.master, bsid,
wantProperties=self.messageFormatter.wantProperties,
wantSteps=self.messageFormatter.wantSteps,
- wantPreviousBuild=self.wantPreviousBuild())
+ wantPreviousBuild=self.wantPreviousBuild(),
+ wantLogs=self.messageFormatter.wantLogs)
builds = res['builds']
buildset = res['buildset']
@@ -262,7 +263,8 @@ def buildComplete(self, key, build):
self.master, buildset, [build],
wantProperties=self.messageFormatter.wantProperties,
wantSteps=self.messageFormatter.wantSteps,
- wantPreviousBuild=self.wantPreviousBuild())
+ wantPreviousBuild=self.wantPreviousBuild(),
+ wantLogs=self.messageFormatter.wantLogs)
# only include builds for which isMailNeeded returns true
if self.isMailNeeded(build):
self.buildMessage(
diff --git a/master/buildbot/reporters/message.py b/master/buildbot/reporters/message.py
index 67ae0fe3bd7..2cefa0f0ef6 100644
--- a/master/buildbot/reporters/message.py
+++ b/master/buildbot/reporters/message.py
@@ -32,6 +32,7 @@ class MessageFormatter(object):
template_type = 'plain'
wantProperties = True
wantSteps = False
+ wantLogs = False
def __init__(self, template_name=None, template_dir=None, template=None, template_type=None):
From e7f1c055a2d53a3e8c5e85226c564d4f730029ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Fri, 21 Oct 2016 12:33:08 +0200
Subject: [PATCH 09/19] Allow the MessageFormatter to customize the subject
---
master/buildbot/reporters/message.py | 51 ++++++++++++-------
.../test/unit/test_reporters_message.py | 7 +++
2 files changed, 40 insertions(+), 18 deletions(-)
diff --git a/master/buildbot/reporters/message.py b/master/buildbot/reporters/message.py
index 2cefa0f0ef6..128d1e4b98b 100644
--- a/master/buildbot/reporters/message.py
+++ b/master/buildbot/reporters/message.py
@@ -34,28 +34,40 @@ class MessageFormatter(object):
wantSteps = False
wantLogs = False
- def __init__(self, template_name=None, template_dir=None, template=None, template_type=None):
+ def __init__(self, template_name=None, template_dir=None, template=None,
+ subject_name=None, subject=None, template_type=None):
if (template is not None) and ((template_name is not None) or (template_dir is not None)):
config.error("Only one of template or template path can be given")
- if template is None:
- if template_dir is None:
- template_dir = os.path.join(os.path.dirname(__file__), "templates")
+ self.body_template = self.getTemplate(template_name, template_dir, template)
+ self.subject_template = None
+ if subject_name or subject:
+ self.subject_template = self.getTemplate(subject_name, template_dir, subject)
- loader = jinja2.FileSystemLoader(template_dir)
- env = jinja2.Environment(
- loader=loader, undefined=jinja2.StrictUndefined)
+ if template_type is not None:
+ self.template_type = template_type
- if template_name is not None:
- self.template_name = template_name
- self.template = env.get_template(self.template_name)
- else:
- self.template = jinja2.Template(template)
+ def getTemplate(self, filename, dirname, content):
+ if content and filename:
+ config.error("Only one of template or template path can be given")
+
+ if content:
+ return jinja2.Template(content)
+
+ if dirname is None:
+ dirname = os.path.join(os.path.dirname(__file__), "templates")
+
+ loader = jinja2.FileSystemLoader(dirname)
+ env = jinja2.Environment(
+ loader=loader, undefined=jinja2.StrictUndefined)
+
+ if filename is None:
+ filename = self.template_name
+
+ return env.get_template(filename)
- if template_type is not None:
- self.template_type = template_type
def getDetectedStatus(self, mode, results, previous_results):
@@ -135,8 +147,8 @@ def messageSummary(self, build, results):
return text
def __call__(self, mode, buildername, buildset, build, master, previous_results, blamelist):
- """Generate a buildbot mail message and return a tuple of message text
- and type."""
+ """Generate a buildbot mail message and return a dictionnary
+ containing the message body, type and subject."""
ss_list = buildset['sourcestamps']
results = build['results']
@@ -158,5 +170,8 @@ def __call__(self, mode, buildername, buildset, build, master, previous_results,
summary=self.messageSummary(build, results),
sourcestamps=self.messageSourceStamps(ss_list)
)
- contents = self.template.render(cxt)
- return {'body': contents, 'type': self.template_type}
+ body = self.body_template.render(cxt)
+ email = {'body': body, 'type': self.template_type}
+ if self.subject_template is not None:
+ email['subject'] = self.subject_template.render(cxt)
+ return email
diff --git a/master/buildbot/test/unit/test_reporters_message.py b/master/buildbot/test/unit/test_reporters_message.py
index 10455177b8c..e8b638e7bea 100644
--- a/master/buildbot/test/unit/test_reporters_message.py
+++ b/master/buildbot/test/unit/test_reporters_message.py
@@ -87,6 +87,7 @@ def test_message_success(self):
Sincerely,
-The Buildbot'''))
+ self.assertTrue('subject' not in res)
@defer.inlineCallbacks
def test_inline_template(self):
@@ -95,6 +96,12 @@ def test_inline_template(self):
self.assertEqual(res['type'], "plain")
self.assertEqual(res['body'], "URL: http://localhost:8080/#builders/80/builds/1 -- Build succeeded!")
+ @defer.inlineCallbacks
+ def test_inline_subject(self):
+ self.message = message.MessageFormatter(subject="subject")
+ res = yield self.doOneTest(SUCCESS, SUCCESS)
+ self.assertEqual(res['subject'], "subject")
+
@defer.inlineCallbacks
def test_message_failure(self):
res = yield self.doOneTest(SUCCESS, FAILURE)
From e0ab5be98e5e777832a2c61e861cca6ebbc0adcc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Fri, 21 Oct 2016 12:34:36 +0200
Subject: [PATCH 10/19] Update the release note
---
master/docs/relnotes/index.rst | 2 ++
1 file changed, 2 insertions(+)
diff --git a/master/docs/relnotes/index.rst b/master/docs/relnotes/index.rst
index fd837ae8740..2aac36ed7dc 100644
--- a/master/docs/relnotes/index.rst
+++ b/master/docs/relnotes/index.rst
@@ -68,6 +68,8 @@ Features
* The ``MessageFormatter`` class also allows inline-templates with the ``template`` parameter.
+* The ``MessageFormatter`` class allows custom mail's subjects with the ``subject`` and ``subject_name`` parameters.
+
.. _Hyper: https://hyper.sh
Fixes
From 6c691f5418267619bfc59cc71598dab450bac06f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Fri, 21 Oct 2016 12:57:51 +0200
Subject: [PATCH 11/19] MessageFormatter: Unify the status_text.
---
master/buildbot/reporters/message.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/master/buildbot/reporters/message.py b/master/buildbot/reporters/message.py
index 128d1e4b98b..5e79e66a247 100644
--- a/master/buildbot/reporters/message.py
+++ b/master/buildbot/reporters/message.py
@@ -78,7 +78,7 @@ def getDetectedStatus(self, mode, results, previous_results):
else:
text = "failed build"
elif results == WARNINGS:
- text = "The Buildbot has detected a problem in the build"
+ text = "problem in the build"
elif results == SUCCESS:
if "change" in mode and previous_results is not None and previous_results != results:
text = "restored build"
From dd114c3119d8473a79337c55b485fe93f00a3f91 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Tue, 25 Oct 2016 10:11:06 +0200
Subject: [PATCH 12/19] MessageFormatter: allow to extend the context given to
the template
---
master/buildbot/reporters/message.py | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/master/buildbot/reporters/message.py b/master/buildbot/reporters/message.py
index 5e79e66a247..f7385ff63b4 100644
--- a/master/buildbot/reporters/message.py
+++ b/master/buildbot/reporters/message.py
@@ -35,7 +35,7 @@ class MessageFormatter(object):
wantLogs = False
def __init__(self, template_name=None, template_dir=None, template=None,
- subject_name=None, subject=None, template_type=None):
+ subject_name=None, subject=None, template_type=None, ctx=None):
if (template is not None) and ((template_name is not None) or (template_dir is not None)):
config.error("Only one of template or template path can be given")
@@ -48,6 +48,11 @@ def __init__(self, template_name=None, template_dir=None, template=None,
if template_type is not None:
self.template_type = template_type
+ if ctx is None:
+ ctx = {}
+
+ self.ctx = ctx
+
def getTemplate(self, filename, dirname, content):
if content and filename:
@@ -152,7 +157,7 @@ def __call__(self, mode, buildername, buildset, build, master, previous_results,
ss_list = buildset['sourcestamps']
results = build['results']
- cxt = dict(results=build['results'],
+ ctx = dict(results=build['results'],
mode=mode,
buildername=buildername,
workername=build['properties'].get(
@@ -170,8 +175,9 @@ def __call__(self, mode, buildername, buildset, build, master, previous_results,
summary=self.messageSummary(build, results),
sourcestamps=self.messageSourceStamps(ss_list)
)
- body = self.body_template.render(cxt)
+ ctx.update(self.ctx)
+ body = self.body_template.render(ctx)
email = {'body': body, 'type': self.template_type}
if self.subject_template is not None:
- email['subject'] = self.subject_template.render(cxt)
+ email['subject'] = self.subject_template.render(ctx)
return email
From f124ce8aed3a938ea8b15a90592de1709ad1acaf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Tue, 25 Oct 2016 10:18:31 +0200
Subject: [PATCH 13/19] Improve slightly the doc about the MessageFormatter.
---
master/docs/manual/cfg-reporters.rst | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/master/docs/manual/cfg-reporters.rst b/master/docs/manual/cfg-reporters.rst
index 54a0e53c57a..cfa8f33925e 100644
--- a/master/docs/manual/cfg-reporters.rst
+++ b/master/docs/manual/cfg-reporters.rst
@@ -124,10 +124,6 @@ Another example of a function delivering a customized html email is given below:
from buildbot.plugins import reporters
- class DetailledMessageFormatter(reporters.MessageFormatter):
- wantSteps = True
- wantLogs = True
-
# XXX: This is work in progress, use with care.
template=u'''\
Build status: {{ summary }}
@@ -142,7 +138,9 @@ Another example of a function delivering a customized html email is given below:
sendToInterestedUsers=False,
mode=('failing',),
extraRecipients=['listaddr@example.org'],
- messageFormatter=DetailledMessageFormatter(template=template, template_type='html'))
+ messageFormatter=reporters.MessageFormatter(
+ template=template, template_type='html',
+ wantProperties=True, wantSteps=True, wantLogs=True))
.. _PyOpenSSL: http://pyopenssl.sourceforge.net/
From d6e04fbd7b7838a02278e41cd67044fbf62346f4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Tue, 25 Oct 2016 10:27:25 +0200
Subject: [PATCH 14/19] Update the release note
---
master/docs/relnotes/index.rst | 2 ++
1 file changed, 2 insertions(+)
diff --git a/master/docs/relnotes/index.rst b/master/docs/relnotes/index.rst
index 2aac36ed7dc..115aa14a157 100644
--- a/master/docs/relnotes/index.rst
+++ b/master/docs/relnotes/index.rst
@@ -70,6 +70,8 @@ Features
* The ``MessageFormatter`` class allows custom mail's subjects with the ``subject`` and ``subject_name`` parameters.
+* The ``MessageFormatter`` class allows extending the context given to the Templates via the ``ctx`` parameter.
+
.. _Hyper: https://hyper.sh
Fixes
From 09c73faf296e51bad6eb95eaff77880e16489cd9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Tue, 25 Oct 2016 11:36:00 +0200
Subject: [PATCH 15/19] Make pyflakes happy
---
master/buildbot/reporters/message.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/master/buildbot/reporters/message.py b/master/buildbot/reporters/message.py
index f7385ff63b4..ff99ce08917 100644
--- a/master/buildbot/reporters/message.py
+++ b/master/buildbot/reporters/message.py
@@ -53,7 +53,6 @@ def __init__(self, template_name=None, template_dir=None, template=None,
self.ctx = ctx
-
def getTemplate(self, filename, dirname, content):
if content and filename:
config.error("Only one of template or template path can be given")
@@ -73,7 +72,6 @@ def getTemplate(self, filename, dirname, content):
return env.get_template(filename)
-
def getDetectedStatus(self, mode, results, previous_results):
if results == FAILURE:
From 11a781bdd842e298224df6a2043fdb7f6fc7d7d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Tue, 25 Oct 2016 12:09:55 +0200
Subject: [PATCH 16/19] MessageFormatter: Update the constructor
---
master/buildbot/reporters/message.py | 30 ++++++++++++++++------------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/master/buildbot/reporters/message.py b/master/buildbot/reporters/message.py
index ff99ce08917..9710d5a80d5 100644
--- a/master/buildbot/reporters/message.py
+++ b/master/buildbot/reporters/message.py
@@ -28,22 +28,23 @@
class MessageFormatter(object):
- template_name = 'default_mail.txt'
+ template_filename = 'default_mail.txt'
template_type = 'plain'
- wantProperties = True
- wantSteps = False
- wantLogs = False
- def __init__(self, template_name=None, template_dir=None, template=None,
- subject_name=None, subject=None, template_type=None, ctx=None):
+ def __init__(self, template_dir=None,
+ template_filename=None, template=None, template_name=None,
+ subject_filename=None, subject=None,
+ template_type=None, ctx=None,
+ wantProperties=True, wantSteps=False, wantLogs=False):
- if (template is not None) and ((template_name is not None) or (template_dir is not None)):
- config.error("Only one of template or template path can be given")
+ if template_name is not None:
+ config.warnDeprecated('0.9.1', "template_name is deprecated, use template_filename")
+ template_filename = template_name
- self.body_template = self.getTemplate(template_name, template_dir, template)
+ self.body_template = self.getTemplate(template_filename, template_dir, template)
self.subject_template = None
- if subject_name or subject:
- self.subject_template = self.getTemplate(subject_name, template_dir, subject)
+ if subject_filename or subject:
+ self.subject_template = self.getTemplate(subject_filename, template_dir, subject)
if template_type is not None:
self.template_type = template_type
@@ -52,9 +53,12 @@ def __init__(self, template_name=None, template_dir=None, template=None,
ctx = {}
self.ctx = ctx
+ self.wantProperties = wantProperties
+ self.wantSteps = wantSteps
+ self.wantLogs = wantLogs
def getTemplate(self, filename, dirname, content):
- if content and filename:
+ if content and (filename or dirname):
config.error("Only one of template or template path can be given")
if content:
@@ -150,7 +154,7 @@ def messageSummary(self, build, results):
return text
def __call__(self, mode, buildername, buildset, build, master, previous_results, blamelist):
- """Generate a buildbot mail message and return a dictionnary
+ """Generate a buildbot mail message and return a dictionary
containing the message body, type and subject."""
ss_list = buildset['sourcestamps']
results = build['results']
From ae23f0b5e549903a0a4d14e169b90f6223d6cc76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Tue, 25 Oct 2016 12:53:24 +0200
Subject: [PATCH 17/19] Update the documentation for the MessageFormatter
---
master/docs/manual/cfg-reporters.rst | 93 ++++++++++++++++++----------
1 file changed, 62 insertions(+), 31 deletions(-)
diff --git a/master/docs/manual/cfg-reporters.rst b/master/docs/manual/cfg-reporters.rst
index cfa8f33925e..e9465d7b466 100644
--- a/master/docs/manual/cfg-reporters.rst
+++ b/master/docs/manual/cfg-reporters.rst
@@ -124,14 +124,13 @@ Another example of a function delivering a customized html email is given below:
from buildbot.plugins import reporters
- # XXX: This is work in progress, use with care.
template=u'''\
Build status: {{ summary }}
Worker used: {{ workername }}
{% for step in build['steps'] %}
{{ step['name'] }}: {{ step['result'] }}
{% endfor %}
- -- The Buildbot
+ -- The Buildbot
'''
mn = reporters.MailNotifier(fromaddr="buildbot@example.org",
@@ -140,7 +139,7 @@ Another example of a function delivering a customized html email is given below:
extraRecipients=['listaddr@example.org'],
messageFormatter=reporters.MessageFormatter(
template=template, template_type='html',
- wantProperties=True, wantSteps=True, wantLogs=True))
+ wantProperties=True, wantSteps=True))
.. _PyOpenSSL: http://pyopenssl.sourceforge.net/
@@ -279,13 +278,9 @@ MailNotifier arguments
Regardless of the setting of ``lookup``, ``MailNotifier`` will also send mail to addresses in the ``extraRecipients`` list.
``messageFormatter``
- This is a optional function that can be used to generate a custom mail message.
- A :func:`messageFormatter` function takes the mail mode (``mode``), builder name (``name``), the build Data API results (``build``), the result code (``results``), and a reference to the BuildMaster object (``master``), which can then be used to create additional Data API calls.
- It returns a dictionary.
- The ``body`` key gives a string that is the complete text of the message.
- The ``type`` key is the message type ('plain' or 'html').
- The 'html' type should be used when generating an HTML message.
- The ``subject`` key is optional, but gives the subject for the email.
+ This is an optional instance of the ``reporters.MessageFormatter`` class that can be used to generate a custom mail message.
+ This class uses the Jinja2_ templating language to generate the body and optionally the subject of the mails.
+ Templates can either be given inline (as string), or read from the filesystem.
``extraHeaders``
(dictionary).
@@ -293,46 +288,82 @@ MailNotifier arguments
Both the keys and the values may be a `Interpolate` instance.
-As a help to those writing :func:`messageFormatter` functions, the following table describes how to get some useful pieces of information from the various data objects:
+MessageFormatter arguments
+++++++++++++++++++++++++++
-Name of the builder that generated this event
- ``name``
+The easiest way to use the ``messageFormatter`` parameter is to create a new instance of the ``reporters.MessageFormatter`` class.
+The constructor to that class takes the following arguments:
-Title of the BuildMaster
- ``master.config.title``
+``template_dir``
+ This is the directory that is used to look for the various templates.
-MailNotifier mode
- ``mode`` (a combination of ``change``, ``failing``, ``passing``, ``problem``, ``warnings``, ``exception``, ``all``)
+``template_filename``
+ This is the name of the file in the ``template_dir`` directory that will be used to generate the body of the mail.
+ It defaults to 'default_mail.txt'.
+
+``template``
+ If this parameter is set, this parameter indicates the content of the template used to generate the body of the mail as string.
+
+``template_type``
+ This indicates the type of the generated template.
+ Use either 'plain' (the default) or 'html'.
+
+``subject_filename``
+ This is the name of the file in the ``template_dir`` directory that contains the content of the subject of the mail.
+
+``subject``
+ Alternatively, this is the content of the subject of the mail as string.
+
+``ctx``
+ This is an extension of the standard context that will be given to the templates.
+ Use this to add content to the templates that is otherwise not available.
+
+``wantProperties``
+ This parameter (defaults to True) will extend the content of the given ``build`` object with the Properties from the build.
-Builder result as a string
+``wantSteps``
+ This parameter (defaults to False) will extend the content of the given ``build`` object with information about the steps of the build.
+ Use it only when necessary as this increases the overhead in term of CPU and memory on the master.
- ::
+``wantLogs``
+ This parameter (defaults to False) will extend the content of the steps of the given ``build`` object with the full Logs of each steps from the build.
+ This requires ``wantSteps`` to be True.
+ Use it only when mandatory as this increases the overhead in term of CPU and memory on the master greatly.
- from buildbot.plugins import util
- result_str = util.Results[results]
- # one of 'success', 'warnings', 'failure', 'skipped', or 'exception'
+
+As a help to those writing Jinja2 templates the following table describes how to get some useful pieces of information from the various data objects:
+
+Name of the builder that generated this event
+ ``{{ buildername }}``
+
+Title of the BuildMaster
+ ``{{ projects }}``
+
+MailNotifier mode
+ ``{{ mode }}`` (a combination of ``change``, ``failing``, ``passing``, ``problem``, ``warnings``, ``exception``, ``all``)
URL to build page
- ``reporters.utils.getURLForBuild(master, build['buildid'])``
+ ``{{ build_url }}``
URL to buildbot main page
- ``master.config.buildbotURL``
+ ``{{ buildbot_url }}``
Build text
- ``build['state_string']``
+ ``{{ build['state_string'] }}``
Mapping of property names to (values, source)
- ``build['properties']``
+ ``{{ build['properties'] }}``
-Worker name
- ``build['properties']['workername']``
+For instance the build reason (from a forced build)
+ ``{{ build['properties']['reason'][0] }}``
-Build reason (from a forced build)
- ``build['properties']['reason']``
+Worker name
+ ``{{ workername }}``
List of responsible users
- ``reporters.utils.getResponsibleUsersForBuild(master, build['buildid'])``
+ ``{{ blamelist | join(', ') }}``
+.. _Jinja2: http://jinja.pocoo.org/docs/dev/templates/
.. bb:reporter:: IRC
From ddb3bd0c955e9de78be9f80f9e3ce283f2446c6f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Tue, 25 Oct 2016 13:32:01 +0200
Subject: [PATCH 18/19] Fix typo in attribute name
---
master/buildbot/reporters/message.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/master/buildbot/reporters/message.py b/master/buildbot/reporters/message.py
index 9710d5a80d5..3b2197d7cf3 100644
--- a/master/buildbot/reporters/message.py
+++ b/master/buildbot/reporters/message.py
@@ -72,7 +72,7 @@ def getTemplate(self, filename, dirname, content):
loader=loader, undefined=jinja2.StrictUndefined)
if filename is None:
- filename = self.template_name
+ filename = self.template_filename
return env.get_template(filename)
From a6c2010ab10ed9743a2a6008d5d6604b03ab7698 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Allard?=
Date: Tue, 25 Oct 2016 14:09:58 +0200
Subject: [PATCH 19/19] Slight documentation fixes
---
master/docs/manual/cfg-reporters.rst | 2 +-
master/docs/relnotes/index.rst | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/master/docs/manual/cfg-reporters.rst b/master/docs/manual/cfg-reporters.rst
index e9465d7b466..070a0110700 100644
--- a/master/docs/manual/cfg-reporters.rst
+++ b/master/docs/manual/cfg-reporters.rst
@@ -299,7 +299,7 @@ The constructor to that class takes the following arguments:
``template_filename``
This is the name of the file in the ``template_dir`` directory that will be used to generate the body of the mail.
- It defaults to 'default_mail.txt'.
+ It defaults to ``default_mail.txt``.
``template``
If this parameter is set, this parameter indicates the content of the template used to generate the body of the mail as string.
diff --git a/master/docs/relnotes/index.rst b/master/docs/relnotes/index.rst
index 115aa14a157..aebb8e9309a 100644
--- a/master/docs/relnotes/index.rst
+++ b/master/docs/relnotes/index.rst
@@ -132,6 +132,8 @@ Deprecations, Removals, and Non-Compatible Changes
* The ``user`` and ``password`` parameters of the ``HttpStatusPush`` reporter have been deprecated in favor of the ``auth`` parameter.
+* The ``template_name`` parameter of the ``MessageFormatter`` class has been deprecated in favor of ``template_filename``.
+
Buildslave
----------