bbtwitter.py
author buildmaster@gans.protocultura.cl
Sat Mar 14 00:25:37 2009 +0000 (2009-03-14)
changeset 7 649271fe0663
parent 6 f3c9cce0766b
permissions -rw-r--r--
Adding a buildNumber variable and changed the text.
marijn@4
     1
#
marijn@4
     2
# Derived from code from the status/mail.py in buildbot 0.7.9
marijn@4
     3
# Needs python-twitter installed to function.
m@0
     4
m@1
     5
import twitter
m@0
     6
m@0
     7
from zope.interface import implements
m@1
     8
from twisted.internet import defer, threads
m@0
     9
from twisted.mail.smtp import sendmail
m@0
    10
from twisted.python import log as twlog
m@0
    11
m@0
    12
from buildbot import interfaces, util
m@0
    13
from buildbot.status import base
m@0
    14
from buildbot.status.builder import FAILURE, SUCCESS, WARNINGS
m@0
    15
m@1
    16
class TwitterNotifier(base.StatusReceiverMultiService):
m@0
    17
marijn@4
    18
    compare_attrs = ["twitname", "twitpass", "mode", "categories", "builders",
m@1
    19
                     "message"]
m@0
    20
marijn@4
    21
    def __init__(self, twitname, twitpass, mode="all", categories=None, builders=None,
buildmaster@7
    22
                 message="%(buildSlave)s reported build %(buildNumber)s as a %(result)s %(buildUrl)s"):
m@0
    23
        """
m@1
    24
        @type  twitname
m@1
    25
        @param twitname: The twitter user
m@1
    26
        @type  twitpass
m@1
    27
        @param twitpass: The twitter user's password. 
m@0
    28
        @type  mode: string (defaults to all)
m@0
    29
        @param mode: one of:
m@0
    30
                     - 'all': send mail about all builds, passing and failing
m@0
    31
                     - 'failing': only send mail about builds which fail
m@0
    32
                     - 'passing': only send mail about builds which succeed
m@0
    33
                     - 'problem': only send mail about a build which failed
m@0
    34
                     when the previous build passed
m@0
    35
m@1
    36
        @type  message: string
m@1
    37
        @param message: a string to be used as the subject line of the message.
m@1
    38
                       - %(buildSlave)s will be replaced with the name of the slave doing the building.
m@1
    39
                       - %(projectName)s with the name of the project
m@1
    40
                       - %(result)s will be the result of the build
m@1
    41
                       - %(buildUrl)s will be the url with the details of the build.
m@1
    42
                       - %(revision)s The revision of the change.
m@0
    43
m@0
    44
        @type  categories: list of strings
m@0
    45
        @param categories: a list of category names to serve status
m@0
    46
                           information for. Defaults to None (all
m@0
    47
                           categories). Use either builders or categories,
m@0
    48
                           but not both.
m@1
    49
                        builder which provoked the message.
m@0
    50
m@1
    51
        @type  builders: list of strings
m@1
    52
        @param builders: a list of builder names for which mail should be
m@1
    53
                         sent. Defaults to None (send mail for all builds).
m@1
    54
                         Use either builders or categories, but not both.
m@0
    55
        """
m@0
    56
m@0
    57
        base.StatusReceiverMultiService.__init__(self)
buildmaster@6
    58
        self.twitname = twitname
buildmaster@6
    59
        self.twitpass = twitpass
m@0
    60
        assert mode in ('all', 'failing', 'problem')
m@0
    61
        self.mode = mode
m@1
    62
        self.message = message
m@0
    63
        self.categories = categories
m@0
    64
        self.builders = builders
buildmaster@5
    65
        self.watched = []
m@0
    66
        self.status = None
m@0
    67
m@0
    68
        # you should either limit on builders or categories, not both
m@0
    69
        if self.builders != None and self.categories != None:
m@0
    70
            twlog.err("Please specify only builders to ignore or categories to include")
m@0
    71
            raise # FIXME: the asserts above do not raise some Exception either
m@0
    72
m@0
    73
    def setServiceParent(self, parent):
m@0
    74
        """
m@0
    75
        @type  parent: L{buildbot.master.BuildMaster}
m@0
    76
        """
m@0
    77
        base.StatusReceiverMultiService.setServiceParent(self, parent)
