Index: third_party/buildbot_7_12/buildbot/status/web/feeds.py |
diff --git a/third_party/buildbot_7_12/buildbot/status/web/feeds.py b/third_party/buildbot_7_12/buildbot/status/web/feeds.py |
deleted file mode 100644 |
index 60cb1a72277888f6aeab7d6e5e5f867d523863e7..0000000000000000000000000000000000000000 |
--- a/third_party/buildbot_7_12/buildbot/status/web/feeds.py |
+++ /dev/null |
@@ -1,357 +0,0 @@ |
-# This module enables ATOM and RSS feeds from webstatus. |
-# |
-# It is based on "feeder.py" which was part of the Buildbot |
-# configuration for the Subversion project. The original file was |
-# created by Lieven Gobaerts and later adjusted by API |
-# (apinheiro@igalia.coma) and also here |
-# http://code.google.com/p/pybots/source/browse/trunk/master/Feeder.py |
-# |
-# All subsequent changes to feeder.py where made by Chandan-Dutta |
-# Chowdhury <chandan-dutta.chowdhury @ hp.com> and Gareth Armstrong |
-# <gareth.armstrong @ hp.com>. |
-# |
-# Those modifications are as follows: |
-# 1) the feeds are usable from baseweb.WebStatus |
-# 2) feeds are fully validated ATOM 1.0 and RSS 2.0 feeds, verified |
-# with code from http://feedvalidator.org |
-# 3) nicer xml output |
-# 4) feeds can be filtered as per the /waterfall display with the |
-# builder and category filters |
-# 5) cleaned up white space and imports |
-# |
-# Finally, the code was directly integrated into these two files, |
-# buildbot/status/web/feeds.py (you're reading it, ;-)) and |
-# buildbot/status/web/baseweb.py. |
- |
-import os |
-import re |
-import sys |
-import time |
-from twisted.web import resource, html |
-from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION |
- |
-class XmlResource(resource.Resource): |
- contentType = "text/xml; charset=UTF-8" |
- def render(self, request): |
- data = self.content(request) |
- request.setHeader("content-type", self.contentType) |
- if request.method == "HEAD": |
- request.setHeader("content-length", len(data)) |
- return '' |
- return data |
- docType = '' |
- def header (self, request): |
- data = ('<?xml version="1.0"?>\n') |
- return data |
- def footer(self, request): |
- data = '' |
- return data |
- def content(self, request): |
- data = self.docType |
- data += self.header(request) |
- data += self.body(request) |
- data += self.footer(request) |
- return data |
- def body(self, request): |
- return '' |
- |
-class FeedResource(XmlResource): |
- title = None |
- link = 'http://dummylink' |
- language = 'en-us' |
- description = 'Dummy rss' |
- status = None |
- |
- def __init__(self, status, categories=None, title=None): |
- self.status = status |
- self.categories = categories |
- self.title = title |
- self.projectName = self.status.getProjectName() |
- self.link = self.status.getBuildbotURL() |
- self.description = 'List of FAILED builds' |
- self.pubdate = time.gmtime(int(time.time())) |
- self.user = self.getEnv(['USER', 'USERNAME'], 'buildmaster') |
- self.hostname = self.getEnv(['HOSTNAME', 'COMPUTERNAME'], |
- 'buildmaster') |
- |
- def getEnv(self, keys, fallback): |
- for key in keys: |
- if key in os.environ: |
- return os.environ[key] |
- return fallback |
- |
- def getBuilds(self, request): |
- builds = [] |
- # THIS is lifted straight from the WaterfallStatusResource Class in |
- # status/web/waterfall.py |
- # |
- # we start with all Builders available to this Waterfall: this is |
- # limited by the config-file -time categories= argument, and defaults |
- # to all defined Builders. |
- allBuilderNames = self.status.getBuilderNames(categories=self.categories) |
- builders = [self.status.getBuilder(name) for name in allBuilderNames] |
- |
- # but if the URL has one or more builder= arguments (or the old show= |
- # argument, which is still accepted for backwards compatibility), we |
- # use that set of builders instead. We still don't show anything |
- # outside the config-file time set limited by categories=. |
- showBuilders = request.args.get("show", []) |
- showBuilders.extend(request.args.get("builder", [])) |
- if showBuilders: |
- builders = [b for b in builders if b.name in showBuilders] |
- |
- # now, if the URL has one or category= arguments, use them as a |
- # filter: only show those builders which belong to one of the given |
- # categories. |
- showCategories = request.args.get("category", []) |
- if showCategories: |
- builders = [b for b in builders if b.category in showCategories] |
- |
- maxFeeds = 25 |
- |
- # Copy all failed builds in a new list. |
- # This could clearly be implemented much better if we had |
- # access to a global list of builds. |
- for b in builders: |
- lastbuild = b.getLastFinishedBuild() |
- if lastbuild is None: |
- continue |
- |
- lastnr = lastbuild.getNumber() |
- |
- totalbuilds = 0 |
- i = lastnr |
- while i >= 0: |
- build = b.getBuild(i) |
- i -= 1 |
- if not build: |
- continue |
- |
- results = build.getResults() |
- |
- # only add entries for failed builds! |
- if results == FAILURE: |
- totalbuilds += 1 |
- builds.append(build) |
- |
- # stop for this builder when our total nr. of feeds is reached |
- if totalbuilds >= maxFeeds: |
- break |
- |
- # Sort build list by date, youngest first. |
- # To keep compatibility with python < 2.4, use this for sorting instead: |
- # We apply Decorate-Sort-Undecorate |
- deco = [(build.getTimes(), build) for build in builds] |
- deco.sort() |
- deco.reverse() |
- builds = [build for (b1, build) in deco] |
- |
- if builds: |
- builds = builds[:min(len(builds), maxFeeds)] |
- return builds |
- |
- def body (self, request): |
- data = '' |
- builds = self.getBuilds(request) |
- |
- for build in builds: |
- start, finished = build.getTimes() |
- finishedTime = time.gmtime(int(finished)) |
- link = re.sub(r'index.html', "", self.status.getURLForThing(build)) |
- |
- # title: trunk r22191 (plus patch) failed on 'i686-debian-sarge1 shared gcc-3.3.5' |
- ss = build.getSourceStamp() |
- source = "" |
- if ss.branch: |
- source += "Branch %s " % ss.branch |
- if ss.revision: |
- source += "Revision %s " % str(ss.revision) |
- if ss.patch: |
- source += " (plus patch)" |
- if ss.changes: |
- pass |
- if (ss.branch is None and ss.revision is None and ss.patch is None |
- and not ss.changes): |
- source += "Latest revision " |
- got_revision = None |
- try: |
- got_revision = build.getProperty("got_revision") |
- except KeyError: |
- pass |
- if got_revision: |
- got_revision = str(got_revision) |
- if len(got_revision) > 40: |
- got_revision = "[revision string too long]" |
- source += "(Got Revision: %s)" % got_revision |
- title = ('%s failed on "%s"' % |
- (source, build.getBuilder().getName())) |
- |
- description = '' |
- description += ('Date: %s<br/><br/>' % |
- time.strftime("%a, %d %b %Y %H:%M:%S GMT", |
- finishedTime)) |
- description += ('Full details available here: <a href="%s">%s</a><br/>' % |
- (self.link, self.projectName)) |
- builder_summary_link = ('%s/builders/%s' % |
- (re.sub(r'/index.html', '', self.link), |
- build.getBuilder().getName())) |
- description += ('Build summary: <a href="%s">%s</a><br/><br/>' % |
- (builder_summary_link, |
- build.getBuilder().getName())) |
- description += ('Build details: <a href="%s">%s</a><br/><br/>' % |
- (link, link)) |
- description += ('Author list: <b>%s</b><br/><br/>' % |
- ",".join(build.getResponsibleUsers())) |
- |
- # Add information about the failing steps. |
- lastlog = '' |
- for s in build.getSteps(): |
- if s.getResults()[0] == FAILURE: |
- description += ('Failed step: <b>%s</b><br/>' % s.getName()) |
- |
- # Add the last 30 lines of each log. |
- for log in s.getLogs(): |
- lastlog += ('Last lines of build log "%s":<br/>' % log.getName()) |
- try: |
- logdata = log.getText() |
- except IOError: |
- # Probably the log file has been removed |
- logdata ='<b>log file not available</b>' |
- |
- lastlines = logdata.split('\n')[-30:] |
- lastlog += '<br/>'.join(lastlines) |
- lastlog += '<br/>' |
- description += '<br/>' |
- |
- data += self.item(title, description=description, lastlog=lastlog, |
- link=link, pubDate=finishedTime) |
- |
- return data |
- |
- def item(self, title='', link='', description='', pubDate=''): |
- """Generates xml for one item in the feed.""" |
- |
-class Rss20StatusResource(FeedResource): |
- def __init__(self, status, categories=None, title=None): |
- FeedResource.__init__(self, status, categories, title) |
- contentType = 'application/rss+xml' |
- |
- def header(self, request): |
- data = FeedResource.header(self, request) |
- data += ('<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">\n') |
- data += (' <channel>\n') |
- if self.title is None: |
- title = 'Build status of ' + self.projectName |
- else: |
- title = self.title |
- data += (' <title>%s</title>\n' % title) |
- if self.link is not None: |
- data += (' <link>%s</link>\n' % self.link) |
- link = re.sub(r'/index.html', '', self.link) |
- data += (' <atom:link href="%s/rss" rel="self" type="application/rss+xml"/>\n' % link) |
- if self.language is not None: |
- data += (' <language>%s</language>\n' % self.language) |
- if self.description is not None: |
- data += (' <description>%s</description>\n' % self.description) |
- if self.pubdate is not None: |
- rfc822_pubdate = time.strftime("%a, %d %b %Y %H:%M:%S GMT", |
- self.pubdate) |
- data += (' <pubDate>%s</pubDate>\n' % rfc822_pubdate) |
- return data |
- |
- def item(self, title='', link='', description='', lastlog='', pubDate=''): |
- data = (' <item>\n') |
- data += (' <title>%s</title>\n' % title) |
- if link is not None: |
- data += (' <link>%s</link>\n' % link) |
- if (description is not None and lastlog is not None): |
- lastlog = lastlog.replace('<br/>', '\n') |
- lastlog = html.escape(lastlog) |
- lastlog = lastlog.replace('\n', '<br/>') |
- content = '<![CDATA[' |
- content += description |
- content += lastlog |
- content += ']]>' |
- data += (' <description>%s</description>\n' % content) |
- if pubDate is not None: |
- rfc822pubDate = time.strftime("%a, %d %b %Y %H:%M:%S GMT", |
- pubDate) |
- data += (' <pubDate>%s</pubDate>\n' % rfc822pubDate) |
- # Every RSS item must have a globally unique ID |
- guid = ('tag:%s@%s,%s:%s' % (self.user, self.hostname, |
- time.strftime("%Y-%m-%d", pubDate), |
- time.strftime("%Y%m%d%H%M%S", |
- pubDate))) |
- data += (' <guid isPermaLink="false">%s</guid>\n' % guid) |
- data += (' </item>\n') |
- return data |
- |
- def footer(self, request): |
- data = (' </channel>\n' |
- '</rss>') |
- return data |
- |
-class Atom10StatusResource(FeedResource): |
- def __init__(self, status, categories=None, title=None): |
- FeedResource.__init__(self, status, categories, title) |
- contentType = 'application/atom+xml' |
- |
- def header(self, request): |
- data = FeedResource.header(self, request) |
- data += '<feed xmlns="http://www.w3.org/2005/Atom">\n' |
- data += (' <id>%s</id>\n' % self.link) |
- if self.title is None: |
- title = 'Build status of ' + self.projectName |
- else: |
- title = self.title |
- data += (' <title>%s</title>\n' % title) |
- if self.link is not None: |
- link = re.sub(r'/index.html', '', self.link) |
- data += (' <link rel="self" href="%s/atom"/>\n' % link) |
- data += (' <link rel="alternate" href="%s/"/>\n' % link) |
- if self.description is not None: |
- data += (' <subtitle>%s</subtitle>\n' % self.description) |
- if self.pubdate is not None: |
- rfc3339_pubdate = time.strftime("%Y-%m-%dT%H:%M:%SZ", |
- self.pubdate) |
- data += (' <updated>%s</updated>\n' % rfc3339_pubdate) |
- data += (' <author>\n') |
- data += (' <name>Build Bot</name>\n') |
- data += (' </author>\n') |
- return data |
- |
- def item(self, title='', link='', description='', lastlog='', pubDate=''): |
- data = (' <entry>\n') |
- data += (' <title>%s</title>\n' % title) |
- if link is not None: |
- data += (' <link href="%s"/>\n' % link) |
- if (description is not None and lastlog is not None): |
- lastlog = lastlog.replace('<br/>', '\n') |
- lastlog = html.escape(lastlog) |
- lastlog = lastlog.replace('\n', '<br/>') |
- data += (' <content type="xhtml">\n') |
- data += (' <div xmlns="http://www.w3.org/1999/xhtml">\n') |
- data += (' %s\n' % description) |
- data += (' <pre xml:space="preserve">%s</pre>\n' % lastlog) |
- data += (' </div>\n') |
- data += (' </content>\n') |
- if pubDate is not None: |
- rfc3339pubDate = time.strftime("%Y-%m-%dT%H:%M:%SZ", |
- pubDate) |
- data += (' <updated>%s</updated>\n' % rfc3339pubDate) |
- # Every Atom entry must have a globally unique ID |
- # http://diveintomark.org/archives/2004/05/28/howto-atom-id |
- guid = ('tag:%s@%s,%s:%s' % (self.user, self.hostname, |
- time.strftime("%Y-%m-%d", pubDate), |
- time.strftime("%Y%m%d%H%M%S", |
- pubDate))) |
- data += (' <id>%s</id>\n' % guid) |
- data += (' <author>\n') |
- data += (' <name>Build Bot</name>\n') |
- data += (' </author>\n') |
- data += (' </entry>\n') |
- return data |
- |
- def footer(self, request): |
- data = ('</feed>') |
- return data |