[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