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, html
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 description = ''
190 description += ('Date: %s<br/><br/>' %
191 time.strftime("%a, %d %b %Y %H:%M:%S GMT",
192 finishedTime))
193 description += ('Full details available here: <a href="%s">%s</a><br/>' %
194 (self.link, self.projectName))
195 builder_summary_link = ('%s/builders/%s' %
196 (re.sub(r'/index.html', '', self.link),
197 build.getBuilder().getName()))
198 description += ('Build summary: <a href="%s">%s</a><br/><br/>' %
199 (builder_summary_link,
200 build.getBuilder().getName()))
201 description += ('Build details: <a href="%s">%s</a><br/><br/>' %
202 (link, link))
203 description += ('Author list: <b>%s</b><br/><br/>' %
204 ",".join(build.getResponsibleUsers()))
205
206
207 lastlog = ''
208 for s in build.getSteps():
209 if s.getResults()[0] == FAILURE:
210 description += ('Failed step: <b>%s</b><br/>' % s.getName())
211
212
213 for log in s.getLogs():
214 lastlog += ('Last lines of build log "%s":<br/>' % log.getName())
215 try:
216 logdata = log.getText()
217 except IOError:
218
219 logdata ='<b>log file not available</b>'
220
221 lastlines = logdata.split('\n')[-30:]
222 lastlog += '<br/>'.join(lastlines)
223 lastlog += '<br/>'
224 description += '<br/>'
225
226 data += self.item(title, description=description, lastlog=lastlog,
227 link=link, pubDate=finishedTime)
228
229 return data
230
231 - def item(self, title='', link='', description='', pubDate=''):
232 """Generates xml for one item in the feed."""
233
238
240 data = FeedResource.header(self, request)
241 data += ('<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">\n')
242 data += (' <channel>\n')
243 if self.title is None:
244 title = 'Build status of ' + self.projectName
245 else:
246 title = self.title
247 data += (' <title>%s</title>\n' % title)
248 if self.link is not None:
249 data += (' <link>%s</link>\n' % self.link)
250 link = re.sub(r'/index.html', '', self.link)
251 data += (' <atom:link href="%s/rss" rel="self" type="application/rss+xml"/>\n' % link)
252 if self.language is not None:
253 data += (' <language>%s</language>\n' % self.language)
254 if self.description is not None:
255 data += (' <description>%s</description>\n' % self.description)
256 if self.pubdate is not None:
257 rfc822_pubdate = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
258 self.pubdate)
259 data += (' <pubDate>%s</pubDate>\n' % rfc822_pubdate)
260 return data
261
263 data = (' <item>\n')
264 data += (' <title>%s</title>\n' % title)
265 if link is not None:
266 data += (' <link>%s</link>\n' % link)
267 if (description is not None and lastlog is not None):
268 lastlog = lastlog.replace('<br/>', '\n')
269 lastlog = html.escape(lastlog)
270 lastlog = lastlog.replace('\n', '<br/>')
271 content = '<![CDATA['
272 content += description
273 content += lastlog
274 content += ']]>'
275 data += (' <description>%s</description>\n' % content)
276 if pubDate is not None:
277 rfc822pubDate = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
278 pubDate)
279 data += (' <pubDate>%s</pubDate>\n' % rfc822pubDate)
280
281 guid = ('tag:%s@%s,%s:%s' % (self.user, self.hostname,
282 time.strftime("%Y-%m-%d", pubDate),
283 time.strftime("%Y%m%d%H%M%S",
284 pubDate)))
285 data += (' <guid isPermaLink="false">%s</guid>\n' % guid)
286 data += (' </item>\n')
287 return data
288
293
295 - def __init__(self, status, categories=None, title=None):
298
300 data = FeedResource.header(self, request)
301 data += '<feed xmlns="http://www.w3.org/2005/Atom">\n'
302 data += (' <id>%s</id>\n' % self.link)
303 if self.title is None:
304 title = 'Build status of ' + self.projectName
305 else:
306 title = self.title
307 data += (' <title>%s</title>\n' % title)
308 if self.link is not None:
309 link = re.sub(r'/index.html', '', self.link)
310 data += (' <link rel="self" href="%s/atom"/>\n' % link)
311 data += (' <link rel="alternate" href="%s/"/>\n' % link)
312 if self.description is not None:
313 data += (' <subtitle>%s</subtitle>\n' % self.description)
314 if self.pubdate is not None:
315 rfc3339_pubdate = time.strftime("%Y-%m-%dT%H:%M:%SZ",
316 self.pubdate)
317 data += (' <updated>%s</updated>\n' % rfc3339_pubdate)
318 data += (' <author>\n')
319 data += (' <name>Build Bot</name>\n')
320 data += (' </author>\n')
321 return data
322
323 - def item(self, title='', link='', description='', lastlog='', pubDate=''):
324 data = (' <entry>\n')
325 data += (' <title>%s</title>\n' % title)
326 if link is not None:
327 data += (' <link href="%s"/>\n' % link)
328 if (description is not None and lastlog is not None):
329 lastlog = lastlog.replace('<br/>', '\n')
330 lastlog = html.escape(lastlog)
331 lastlog = lastlog.replace('\n', '<br/>')
332 data += (' <content type="xhtml">\n')
333 data += (' <div xmlns="http://www.w3.org/1999/xhtml">\n')
334 data += (' %s\n' % description)
335 data += (' <pre xml:space="preserve">%s</pre>\n' % lastlog)
336 data += (' </div>\n')
337 data += (' </content>\n')
338 if pubDate is not None:
339 rfc3339pubDate = time.strftime("%Y-%m-%dT%H:%M:%SZ",
340 pubDate)
341 data += (' <updated>%s</updated>\n' % rfc3339pubDate)
342
343
344 guid = ('tag:%s@%s,%s:%s' % (self.user, self.hostname,
345 time.strftime("%Y-%m-%d", pubDate),
346 time.strftime("%Y%m%d%H%M%S",
347 pubDate)))
348 data += (' <id>%s</id>\n' % guid)
349 data += (' <author>\n')
350 data += (' <name>Build Bot</name>\n')
351 data += (' </author>\n')
352 data += (' </entry>\n')
353 return data
354
358