m@0
    78
        self.setup()
m@0
    79
m@0
    80
    def setup(self):
m@0
    81
        self.status = self.parent.getStatus()
m@0
    82
        self.status.subscribe(self)
m@0
    83
m@0
    84
    def disownServiceParent(self):
m@0
    85
        self.status.unsubscribe(self)
m@0
    86
        for w in self.watched:
m@0
    87
            w.unsubscribe(self)
m@0
    88
        return base.StatusReceiverMultiService.disownServiceParent(self)
m@0
    89
m@0
    90
    def builderAdded(self, name, builder):
m@0
    91
        # only subscribe to builders we are interested in
m@0
    92
        if self.categories != None and builder.category not in self.categories:
m@0
    93
            return None
m@0
    94
m@0
    95
        self.watched.append(builder)
m@0
    96
        return self # subscribe to this builder
m@0
    97
m@0
    98
    def builderRemoved(self, name):
m@0
    99
        pass
m@0
   100
m@0
   101
    def builderChangedState(self, name, state):
m@0
   102
        pass
m@0
   103
    def buildStarted(self, name, build):
m@0
   104
        pass
m@0
   105
    def buildFinished(self, name, build, results):
m@0
   106
        # here is where we actually do something.
m@0
   107
        builder = build.getBuilder()
m@0
   108
        if self.builders is not None and name not in self.builders:
m@0
   109
            return # ignore this build
m@0
   110
        if self.categories is not None and \
m@0
   111
               builder.category not in self.categories:
m@0
   112
            return # ignore this build
m@0
   113
m@0
   114
        if self.mode == "failing" and results != FAILURE:
m@0
   115
            return
m@0
   116
        if self.mode == "passing" and results != SUCCESS:
m@0
   117
            return
m@0
   118
        if self.mode == "problem":
m@0
   119
            if results != FAILURE:
m@0
   120
                return
m@0
   121
            prev = build.getPreviousBuild()
m@0
   122
            if prev and prev.getResults() == FAILURE:
m@0
   123
                return
m@0
   124
        # for testing purposes, buildMessage returns a Deferred that fires
m@0
   125
        # when the mail has been sent. To help unit tests, we return that
m@0
   126
        # Deferred here even though the normal IStatusReceiver.buildFinished
m@0
   127
        # signature doesn't do anything with it. If that changes (if
m@0
   128
        # .buildFinished's return value becomes significant), we need to
m@0
   129
        # rearrange this.
m@0
   130
        return self.buildMessage(name, build, results)
m@0
   131
m@0
   132
    def buildMessage(self, name, build, results):
m@0
   133
        projectName = self.status.getProjectName()
m@1
   134
        buildSlave = build.getSlavename()
m@1
   135
        buildUrl = self.status.getURLForThing(build)
buildmaster@7
   136
        buildNumber  = build.getNumber()
m@2
   137
m@2
   138
        ss = build.getSourceStamp()
m@2
   139
        if ss is None:
m@2
   140
            revision = "unavailable"
m@2
   141
        else:
m@2
   142
            revision = ""
m@2
   143
            if ss.revision:
m@2
   144
                source += ss.revision
m@2
   145
            else:
m@2
   146
                revision += "HEAD"
m@2
   147
m@2
   148
        if results == SUCCESS:
m@2
   149
            result = "success"
m@2
   150
        elif results == WARNINGS:
m@2
   151
            result = "warnings"
m@2
   152
        else:
m@2
   153
            res = "failure"
m@2
   154
m@2
   155
        text = self.message % {
m@1
   156
            'projectName': projectName,
m@1
   157
            'buildSlave':  buildSlave,
m@2
   158
            'result': result,
m@1
   159
            'revision': revision,
buildmaster@6
   160
            'buildUrl': buildUrl,
buildmaster@7
   161
            'buildNumber' : buildNumber,
m@1
   162
        }
m@1
   163
        
buildmaster@6
   164
        d = threads.deferToThread(_sendTwitter, self.twitname, self.twitpass, 
buildmaster@6
   165
                                  text)
m@0
   166
        return d
m@0
   167
m@1
   168
def _sendTwitter(twitname, twitpass, text):
m@1
   169
    api = twitter.Api(twitname, twitpass)
m@1
   170
    api.PostUpdate(text)
m@1
   171