1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import time
17 from xml.dom import minidom
18
19 from twisted.python import log
20 from twisted.internet import defer
21 from twisted.web import client
22
23 from buildbot.changes import base
24 from buildbot.util import epoch2datetime
27 - def __init__(self, value="InvalidResultError"):
30 return repr(self.value)
31
34
37
40
42 """I hold a list of CiNodes"""
45
47 if len(self.nodes) != len(other.nodes):
48 return False
49 for i in range(len(self.nodes)):
50 if self.nodes[i].log != other.nodes[i].log \
51 or self.nodes[i].who != other.nodes[i].who \
52 or self.nodes[i].date != other.nodes[i].date \
53 or len(self.nodes[i].files) != len(other.nodes[i].files):
54 return -1
55
56 for j in range(len(self.nodes[i].files)):
57 if self.nodes[i].files[j].revision \
58 != other.nodes[i].files[j].revision \
59 or self.nodes[i].files[j].filename \
60 != other.nodes[i].files[j].filename:
61 return -1
62
63 return 0
64
66 """I hold information baout one <ci> node, including a list of files"""
67 - def __init__(self, log="", who="", date=0, files=[]):
68 self.log = log
69 self.who = who
70 self.date = date
71 self.files = files
72
74 """I hold information about one <f> node"""
75 - def __init__(self, revision="", filename=""):
78
80 """I parse the XML result from a bonsai cvsquery."""
81
83 try:
84
85
86
87
88 data = data.decode("latin1")
89 data = data.encode("ascii", "replace")
90 self.dom = minidom.parseString(data)
91 log.msg(data)
92 except:
93 raise InvalidResultError("Malformed XML in result")
94
95 self.ciNodes = self.dom.getElementsByTagName("ci")
96 self.currentCiNode = None
97 self.fileNodes = None
98 self.currentFileNode = None
99 self.bonsaiResult = self._parseData()
100
102 return self.bonsaiResult
103
105 """Returns data from a Bonsai cvsquery in a BonsaiResult object"""
106 nodes = []
107 try:
108 while self._nextCiNode():
109 files = []
110 try:
111 while self._nextFileNode():
112 files.append(FileNode(self._getRevision(),
113 self._getFilename()))
114 except NoMoreFileNodes:
115 pass
116 except InvalidResultError:
117 raise
118 cinode = CiNode(self._getLog(), self._getWho(),
119 self._getDate(), files)
120
121 if not cinode.log and nodes and \
122 not nodes[-1].log and \
123 cinode.who == nodes[-1].who and \
124 cinode.date == nodes[-1].date:
125 nodes[-1].files += cinode.files
126 else:
127 nodes.append(cinode)
128
129 except NoMoreCiNodes:
130 pass
131 except (InvalidResultError, EmptyResult):
132 raise
133
134 return BonsaiResult(nodes)
135
136
138 """Iterates to the next <ci> node and fills self.fileNodes with
139 child <f> nodes"""
140 try:
141 self.currentCiNode = self.ciNodes.pop(0)
142 if len(self.currentCiNode.getElementsByTagName("files")) > 1:
143 raise InvalidResultError("Multiple <files> for one <ci>")
144
145 self.fileNodes = self.currentCiNode.getElementsByTagName("f")
146 except IndexError:
147
148 if not self.currentCiNode:
149 raise EmptyResult
150 else:
151 raise NoMoreCiNodes
152
153 return True
154
156 """Iterates to the next <f> node"""
157 try:
158 self.currentFileNode = self.fileNodes.pop(0)
159 except IndexError:
160 raise NoMoreFileNodes
161
162 return True
163
165 """Returns the log of the current <ci> node"""
166 logs = self.currentCiNode.getElementsByTagName("log")
167 if len(logs) < 1:
168 raise InvalidResultError("No log present")
169 elif len(logs) > 1:
170 raise InvalidResultError("Multiple logs present")
171
172
173 if logs[0].firstChild:
174 return logs[0].firstChild.data
175 return ''
176
178 """Returns the e-mail address of the commiter"""
179
180 return str(self.currentCiNode.getAttribute("who"))
181
183 """Returns the date (unix time) of the commit"""
184
185 try:
186 commitDate = int(self.currentCiNode.getAttribute("date"))
187 except ValueError:
188 raise InvalidResultError
189
190 return commitDate
191
193 """Returns the filename of the current <f> node"""
194 try:
195 filename = self.currentFileNode.firstChild.data
196 except AttributeError:
197 raise InvalidResultError("Missing filename")
198
199 return filename
200
202 return self.currentFileNode.getAttribute("rev")
203
206 compare_attrs = ["bonsaiURL", "pollInterval", "tree",
207 "module", "branch", "cvsroot"]
208
209 - def __init__(self, bonsaiURL, module, branch, tree="default",
210 cvsroot="/cvsroot", pollInterval=30, project=''):
211 self.bonsaiURL = bonsaiURL
212 self.module = module
213 self.branch = branch
214 self.tree = tree
215 self.cvsroot = cvsroot
216 self.repository = module != 'all' and module or ''
217 self.pollInterval = pollInterval
218 self.lastChange = time.time()
219 self.lastPoll = time.time()
220
222 str = ""
223 str += "Getting changes from the Bonsai service running at %s " \
224 % self.bonsaiURL
225 str += "<br>Using tree: %s, branch: %s, and module: %s" % (self.tree, \
226 self.branch, self.module)
227 return str
228
230 d = self._get_changes()
231 d.addCallback(self._process_changes)
232 return d
233
235 args = ["treeid=%s" % self.tree, "module=%s" % self.module,
236 "branch=%s" % self.branch, "branchtype=match",
237 "sortby=Date", "date=explicit",
238 "mindate=%d" % self.lastChange,
239 "maxdate=%d" % int(time.time()),
240 "cvsroot=%s" % self.cvsroot, "xml=1"]
241
242 url = self.bonsaiURL
243 url += "/cvsquery.cgi?"
244 url += "&".join(args)
245
246 return url
247
249 url = self._make_url()
250 log.msg("Polling Bonsai tree at %s" % url)
251
252 self.lastPoll = time.time()
253
254 return client.getPage(url, timeout=self.pollInterval)
255
256 @defer.deferredGenerator
258 try:
259 bp = BonsaiParser(query)
260 result = bp.getData()
261 except InvalidResultError, e:
262 log.msg("Could not process Bonsai query: " + e.value)
263 return
264 except EmptyResult:
265 return
266
267 for cinode in result.nodes:
268 files = [file.filename + ' (revision '+file.revision+')'
269 for file in cinode.files]
270 self.lastChange = self.lastPoll
271 w = defer.waitForDeferred(
272 self.master.addChange(author = cinode.who,
273 files = files,
274 comments = cinode.log,
275 when_timestamp = epoch2datetime(cinode.date),
276 branch = self.branch))
277 yield w
278 w.getResult()
279