1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 import os
27 import re
28 import sys
29 import time
30 from twisted.web import resource
31 from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION
32
34 contentType = "text/xml; charset=UTF-8"
36 data = self.content(request)
37 request.setHeader("content-type", self.contentType)
38 if request.method == "HEAD":
39 request.setHeader("content-length", len(data))
40 return ''
41 return data
42 docType = ''
44 data = ('<?xml version="1.0"?>\n')
45 return data
49 - def content(self, request):
50 data = self.docType
51 data += self.header(request)
52 data += self.body(request)
53 data += self.footer(request)
54 return data
55 - def body(self, request):
57
59 title = None
60 link = 'http://dummylink'
61 language = 'en-us'
62 description = 'Dummy rss'
63 status = None
64
65 - def __init__(self, status, categories=None, title=None):
76
77 - def getEnv(self, keys, fallback):
78 for key in keys:
79 if key in os.environ:
80 return os.environ[key]
81 return fallback
82
84 builds = []
85
86
87
88
89
90
91 allBuilderNames = self.status.getBuilderNames(categories=self.categories)
92 builders = [self.status.getBuilder(name) for name in allBuilderNames]
93
94
95
96
97
98 showBuilders = request.args.get("show", [])
99 showBuilders.extend(request.args.get("builder", []))
100 if showBuilders:
101 builders = [b for b in builders if b.name in showBuilders]
102
103
104
105
106 showCategories = request.args.get("category", [])
107 if showCategories:
108 builders = [b for b in builders if b.category in showCategories]
109
110 maxFeeds = 25
111
112
113
114
115 for b in builders:
116 lastbuild = b.getLastFinishedBuild()
117 if lastbuild is None:
118 continue
119
120 lastnr = lastbuild.getNumber()
121
122 totalbuilds = 0
123 i = lastnr
124 while i >= 0:
125 build = b.getBuild(i)
126 i -= 1
127 if not build:
128 continue
129
130 results = build.getResults()
131
132
133 if results == FAILURE:
134 totalbuilds += 1
135 builds.append(build)
136
137
138 if totalbuilds >= maxFeeds:
139 break
140
141
142
143
144 deco = [(build.getTimes(), build) for build in builds]
145 deco.sort()
146 deco.reverse()
147 builds = [build for (b1, build) in deco]
148
149 if builds:
150 builds = builds[:min(len(builds), maxFeeds)]
151 return builds
152
153 - def body (self, request):
154 data = ''
155 builds = self.getBuilds(request)
156
157 for build in builds:
158 start, finished = build.getTimes()
159 finishedTime = time.gmtime(int(finished))
160 link = re.sub(r'index.html', "", self.status.getURLForThing(build))
161
162
163 ss = build.getSourceStamp()
164 source = ""
165 if ss.branch:
166 source += "Branch %s " % ss.branch
167 if ss.revision:
168 source += "Revision %s " % str(ss.revision)
169 if ss.patch:
170 source += " (plus patch)"
171 if ss.changes:
172 pass
173 if (ss.branch is None and ss.revision is None and ss.patch is None
174 and not ss.changes):
175 source += "Latest revision "
176 got_revision = None
177 try:
178 got_revision = build.getProperty("got_revision")
179 except KeyError:
180 pass
181 if got_revision:
182 got_revision = str(got_revision)
183 if len(got_revision) > 40:
184 got_revision = "[revision string too long]"
185 source += "(Got Revision: %s)" % got_revision
186 title = ('%s failed on "%s"' %
187 (source, build.getBuilder().getName()))
188
189
190 if build.getLogs():
191 log = build.getLogs()[-1]
192 laststep = log.getStep().getName()
193 try:
194 lastlog = log.getText()
195 except IOError:
196
197 lastlog='<b>log file not available</b>'
198
199 lines = re.split('\n', lastlog)
200 lastlog = ''
201 for logline in lines[max(0, len(lines)-30):]:
202 lastlog = lastlog + logline + '<br/>'
203 lastlog = lastlog.replace('\n', '<br/>')
204
205 description = ''
206 description += ('Date: %s<br/><br/>' %
207 time.strftime("%a, %d %b %Y %H:%M:%S GMT",
208 finishedTime))
209 description += ('Full details available here: <a href="%s">%s</a><br/>' %
210 (self.link, self.projectName))
211 builder_summary_link = ('%s/builders/%s' %
212 (re.sub(r'/index.html', '', self.link),
213 build.getBuilder().getName()))
214 description += ('Build summary: <a href="%s">%s</a><br/><br/>' %
215 (builder_summary_link,
216 build.getBuilder().getName()))
217 description += ('Build details: <a href="%s">%s</a><br/><br/>' %
218 (link, link))
219 description += ('Author list: <b>%s</b><br/><br/>' %
220 ",".join(build.getResponsibleUsers()))
221 description += ('Failed step: <b>%s</b><br/><br/>' % laststep)
222 description += 'Last lines of the build log:<br/>'
223
224 data += self.item(title, description=description, lastlog=lastlog,
225 link=link, pubDate=finishedTime)
226
227 return data
228
229 - def item(self, title='', link='', description='', pubDate=''):
230 """Generates xml for one item in the feed."""
231
236
238 data = FeedResource.header(self, request)
239 data += ('<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">\n')
240 data += (' <channel>\n')
241 if self.title is None:
242 title = 'Build status of ' + self.projectName
243 else:
244 title = self.title
245 data += (' <title>%s</title>\n' % title)
246 if self.link is not None:
247 data += (' <link>%s</link>\n' % self.link)
248 link = re.sub(r'/index.html', '', self.link)
249 data += (' <atom:link href="%s/rss" rel="self" type="application/rss+xml"/>\n' % link)
250 if self.language is not None:
251 data += (' <language>%s</language>\n' % self.language)
252 if self.description is not None:
253 data += (' <description>%s</description>\n' % self.description)
254 if self.pubdate is not None:
255 rfc822_pubdate = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
256 self.pubdate)
257 data += (' <pubDate>%s</pubDate>\n' % rfc822_pubdate)
258 return data
259
261 data = (' <item>\n')
262 data += (' <title>%s</title>\n' % title)
263 if link is not None:
264 data += (' <link>%s</link>\n' % link)
265 if (description is not None and lastlog is not None):
266 lastlog = re.sub(r'<br/>', "\n", lastlog)
267 lastlog = re.sub(r'&', "&", lastlog)
268 lastlog = re.sub(r"'", "'", lastlog)
269 lastlog = re.sub(r'"', """, lastlog)
270 lastlog = re.sub(r'<', '<', lastlog)
271 lastlog = re.sub(r'>', '>', lastlog)
272 lastlog = lastlog.replace('\n', '<br/>')
273 content = '<![CDATA['
274 content += description
275 content += lastlog
276 content += ']]>'
277 data += (' <description>%s</description>\n' % content)
278 if pubDate is not None:
279 rfc822pubDate = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
280 pubDate)
281 data += (' <pubDate>%s</pubDate>\n' % rfc822pubDate)
282
283 guid = ('tag:%s@%s,%s:%s' % (self.user, self.hostname,
284 time.strftime("%Y-%m-%d", pubDate),
285 time.strftime("%Y%m%d%H%M%S",
286 pubDate)))
287 data += (' <guid isPermaLink="false">%s</guid>\n' % guid)
288 data += (' </item>\n')
289 return data
290
295
297 - def __init__(self, status, categories=None, title=None):
300
302 data = FeedResource.header(self, request)
303 data += '<feed xmlns="http://www.w3.org/2005/Atom">\n'
304 data += (' <id>%s</id>\n' % self.link)
305 if self.title is None:
306 title = 'Build status of ' + self.projectName
307 else:
308 title = self.title
309 data += (' <title>%s</title>\n' % title)
310 if self.link is not None:
311 link = re.sub(r'/index.html', '', self.link)
312 data += (' <link rel="self" href="%s/atom"/>\n' % link)
313 data += (' <link rel="alternate" href="%s/"/>\n' % link)
314 if self.description is not None:
315 data += (' <subtitle>%s</subtitle>\n' % self.description)
316 if self.pubdate is not None:
317 rfc3339_pubdate = time.strftime("%Y-%m-%dT%H:%M:%SZ",
318 self.pubdate)
319 data += (' <updated>%s</updated>\n' % rfc3339_pubdate)
320 data += (' <author>\n')
321 data += (' <name>Build Bot</name>\n')
322 data += (' </author>\n')
323 return data
324
325 - def item(self, title='', link='', description='', lastlog='', pubDate=''):
326 data = (' <entry>\n')
327 data += (' <title>%s</title>\n' % title)
328 if link is not None:
329 data += (' <link href="%s"/>\n' % link)
330 if (description is not None and lastlog is not None):
331 lastlog = re.sub(r'<br/>', "\n", lastlog)
332 lastlog = re.sub(r'&', "&", lastlog)
333 lastlog = re.sub(r"'", "'", lastlog)
334 lastlog = re.sub(r'"', """, lastlog)
335 lastlog = re.sub(r'<', '<', lastlog)
336 lastlog = re.sub(r'>', '>', lastlog)
337 data += (' <content type="xhtml">\n')
338 data += (' <div xmlns="http://www.w3.org/1999/xhtml">\n')
339 data += (' %s\n' % description)
340 data += (' <pre xml:space="preserve">%s</pre>\n' % lastlog)
341 data += (' </div>\n')
342 data += (' </content>\n')
343 if pubDate is not None:
344 rfc3339pubDate = time.strftime("%Y-%m-%dT%H:%M:%SZ",
345 pubDate)
346 data += (' <updated>%s</updated>\n' % rfc3339pubDate)
347
348
349 guid = ('tag:%s@%s,%s:%s' % (self.user, self.hostname,
350 time.strftime("%Y-%m-%d", pubDate),
351 time.strftime("%Y%m%d%H%M%S",
352 pubDate)))
353 data += (' <id>%s</id>\n' % guid)
354 data += (' <author>\n')
355 data += (' <name>Build Bot</name>\n')
356 data += (' </author>\n')
357 data += (' </entry>\n')
358 return data
359
363