Renaming to avoid clashes.
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/bbtwitter.py Sat Feb 07 20:14:27 2009 +0100
1.3 @@ -0,0 +1,167 @@
1.4 +# -*- test-case-name: buildbot.test.test_status -*-
1.5 +
1.6 +# the email.MIMEMultipart module is only available in python-2.2.2 and later
1.7 +
1.8 +import twitter
1.9 +
1.10 +from zope.interface import implements
1.11 +from twisted.internet import defer, threads
1.12 +from twisted.mail.smtp import sendmail
1.13 +from twisted.python import log as twlog
1.14 +
1.15 +from buildbot import interfaces, util
1.16 +from buildbot.status import base
1.17 +from buildbot.status.builder import FAILURE, SUCCESS, WARNINGS
1.18 +
1.19 +
1.20 +class TwitterNotifier(base.StatusReceiverMultiService):
1.21 +
1.22 + compare_attrs = ["twitname", "twitpass", "mode", "categories". "builders".
1.23 + "message"]
1.24 +
1.25 + def __init__(self, twitname, twitpass, mode="all", categories=None, builders=None
1.26 + message="%(projectName)s, %(buildSlave)s was a %(result)s %(buildUrl)s"):
1.27 + """
1.28 + @type twitname
1.29 + @param twitname: The twitter user
1.30 + @type twitpass
1.31 + @param twitpass: The twitter user's password.
1.32 + @type mode: string (defaults to all)
1.33 + @param mode: one of:
1.34 + - 'all': send mail about all builds, passing and failing
1.35 + - 'failing': only send mail about builds which fail
1.36 + - 'passing': only send mail about builds which succeed
1.37 + - 'problem': only send mail about a build which failed
1.38 + when the previous build passed
1.39 +
1.40 + @type message: string
1.41 + @param message: a string to be used as the subject line of the message.
1.42 + - %(buildSlave)s will be replaced with the name of the slave doing the building.
1.43 + - %(projectName)s with the name of the project
1.44 + - %(result)s will be the result of the build
1.45 + - %(buildUrl)s will be the url with the details of the build.
1.46 + - %(revision)s The revision of the change.
1.47 +
1.48 + @type categories: list of strings
1.49 + @param categories: a list of category names to serve status
1.50 + information for. Defaults to None (all
1.51 + categories). Use either builders or categories,
1.52 + but not both.
1.53 + builder which provoked the message.
1.54 +
1.55 + @type builders: list of strings
1.56 + @param builders: a list of builder names for which mail should be
1.57 + sent. Defaults to None (send mail for all builds).
1.58 + Use either builders or categories, but not both.
1.59 + """
1.60 +
1.61 + base.StatusReceiverMultiService.__init__(self)
1.62 + assert mode in ('all', 'failing', 'problem')
1.63 + self.mode = mode
1.64 + self.message = message
1.65 + self.addUrl = addUrl
1.66 + self.categories = categories
1.67 + self.builders = builders
1.68 + self.status = None
1.69 +
1.70 + # you should either limit on builders or categories, not both
1.71 + if self.builders != None and self.categories != None:
1.72 + twlog.err("Please specify only builders to ignore or categories to include")
1.73 + raise # FIXME: the asserts above do not raise some Exception either
1.74 +
1.75 + def setServiceParent(self, parent):
1.76 + """
1.77 + @type parent: L{buildbot.master.BuildMaster}
1.78 + """
1.79 + base.StatusReceiverMultiService.setServiceParent(self, parent)
1.80 + self.setup()
1.81 +
1.82 + def setup(self):
1.83 + self.status = self.parent.getStatus()
1.84 + self.status.subscribe(self)
1.85 +
1.86 + def disownServiceParent(self):
1.87 + self.status.unsubscribe(self)
1.88 + for w in self.watched:
1.89 + w.unsubscribe(self)
1.90 + return base.StatusReceiverMultiService.disownServiceParent(self)
1.91 +
1.92 + def builderAdded(self, name, builder):
1.93 + # only subscribe to builders we are interested in
1.94 + if self.categories != None and builder.category not in self.categories:
1.95 + return None
1.96 +
1.97 + self.watched.append(builder)
1.98 + return self # subscribe to this builder
1.99 +
1.100 + def builderRemoved(self, name):
1.101 + pass
1.102 +
1.103 + def builderChangedState(self, name, state):
1.104 + pass
1.105 + def buildStarted(self, name, build):
1.106 + pass
1.107 + def buildFinished(self, name, build, results):
1.108 + # here is where we actually do something.
1.109 + builder = build.getBuilder()
1.110 + if self.builders is not None and name not in self.builders:
1.111 + return # ignore this build
1.112 + if self.categories is not None and \
1.113 + builder.category not in self.categories:
1.114 + return # ignore this build
1.115 +
1.116 + if self.mode == "failing" and results != FAILURE:
1.117 + return
1.118 + if self.mode == "passing" and results != SUCCESS:
1.119 + return
1.120 + if self.mode == "problem":
1.121 + if results != FAILURE:
1.122 + return
1.123 + prev = build.getPreviousBuild()
1.124 + if prev and prev.getResults() == FAILURE:
1.125 + return
1.126 + # for testing purposes, buildMessage returns a Deferred that fires
1.127 + # when the mail has been sent. To help unit tests, we return that
1.128 + # Deferred here even though the normal IStatusReceiver.buildFinished
1.129 + # signature doesn't do anything with it. If that changes (if
1.130 + # .buildFinished's return value becomes significant), we need to
1.131 + # rearrange this.
1.132 + return self.buildMessage(name, build, results)
1.133 +
1.134 + def buildMessage(self, name, build, results):
1.135 + projectName = self.status.getProjectName()
1.136 + buildSlave = build.getSlavename()
1.137 + buildUrl = self.status.getURLForThing(build)
1.138 +
1.139 + ss = build.getSourceStamp()
1.140 + if ss is None:
1.141 + revision = "unavailable"
1.142 + else:
1.143 + revision = ""
1.144 + if ss.revision:
1.145 + source += ss.revision
1.146 + else:
1.147 + revision += "HEAD"
1.148 +
1.149 + if results == SUCCESS:
1.150 + result = "success"
1.151 + elif results == WARNINGS:
1.152 + result = "warnings"
1.153 + else:
1.154 + res = "failure"
1.155 +
1.156 + text = self.message % {
1.157 + 'projectName': projectName,
1.158 + 'buildSlave': buildSlave,
1.159 + 'result': result,
1.160 + 'revision': revision,
1.161 + 'detailUrl': buildUrl,
1.162 + }
1.163 +
1.164 + d = threads.deferToThread(_sendTwitter, twitname, twitpass, text)
1.165 + return d
1.166 +
1.167 +def _sendTwitter(twitname, twitpass, text):
1.168 + api = twitter.Api(twitname, twitpass)
1.169 + api.PostUpdate(text)
1.170 +
2.1 --- a/twitter.py Sat Feb 07 19:52:34 2009 +0100
2.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2.3 @@ -1,167 +0,0 @@
2.4 -# -*- test-case-name: buildbot.test.test_status -*-
2.5 -
2.6 -# the email.MIMEMultipart module is only available in python-2.2.2 and later
2.7 -
2.8 -import twitter
2.9 -
2.10 -from zope.interface import implements
2.11 -from twisted.internet import defer, threads
2.12 -from twisted.mail.smtp import sendmail
2.13 -from twisted.python import log as twlog
2.14 -
2.15 -from buildbot import interfaces, util
2.16 -from buildbot.status import base
2.17 -from buildbot.status.builder import FAILURE, SUCCESS, WARNINGS
2.18 -
2.19 -
2.20 -class TwitterNotifier(base.StatusReceiverMultiService):
2.21 -
2.22 - compare_attrs = ["twitname", "twitpass", "mode", "categories". "builders".
2.23 - "message"]
2.24 -
2.25 - def __init__(self, twitname, twitpass, mode="all", categories=None, builders=None
2.26 - message="%(projectName)s, %(buildSlave)s was a %(result)s %(buildUrl)s"):
2.27 - """
2.28 - @type twitname
2.29 - @param twitname: The twitter user
2.30 - @type twitpass
2.31 - @param twitpass: The twitter user's password.
2.32 - @type mode: string (defaults to all)
2.33 - @param mode: one of:
2.34 - - 'all': send mail about all builds, passing and failing
2.35 - - 'failing': only send mail about builds which fail
2.36 - - 'passing': only send mail about builds which succeed
2.37 - - 'problem': only send mail about a build which failed
2.38 - when the previous build passed
2.39 -
2.40 - @type message: string
2.41 - @param message: a string to be used as the subject line of the message.
2.42 - - %(buildSlave)s will be replaced with the name of the slave doing the building.
2.43 - - %(projectName)s with the name of the project
2.44 - - %(result)s will be the result of the build
2.45 - - %(buildUrl)s will be the url with the details of the build.
2.46 - - %(revision)s The revision of the change.
2.47 -
2.48 - @type categories: list of strings
2.49 - @param categories: a list of category names to serve status
2.50 - information for. Defaults to None (all
2.51 - categories). Use either builders or categories,
2.52 - but not both.
2.53 - builder which provoked the message.
2.54 -
2.55 - @type builders: list of strings
2.56 - @param builders: a list of builder names for which mail should be
2.57 - sent. Defaults to None (send mail for all builds).
2.58 - Use either builders or categories, but not both.
2.59 - """
2.60 -
2.61 - base.StatusReceiverMultiService.__init__(self)
2.62 - assert mode in ('all', 'failing', 'problem')
2.63 - self.mode = mode
2.64 - self.message = message
2.65 - self.addUrl = addUrl
2.66 - self.categories = categories
2.67 - self.builders = builders
2.68 - self.status = None
2.69 -
2.70 - # you should either limit on builders or categories, not both
2.71 - if self.builders != None and self.categories != None:
2.72 - twlog.err("Please specify only builders to ignore or categories to include")
2.73 - raise # FIXME: the asserts above do not raise some Exception either
2.74 -
2.75 - def setServiceParent(self, parent):
2.76 - """
2.77 - @type parent: L{buildbot.master.BuildMaster}
2.78 - """
2.79 - base.StatusReceiverMultiService.setServiceParent(self, parent)
2.80 - self.setup()
2.81 -
2.82 - def setup(self):
2.83 - self.status = self.parent.getStatus()
2.84 - self.status.subscribe(self)
2.85 -
2.86 - def disownServiceParent(self):
2.87 - self.status.unsubscribe(self)
2.88 - for w in self.watched:
2.89 - w.unsubscribe(self)
2.90 - return base.StatusReceiverMultiService.disownServiceParent(self)
2.91 -
2.92 - def builderAdded(self, name, builder):
2.93 - # only subscribe to builders we are interested in
2.94 - if self.categories != None and builder.category not in self.categories:
2.95 - return None
2.96 -
2.97 - self.watched.append(builder)
2.98 - return self # subscribe to this builder
2.99 -
2.100 - def builderRemoved(self, name):
2.101 - pass
2.102 -
2.103 - def builderChangedState(self, name, state):
2.104 - pass
2.105 - def buildStarted(self, name, build):
2.106 - pass
2.107 - def buildFinished(self, name, build, results):
2.108 - # here is where we actually do something.
2.109 - builder = build.getBuilder()
2.110 - if self.builders is not None and name not in self.builders:
2.111 - return # ignore this build
2.112 - if self.categories is not None and \
2.113 - builder.category not in self.categories:
2.114 - return # ignore this build
2.115 -
2.116 - if self.mode == "failing" and results != FAILURE:
2.117 - return
2.118 - if self.mode == "passing" and results != SUCCESS:
2.119 - return
2.120 - if self.mode == "problem":
2.121 - if results != FAILURE:
2.122 - return
2.123 - prev = build.getPreviousBuild()
2.124 - if prev and prev.getResults() == FAILURE:
2.125 - return
2.126 - # for testing purposes, buildMessage returns a Deferred that fires
2.127 - # when the mail has been sent. To help unit tests, we return that
2.128 - # Deferred here even though the normal IStatusReceiver.buildFinished
2.129 - # signature doesn't do anything with it. If that changes (if
2.130 - # .buildFinished's return value becomes significant), we need to
2.131 - # rearrange this.
2.132 - return self.buildMessage(name, build, results)
2.133 -
2.134 - def buildMessage(self, name, build, results):
2.135 - projectName = self.status.getProjectName()
2.136 - buildSlave = build.getSlavename()
2.137 - buildUrl = self.status.getURLForThing(build)
2.138 -
2.139 - ss = build.getSourceStamp()
2.140 - if ss is None:
2.141 - revision = "unavailable"
2.142 - else:
2.143 - revision = ""
2.144 - if ss.revision:
2.145 - source += ss.revision
2.146 - else:
2.147 - revision += "HEAD"
2.148 -
2.149 - if results == SUCCESS:
2.150 - result = "success"
2.151 - elif results == WARNINGS:
2.152 - result = "warnings"
2.153 - else:
2.154 - res = "failure"
2.155 -
2.156 - text = self.message % {
2.157 - 'projectName': projectName,
2.158 - 'buildSlave': buildSlave,
2.159 - 'result': result,
2.160 - 'revision': revision,
2.161 - 'detailUrl': buildUrl,
2.162 - }
2.163 -
2.164 - d = threads.deferToThread(_sendTwitter, twitname, twitpass, text)
2.165 - return d
2.166 -
2.167 -def _sendTwitter(twitname, twitpass, text):
2.168 - api = twitter.Api(twitname, twitpass)
2.169 - api.PostUpdate(text)
2.170 -