| OLD | NEW |
| (Empty) |
| 1 | |
| 2 import os.path | |
| 3 | |
| 4 from zope.interface import implements | |
| 5 from twisted.cred import credentials | |
| 6 from twisted.spread import pb | |
| 7 from twisted.application.internet import TCPClient | |
| 8 from twisted.python import log | |
| 9 | |
| 10 import cvstoys.common # to make sure VersionedPatch gets registered | |
| 11 | |
| 12 from buildbot.interfaces import IChangeSource | |
| 13 from buildbot.pbutil import ReconnectingPBClientFactory | |
| 14 from buildbot.changes.changes import Change | |
| 15 from buildbot import util | |
| 16 | |
| 17 class FreshCVSListener(pb.Referenceable): | |
| 18 def remote_notify(self, root, files, message, user): | |
| 19 try: | |
| 20 self.source.notify(root, files, message, user) | |
| 21 except Exception, e: | |
| 22 print "notify failed" | |
| 23 log.err() | |
| 24 | |
| 25 def remote_goodbye(self, message): | |
| 26 pass | |
| 27 | |
| 28 class FreshCVSConnectionFactory(ReconnectingPBClientFactory): | |
| 29 | |
| 30 def gotPerspective(self, perspective): | |
| 31 log.msg("connected to FreshCVS daemon") | |
| 32 ReconnectingPBClientFactory.gotPerspective(self, perspective) | |
| 33 self.source.connected = True | |
| 34 # TODO: freshcvs-1.0.10 doesn't handle setFilter correctly, it will | |
| 35 # be fixed in the upcoming 1.0.11 . I haven't been able to test it | |
| 36 # to make sure the failure mode is survivable, so I'll just leave | |
| 37 # this out for now. | |
| 38 return | |
| 39 if self.source.prefix is not None: | |
| 40 pathfilter = "^%s" % self.source.prefix | |
| 41 d = perspective.callRemote("setFilter", | |
| 42 None, pathfilter, None) | |
| 43 # ignore failures, setFilter didn't work in 1.0.10 and this is | |
| 44 # just an optimization anyway | |
| 45 d.addErrback(lambda f: None) | |
| 46 | |
| 47 def clientConnectionLost(self, connector, reason): | |
| 48 ReconnectingPBClientFactory.clientConnectionLost(self, connector, | |
| 49 reason) | |
| 50 self.source.connected = False | |
| 51 | |
| 52 class FreshCVSSourceNewcred(TCPClient, util.ComparableMixin): | |
| 53 """This source will connect to a FreshCVS server associated with one or | |
| 54 more CVS repositories. Each time a change is committed to a repository, | |
| 55 the server will send us a message describing the change. This message is | |
| 56 used to build a Change object, which is then submitted to the | |
| 57 ChangeMaster. | |
| 58 | |
| 59 This class handles freshcvs daemons which use newcred. CVSToys-1.0.9 | |
| 60 does not, later versions might. | |
| 61 """ | |
| 62 | |
| 63 implements(IChangeSource) | |
| 64 compare_attrs = ["host", "port", "username", "password", "prefix"] | |
| 65 | |
| 66 changemaster = None # filled in when we're added | |
| 67 connected = False | |
| 68 | |
| 69 def __init__(self, host, port, user, passwd, prefix=None): | |
| 70 self.host = host | |
| 71 self.port = port | |
| 72 self.username = user | |
| 73 self.password = passwd | |
| 74 if prefix is not None and not prefix.endswith("/"): | |
| 75 log.msg("WARNING: prefix '%s' should probably end with a slash" \ | |
| 76 % prefix) | |
| 77 self.prefix = prefix | |
| 78 self.listener = l = FreshCVSListener() | |
| 79 l.source = self | |
| 80 self.factory = f = FreshCVSConnectionFactory() | |
| 81 f.source = self | |
| 82 self.creds = credentials.UsernamePassword(user, passwd) | |
| 83 f.startLogin(self.creds, client=l) | |
| 84 TCPClient.__init__(self, host, port, f) | |
| 85 | |
| 86 def __repr__(self): | |
| 87 return "<FreshCVSSource where=%s, prefix=%s>" % \ | |
| 88 ((self.host, self.port), self.prefix) | |
| 89 | |
| 90 def describe(self): | |
| 91 online = "" | |
| 92 if not self.connected: | |
| 93 online = " [OFFLINE]" | |
| 94 return "freshcvs %s:%s%s" % (self.host, self.port, online) | |
| 95 | |
| 96 def notify(self, root, files, message, user): | |
| 97 pathnames = [] | |
| 98 isdir = 0 | |
| 99 for f in files: | |
| 100 if not isinstance(f, (cvstoys.common.VersionedPatch, | |
| 101 cvstoys.common.Directory)): | |
| 102 continue | |
| 103 pathname, filename = f.pathname, f.filename | |
| 104 #r1, r2 = getattr(f, 'r1', None), getattr(f, 'r2', None) | |
| 105 if isinstance(f, cvstoys.common.Directory): | |
| 106 isdir = 1 | |
| 107 path = os.path.join(pathname, filename) | |
| 108 log.msg("FreshCVS notify '%s'" % path) | |
| 109 if self.prefix: | |
| 110 if path.startswith(self.prefix): | |
| 111 path = path[len(self.prefix):] | |
| 112 else: | |
| 113 continue | |
| 114 pathnames.append(path) | |
| 115 if pathnames: | |
| 116 # now() is close enough: FreshCVS *is* realtime, after all | |
| 117 when=util.now() | |
| 118 c = Change(user, pathnames, message, isdir, when=when) | |
| 119 self.parent.addChange(c) | |
| 120 | |
| 121 class FreshCVSSourceOldcred(FreshCVSSourceNewcred): | |
| 122 """This is for older freshcvs daemons (from CVSToys-1.0.9 and earlier). | |
| 123 """ | |
| 124 | |
| 125 def __init__(self, host, port, user, passwd, | |
| 126 serviceName="cvstoys.notify", prefix=None): | |
| 127 self.host = host | |
| 128 self.port = port | |
| 129 self.prefix = prefix | |
| 130 self.listener = l = FreshCVSListener() | |
| 131 l.source = self | |
| 132 self.factory = f = FreshCVSConnectionFactory() | |
| 133 f.source = self | |
| 134 f.startGettingPerspective(user, passwd, serviceName, client=l) | |
| 135 TCPClient.__init__(self, host, port, f) | |
| 136 | |
| 137 def __repr__(self): | |
| 138 return "<FreshCVSSourceOldcred where=%s, prefix=%s>" % \ | |
| 139 ((self.host, self.port), self.prefix) | |
| 140 | |
| 141 # this is suitable for CVSToys-1.0.10 and later. If you run CVSToys-1.0.9 or | |
| 142 # earlier, use FreshCVSSourceOldcred instead. | |
| 143 FreshCVSSource = FreshCVSSourceNewcred | |
| 144 | |
| OLD | NEW |