[cig-commits] r12636 - cs/buildbot/trunk/buildbot/status
leif at geodynamics.org
leif at geodynamics.org
Thu Aug 14 14:52:31 PDT 2008
Author: leif
Date: 2008-08-14 14:52:31 -0700 (Thu, 14 Aug 2008)
New Revision: 12636
Modified:
cs/buildbot/trunk/buildbot/status/html.py
Log:
Invented *.bbl files. A BBL (BuildBot Log) file is simply a .tar.gz
file that has special meaning to BuildBot. A BBL file may be listed
in the 'logfiles' for a build step. When its link is clicked in the
waterfall display, BuildBot expands the BBL file on the fly, allowing
the user to browse its contents. (A BBL directory index may be
customized by adding an "index.html" file within the BBL itself.)
The advantage of this is that it allows a build step to upload an
arbitrary number of log files, with arbitrary names. Logs can be
added/removed/renamed within the BBL file without reconfiguring the
BuildBot server.
Modified: cs/buildbot/trunk/buildbot/status/html.py
===================================================================
--- cs/buildbot/trunk/buildbot/status/html.py 2008-08-14 20:39:53 UTC (rev 12635)
+++ cs/buildbot/trunk/buildbot/status/html.py 2008-08-14 21:52:31 UTC (rev 12636)
@@ -760,16 +760,26 @@
else:
__implements__ = IHTMLLog,
- asText = False
+ raw = False
subscribed = False
+ membername = None
+ data = ""
def __init__(self, original):
Resource.__init__(self)
self.original = original
+ name = original.getName()
+ if name.endswith(".bbl"):
+ self.membername = name[0:-4]
+ self.raw = True
+ return
def getChild(self, path, request):
+ if self.membername:
+ self.membername += "/" + path
+ return self
if path == "text":
- self.asText = True
+ self.raw = True
return self
return NoResource("bad pathname")
@@ -788,12 +798,15 @@
spanfmt = '<span class="%s">%s</span>'
data = ""
for type, entry in entries:
- if self.asText:
+ if self.raw:
if type != builder.HEADER:
data += entry
else:
data += spanfmt % (builder.ChunkTypes[type],
html.escape(entry))
+ if self.membername:
+ self.data += data
+ return ""
return data
def htmlFooter(self):
@@ -813,7 +826,7 @@
self.setContentType(req)
- if not self.asText:
+ if not self.raw:
req.write(self.htmlHeader(req))
self.original.subscribeConsumer(ChunkConsumer(req, self))
@@ -821,11 +834,18 @@
def setContentType(self, request):
import mimetypes
+ if self.membername:
+ ctype, encoding = mimetypes.guess_type(self.membername)
+ if ctype is not None and encoding is None:
+ request.setHeader("content-type", ctype)
+ else:
+ request.setHeader("content-type", "text/html")
+ return
ctype, encoding = mimetypes.guess_type(self.original.getName())
if ctype is not None and encoding is None:
request.setHeader("content-type", ctype)
- self.asText = True
- elif self.asText:
+ self.raw = True
+ elif self.raw:
request.setHeader("content-type", "text/plain")
else:
request.setHeader("content-type", "text/html")
@@ -835,7 +855,9 @@
if not self.req:
return
try:
- if not self.asText:
+ if self.membername:
+ self.extractMember()
+ elif not self.raw:
self.req.write(self.htmlFooter())
self.req.finish()
except pb.DeadReferenceError:
@@ -844,6 +866,55 @@
# Deferred (from req.notifyFinish) that's pointing at us.
self.req = None
+ def extractMember(self):
+ import tarfile
+ from StringIO import StringIO
+ from shutil import copyfileobj
+
+ stream = StringIO(self.data)
+ bbl = tarfile.open("log.bbl", 'r:gz', stream)
+ try:
+ tarinfo = bbl.getmember(self.membername)
+ except KeyError, e:
+ if self.membername.endswith('/'):
+ self.req.write(self.htmlErrorMessage(str(e)))
+ return
+ else:
+ try:
+ self.membername += '/'
+ tarinfo = bbl.getmember(self.membername)
+ except KeyError, e:
+ self.req.write(self.htmlErrorMessage(str(e)))
+ return
+
+ if tarinfo.isdir():
+ try:
+ index = bbl.getmember(self.membername + "index.html")
+ if index.isreg():
+ tarinfo = index
+ except KeyError:
+ pass
+
+ if tarinfo.isreg():
+ dataStream = bbl.extractfile(tarinfo)
+ copyfileobj(dataStream, self.req)
+ elif tarinfo.isdir():
+ data = '<html><head><title>%s</title></head><body><ul>' % self.membername
+ for name in bbl.getnames():
+ if name.startswith(self.membername):
+ name = name[len(self.membername):]
+ if name and name[0:-1].find('/') == -1:
+ data += '<li><a href="%s">%s</a></li>' % (name, name)
+ data += '</ul></body></html>'
+ self.req.write(data)
+ else:
+ self.req.write(self.htmlErrorMessage("'%s' is neither a directory nor a regular file" % tarinfo.name))
+ return
+
+ def htmlErrorMessage(self, text):
+ return '<html><head><title>Error</title></head><body><p>%s</p></body></html>' % text
+
+
components.registerAdapter(TextLog, interfaces.IStatusLog, IHTMLLog)
@@ -1002,7 +1073,8 @@
for num in range(len(logs)):
name = logs[num].getName()
if logs[num].hasContents():
- url = "%s/%d" % (urlbase, num)
+ urlFormat = name.endswith(".bbl") and "%s/%d/" or "%s/%d"
+ url = urlFormat % (urlbase, num)
text.append("<a href=\"%s\">%s</a>" % (url, html.escape(name)))
else:
text.append(html.escape(name))
More information about the cig-commits
mailing list