Edgewall Software

source: trunk/bitten/notify.py @ 1001

Last change on this file since 1001 was 953, checked in by osimons, 13 years ago

Add 'Platform' name to build report for web display and notifications. Fixes #541 and #633.

File size: 5.7 KB
CovLine 
1#-*- coding: utf-8 -*-
2#
3# Copyright (C) 2007 Ole Trenner, <ole@jayotee.de>
4# All rights reserved.
5#
6# This software is licensed as described in the file COPYING, which
7# you should have received as part of this distribution.
8
19from genshi.template.text import NewTextTemplate
110from trac.core import Component, implements
111from trac.web.chrome import ITemplateProvider, Chrome
112from trac.config import BoolOption
113from trac.notification import NotifyEmail
114from bitten.api import IBuildListener
115from bitten.model import Build, BuildStep, BuildLog, TargetPlatform
16
17
218class BittenNotify(Component):
119    """Sends notifications on build status by mail."""
120    implements(IBuildListener, ITemplateProvider)
21
122    notify_on_failure = BoolOption('notification',
123            'notify_on_failed_build', 'true',
124            """Notify if bitten build fails.""")
25
126    notify_on_success = BoolOption('notification',
127            'notify_on_successful_build', 'false',
128            """Notify if bitten build succeeds.""")
29
130    def __init__(self):
1431        self.log.debug('Initializing BittenNotify plugin')
32
133    def notify(self, build=None):
834        self.log.info('BittenNotify invoked for build %r', build)
835        self.log.debug('build status: %s', build.status)
836        if not self._should_notify(build):
637            return
238        self.log.info('Sending notification for build %r', build)
239        try:
240            email = BuildNotifyEmail(self.env)
241            email.notify(build)
242        except Exception, e:
243            self.log.exception("Failure sending notification for build "
244                               "%s: %s", build.id, e)
45
146    def _should_notify(self, build):
1247        if build.status == Build.FAILURE:
448            return self.notify_on_failure
849        elif build.status == Build.SUCCESS:
650            return self.notify_on_success
651        else:
252            return False
53
54    # IBuildListener methods
55
156    def build_started(self, build):
57        """build started"""
158        self.notify(build)
59
160    def build_aborted(self, build):
61        """build aborted"""
162        self.notify(build)
63
164    def build_completed(self, build):
65        """build completed"""
666        self.notify(build)
67
68    # ITemplateProvider methods
69
170    def get_templates_dirs(self):
71        """Return a list of directories containing the provided template
72        files."""
473        from pkg_resources import resource_filename
474        return [resource_filename(__name__, 'templates')]
75
176    def get_htdocs_dirs(self):
77        """Return the absolute path of a directory containing additional
78        static resources (such as images, style sheets, etc)."""
079        return []
80
81
282class BuildNotifyEmail(NotifyEmail):
183    """Notification of failed builds."""
84
185    readable_states = {
186        Build.SUCCESS: 'Successful',
187        Build.FAILURE: 'Failed',
188    }
189    template_name = 'bitten_notify_email.txt'
190    from_email = 'bitten@localhost'
91
192    def __init__(self, env):
493        NotifyEmail.__init__(self, env)
94        # Override the template type to always use NewTextTemplate
495        if not isinstance(self.template, NewTextTemplate):
096            self.template = Chrome(env).templates.load(
097                                self.template.filepath, cls=NewTextTemplate)
98
199    def notify(self, build):
4100        self.build = build
4101        self.data.update(self.template_data())
2102        subject = '[%s Build] %s [%s] %s' % (self.readable_states[build.status],
2103                                             self.env.project_name,
2104                                             self.build.rev,
2105                                             self.build.config)
2106        NotifyEmail.notify(self, self.build.id, subject)
107
1108    def get_recipients(self, resid):
2109        to = [self.get_author()]
2110        cc = []
2111        return (to, cc)
112
1113    def send(self, torcpts, ccrcpts):
0114        mime_headers = {
0115            'X-Trac-Build-ID': str(self.build.id),
0116            'X-Trac-Build-URL': self.build_link(),
0117        }
0118        NotifyEmail.send(self, torcpts, ccrcpts, mime_headers)
119
1120    def build_link(self):
4121        return self.env.abs_href.build(self.build.config, self.build.id)
122
1123    def template_data(self):
4124        failed_steps = BuildStep.select(self.env, build=self.build.id,
4125                                        status=BuildStep.FAILURE)
4126        platform = TargetPlatform.fetch(self.env, id=self.build.platform)
4127        change = self.get_changeset()
4128        return {
4129            'build': {
4130                'id': self.build.id,
4131                'status': self.readable_states[self.build.status],
4132                'link': self.build_link(),
4133                'config': self.build.config,
4134                'platform': getattr(platform, 'name', 'unknown'),
4135                'slave': self.build.slave,
4136                'failed_steps': [{
4137                    'name': step.name,
4138                    'description': step.description,
4139                    'errors': step.errors,
4140                    'log_messages': self.get_all_log_messages_for_step(step),
6141                } for step in failed_steps],
6142            },
4143            'change': {
4144                'rev': change.rev,
2145                'link': self.env.abs_href.changeset(change.rev),
2146                'author': change.author,
2147            },
2148        }
149
1150    def get_all_log_messages_for_step(self, step):
2151        messages = []
2152        for log in BuildLog.select(self.env, build=self.build.id,
2153                                   step=step.name):
0154            messages.extend(log.messages)
2155        return messages
156
1157    def get_changeset(self):
6158        repos = self.env.get_repository()
6159        assert repos, 'No "(default)" Repository: Add a repository or alias ' \
6160                      'named "(default)" to Trac.'
6161        return repos.get_changeset(self.build.rev)
162
1163    def get_author(self):
2164        return self.get_changeset().author
Note: See TracBrowser for help on using the repository browser.