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