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 |