[Buildbot-commits] buildbot/buildbot/status/web base.py, 1.12, 1.13 baseweb.py, 1.21, 1.22 build.py, 1.11, 1.12 builder.py, 1.9, 1.10 changes.py, 1.3, 1.4 logs.py, 1.5, 1.6 slaves.py, 1.2, 1.3 step.py, 1.5, 1.6 tests.py, 1.3, 1.4 waterfall.py, 1.20, 1.21

Brian Warner warner at users.sourceforge.net
Fri Sep 28 09:33:33 UTC 2007


Update of /cvsroot/buildbot/buildbot/buildbot/status/web
In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv11817/buildbot/status/web

Modified Files:
	base.py baseweb.py build.py builder.py changes.py logs.py 
	slaves.py step.py tests.py waterfall.py 
Log Message:
[project @ web: big cleanup of URL generation, to use relative links everywhere]

Original author: warner at lothar.com
Date: 2007-09-28 09:08:11+00:00

Index: base.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/base.py,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -d -r1.12 -r1.13
--- base.py	27 Sep 2007 22:51:10 -0000	1.12
+++ base.py	28 Sep 2007 09:33:31 -0000	1.13
@@ -1,5 +1,5 @@
 
-import urlparse
+import urlparse, urllib
 from zope.interface import Interface
 from twisted.web import html, resource
 from buildbot.status import builder
@@ -9,15 +9,22 @@
 class ITopBox(Interface):
     """I represent a box in the top row of the waterfall display: the one
     which shows the status of the last build for each builder."""
-    pass
+    def getBox(self, request):
+        """Return a Box instance, which can produce a <td> cell.
+        """
 
 class ICurrentBox(Interface):
     """I represent the 'current activity' box, just above the builder name."""
-    pass
+    def getBox(self, status):
+        """Return a Box instance, which can produce a <td> cell.
+        """
 
 class IBox(Interface):
     """I represent a box in the waterfall display."""
-    pass
+    def getBox(self, request):
+        """Return a Box instance, which wraps an Event and can produce a <td>
+        cell.
+        """
 
 class IHTMLLog(Interface):
     pass
@@ -102,6 +109,31 @@
         return "running"
     return builder.Results[result]
 
+def path_to_root(request):
+    # /waterfall : ['waterfall'] -> ''
+    # /somewhere/lower : ['somewhere', 'lower'] -> '../'
+    # /somewhere/indexy/ : ['somewhere', 'indexy', ''] -> '../../'
+    # / : [] -> ''
+    if request.prepath:
+        segs = len(request.prepath) - 1
+    else:
+        segs = 0
+    root = "../" * segs
+    return root
+
+def path_to_builder(request, builderstatus):
+    return (path_to_root(request) +
+            "builders/" +
+            urllib.quote(builderstatus.getName(), safe=''))
+
+def path_to_build(request, buildstatus):
+    return (path_to_builder(request, buildstatus.getBuilder()) +
+            "/builds/%d" % buildstatus.getNumber())
+
+def path_to_step(request, stepstatus):
+    return (path_to_build(request, stepstatus.getBuild()) +
+            "/steps/%s" % urllib.quote(stepstatus.getName(), safe=''))
+
 class Box:
     # a Box wraps an Event. The Box has HTML <td> parameters that Events
     # lack, and it has a base URL to which each File's name is relative.
@@ -142,10 +174,23 @@
         return resource.Resource.getChild(self, path, request)
 
     def render(self, request):
-        if self.addSlash and request.prepath[-1] != '':
+
+        # Our pages no longer require that their URL end in a slash. Instead,
+        # they all use request.childLink() or some equivalent which takes the
+        # last path component into account. This clause is left here for
+        # historical and educational purposes.
+        if False and self.addSlash and request.prepath[-1] != '':
             # this is intended to behave like request.URLPath().child('')
             # but we need a relative URL, since we might be living behind a
             # reverse proxy
+            #
+            # note that the Location: header (as used in redirects) are
+            # required to have absolute URIs, and my attempt to handle
+            # reverse-proxies gracefully violates rfc2616. This frequently
+            # works, but single-component paths sometimes break. The best
+            # strategy is to avoid these redirects whenever possible by using
+            # HREFs with trailing slashes, and only use the redirects for
+            # manually entered URLs.
             url = request.prePathURL()
             scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
             new_url = request.prepath[-1] + "/"
@@ -172,16 +217,7 @@
         return request.site.buildbot_service.parent.change_svc
 
     def path_to_root(self, request):
-        # /waterfall : ['waterfall'] -> ''
-        # /somewhere/lower : ['somewhere', 'lower'] -> '../'
-        # /somewhere/indexy/ : ['somewhere', 'indexy', ''] -> '../../'
-        # / : [] -> ''
-        if request.prepath:
-            segs = len(request.prepath) - 1
-        else:
-            segs = 0
-        root = "../" * segs
-        return root
+        return path_to_root(request)
 
     def getTitle(self, request):
         return self.title

Index: baseweb.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/baseweb.py,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -d -r1.21 -r1.22
--- baseweb.py	26 Sep 2007 09:57:30 -0000	1.21
+++ baseweb.py	28 Sep 2007 09:33:31 -0000	1.22
@@ -292,7 +292,14 @@
                                 Builders it triggers, and list of the Changes
                                 that are queued awaiting the tree-stable
                                 timer, and controls to accelerate the timer.
-     /others...
+     /buildslaves : list all BuildSlaves
+     /buildslaves/SLAVENAME : describe a single BuildSlave
+     /one_line_per_build : summarize the last few builds, one line each
+     /one_line_per_build/BUILDERNAME : same, but only for a single builder
+     /one_box_per_builder : show the latest build and current activity
+     /about : describe this buildmaster (Buildbot and support library versions)
+     /xmlrpc : (not yet implemented) an XMLRPC server with build status
+
 
     All URLs for pages which are not defined here are used to look for files
     in BASEDIR/public_html/ , which means that /robots.txt or /buildbot.css
@@ -411,7 +418,7 @@
     def setupUsualPages(self):
         #self.putChild("", IndexOrWaterfallRedirection())
         self.putChild("waterfall", WaterfallStatusResource())
-        self.putChild("builders", BuildersResource())
+        self.putChild("builders", BuildersResource()) # has builds/steps/logs
         self.putChild("changes", ChangesResource())
         self.putChild("buildslaves", BuildSlavesResource())
         #self.putChild("schedulers", SchedulersResource())

Index: build.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/build.py,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -d -r1.11 -r1.12
--- build.py	27 Sep 2007 22:51:23 -0000	1.11
+++ build.py	28 Sep 2007 09:33:31 -0000	1.12
@@ -5,12 +5,13 @@
 
 import urllib, time
 from twisted.python import log
-from buildbot.status.web.base import HtmlResource, make_row, css_classes
+from buildbot.status.web.base import HtmlResource, make_row, css_classes, \
+     path_to_builder
 
 from buildbot.status.web.tests import TestsResource
 from buildbot.status.web.step import StepsResource
 
-# builders/$builder/builds/$buildnum
+# /builders/$builder/builds/$buildnum
 class StatusResourceBuild(HtmlResource):
     addSlash = True
 
@@ -29,12 +30,13 @@
         b = self.build_status
         status = self.getStatus(req)
         projectName = status.getProjectName()
-        data = ('<div class="title"><a href="../../../..">%s</a></div>\n'
-                % projectName)
+        data = ('<div class="title"><a href="%s">%s</a></div>\n'
+                % (self.path_to_root(req), projectName))
         # the color in the following line gives python-mode trouble
         builder_name = b.getBuilder().getName()
-        data += ("<h1><a href=\"../..\">Builder %s</a>: Build #%d</h1>\n"
-                 % (builder_name, b.getNumber()))
+        data += ("<h1><a href=\"%s\">Builder %s</a>: Build #%d</h1>\n"
+                 % (path_to_builder(req, b.getBuilder()),
+                    builder_name, b.getNumber()))
 
         if not b.isFinished():
             data += "<h2>Build In Progress</h2>"
@@ -99,6 +101,12 @@
         data += "<h2>Reason:</h2>\n%s\n" % html.escape(b.getReason())
 
         data += "<h2>Steps and Logfiles:</h2>\n"
+        # TODO:
+#        urls = self.original.getURLs()
+#        ex_url_class = "BuildStep external"
+#        for name, target in urls.items():
+#            text.append('[<a href="%s" class="%s">%s</a>]' %
+#                        (target, ex_url_class, html.escape(name)))
         if b.getLogs():
             data += "<ol>\n"
             for s in b.getSteps():
@@ -232,6 +240,7 @@
 
         return HtmlResource.getChild(self, path, req)
 
+# /builders/$builder/builds
 class BuildsResource(HtmlResource):
     addSlash = True
 

Index: builder.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/builder.py,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- builder.py	27 Sep 2007 22:51:23 -0000	1.9
+++ builder.py	28 Sep 2007 09:33:31 -0000	1.10
@@ -12,7 +12,7 @@
 
 from buildbot.status.web.build import BuildsResource
 
-# builders/$builder
+# /builders/$builder
 class StatusResourceBuilder(HtmlResource):
     addSlash = True
 
@@ -26,7 +26,7 @@
 
     def build_line(self, build, req):
         buildnum = build.getNumber()
-        buildurl = "builds/%d" % buildnum
+        buildurl = req.childLink("builds/%d" % buildnum)
         data = '<a href="%s">#%d</a> ' % (buildurl, buildnum)
         when = build.getETA()
         if when is not None:
@@ -38,7 +38,7 @@
 
     def build_finished_line(self, build, req):
         buildnum = build.getNumber()
-        buildurl = "builds/%d" % buildnum
+        buildurl = req.childLink("builds/%d" % buildnum)
         results = build.getResults()
         text = " ".join(build.getText())
         data = '<a href="%s">#%d</a> ' % (buildurl, buildnum)
@@ -55,7 +55,7 @@
 
         projectName = status.getProjectName()
 
-        data = '<a href="../..">%s</a>\n' % projectName
+        data = '<a href="%s">%s</a>\n' % (self.path_to_root(req), projectName)
 
         data += "<h1>Builder: %s</h1>\n" % html.escape(b.getName())
 
@@ -215,9 +215,28 @@
         return HtmlResource.getChild(self, path, req)
 
 
+# /builders
 class BuildersResource(HtmlResource):
+    title = "Builders"
     addSlash = True
 
+    def body(self, req):
+        s = self.getStatus(req)
+        data = ""
+        data += "<h1>Builders</h1>\n"
+
+        # TODO: this is really basic. It should be expanded to include a
+        # brief one-line summary of the builder (perhaps with whatever the
+        # builder is currently doing)
+        data += "<ol>\n"
+        for bname in s.getBuilderNames():
+            data += (' <li><a href="%s">%s</a></li>\n' %
+                     (req.childLink(urllib.quote(bname, safe='')),
+                      urllib.quote(bname, safe='')))
+        data += "</ol>\n"
+
+        return data
+
     def getChild(self, path, req):
         s = self.getStatus(req)
         if path in s.getBuilderNames():

Index: changes.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/changes.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- changes.py	1 Aug 2007 22:08:32 -0000	1.3
+++ changes.py	28 Sep 2007 09:33:31 -0000	1.4
@@ -6,7 +6,7 @@
 from buildbot.changes.changes import Change
 from buildbot.status.web.base import HtmlResource, StaticHTML, IBox, Box
 
-# $changes/NN
+# /changes/NN
 class ChangesResource(HtmlResource):
 
     def body(self, req):
@@ -33,8 +33,8 @@
 class ChangeBox(components.Adapter):
     implements(IBox)
 
-    def getBox(self):
-        url = "changes/%d" % self.original.number
+    def getBox(self, req):
+        url = req.childLink("../changes/%d" % self.original.number)
         text = self.original.get_HTML_box(url)
         return Box([text], color="white", class_="Change")
 components.registerAdapter(ChangeBox, Change, IBox)

Index: logs.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/logs.py,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- logs.py	1 Aug 2007 23:30:14 -0000	1.5
+++ logs.py	28 Sep 2007 09:33:31 -0000	1.6
@@ -50,6 +50,8 @@
     def finish(self):
         self.textlog.finished()
 
+
+# /builders/$builder/builds/$buildnum/steps/$stepname/logs/$logname
 class TextLog(Resource):
     # a new instance of this Resource is created for each client who views
     # it, so we can afford to track the request in the Resource.

Index: slaves.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/slaves.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- slaves.py	25 Sep 2007 22:50:36 -0000	1.2
+++ slaves.py	28 Sep 2007 09:33:31 -0000	1.3
@@ -2,11 +2,11 @@
 import time
 from buildbot.status.web.base import HtmlResource, abbreviate_age
 
-# buildslaves/$slavename
+# /buildslaves/$slavename
 class OneBuildSlaveResource(HtmlResource):
-    pass
+    pass  # TODO
 
-# buildslaves/
+# /buildslaves/
 class BuildSlavesResource(HtmlResource):
     title = "BuildSlaves"
     addSlash = True
@@ -30,10 +30,12 @@
             slave = s.getSlave(name)
             data += " <li>%s:\n" % name
             data += " <ul>\n"
-            builder_links = ['<a href="../builders/%s">%s</a>' % (bname, bname)
+            builder_links = ['<a href="%s">%s</a>'
+                             % (req.childLink("../builders/%s" % bname),bname)
                              for bname in used_by_builder.get(name, [])]
             if builder_links:
-                data += "  <li>Used by: %s</li>\n" % ", ".join(builder_links)
+                data += ("  <li>Used by Builders: %s</li>\n" %
+                         ", ".join(builder_links))
             else:
                 data += "  <li>Not used by any Builders</li>\n"
             if slave.isConnected():

Index: step.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/step.py,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- step.py	1 Aug 2007 23:30:14 -0000	1.5
+++ step.py	28 Sep 2007 09:33:31 -0000	1.6
@@ -2,10 +2,11 @@
 from twisted.web import html
 
 import urllib
-from buildbot.status.web.base import HtmlResource
+from buildbot.status.web.base import HtmlResource, path_to_builder, \
+     path_to_build
 from buildbot.status.web.logs import LogsResource
 
-# builders/$builder/builds/$buildnum/steps/$stepname
+# /builders/$builder/builds/$buildnum/steps/$stepname
 class StatusResourceBuildStep(HtmlResource):
     title = "Build Step"
     addSlash = True
@@ -21,9 +22,9 @@
         builder_name = b.getBuilder().getName()
         build_num = b.getNumber()
         data = ""
-        data += ("<h1>BuildStep <a href=\"../../../../%s\">%s</a>:" %
-                 (urllib.quote(builder_name), builder_name))
-        data += "<a href=\"../../%d\">#%d</a>" % (build_num, build_num)
+        data += ('<h1>BuildStep <a href="%s">%s</a>:' %
+                 (path_to_builder(req, b.getBuilder()), builder_name))
+        data += '<a href="%s">#%d</a>' % (path_to_build(req, b), build_num)
         data += ":%s</h1>\n" % s.getName()
 
         if s.isFinished():
@@ -69,6 +70,7 @@
 
 
 
+# /builders/$builder/builds/$buildnum/steps
 class StepsResource(HtmlResource):
     addSlash = True
 

Index: tests.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/tests.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- tests.py	1 Aug 2007 22:08:32 -0000	1.3
+++ tests.py	28 Sep 2007 09:33:31 -0000	1.4
@@ -4,7 +4,7 @@
 
 from buildbot.status.web.base import HtmlResource
 
-# $builder/builds/NN/tests/TESTNAME
+# /builders/$builder/builds/$buildnum/tests/$testname
 class TestResult(HtmlResource):
     title = "Test Logs"
 
@@ -26,7 +26,7 @@
         return data
 
 
-# $builder/builds/NN/tests
+# /builders/$builder/builds/$buildnum/tests
 class TestsResource(HtmlResource):
     title = "Test Results"
 

Index: waterfall.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/waterfall.py,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -d -r1.20 -r1.21
--- waterfall.py	25 Sep 2007 23:04:29 -0000	1.20
+++ waterfall.py	28 Sep 2007 09:33:31 -0000	1.21
@@ -12,7 +12,7 @@
 from buildbot.status import builder
 
 from buildbot.status.web.base import Box, HtmlResource, IBox, ICurrentBox, \
-     ITopBox, td, build_get_class
+     ITopBox, td, build_get_class, path_to_build, path_to_step
 
 
 
@@ -97,14 +97,14 @@
     # showing the results of the most recent build
     implements(IBox)
 
-    def getBox(self):
+    def getBox(self, req):
         assert interfaces.IBuilderStatus(self.original)
         b = self.original.getLastFinishedBuild()
         if not b:
             return Box(["none"], "white", class_="LastBuild")
         name = b.getBuilder().getName()
         number = b.getNumber()
-        url = "%s/builds/%d" % (name, number)
+        url = path_to_build(req, b)
         text = b.getText()
         # TODO: add logs?
         # TODO: add link to the per-build page at 'url'
@@ -117,11 +117,10 @@
     # this provides the yellow "starting line" box for each build
     implements(IBox)
 
-    def getBox(self):
+    def getBox(self, req):
         b = self.original
-        name = b.getBuilder().getName()
         number = b.getNumber()
-        url = "builders/%s/builds/%d" % (urllib.quote(name, safe=''), number)
+        url = path_to_build(req, b)
         reason = b.getReason()
         text = ('<a title="Reason: %s" href="%s">Build %d</a>'
                 % (html.escape(reason), url, number))
@@ -139,12 +138,8 @@
 class StepBox(components.Adapter):
     implements(IBox)
 
-    def getBox(self):
-        b = self.original.getBuild()
-        urlbase = "builders/%s/builds/%d/steps/%s" % (
-            urllib.quote(b.getBuilder().getName(), safe=''),
-            b.getNumber(),
-            urllib.quote(self.original.getName(), safe=''))
+    def getBox(self, req):
+        urlbase = path_to_step(req, self.original)
         text = self.original.getText()
         if text is None:
             log.msg("getText() gave None", urlbase)
@@ -172,7 +167,7 @@
 class EventBox(components.Adapter):
     implements(IBox)
 
-    def getBox(self):
+    def getBox(self, req):
         text = self.original.getText()
         color = self.original.getColor()
         class_ = "Event"
@@ -199,7 +194,7 @@
 class SpacerBox(components.Adapter):
     implements(IBox)
 
-    def getBox(self):
+    def getBox(self, req):
         #b = Box(["spacer"], "white")
         b = Box([])
         b.spacer = True
@@ -483,14 +478,14 @@
 
         if projectName and projectURL:
             # TODO: this is going to look really ugly
-            topleft = "<a href=\"%s\">%s</a><br />last build" % \
+            topleft = '<a href="%s">%s</a><br />last build' % \
                       (projectURL, projectName)
         else:
             topleft = "last build"
         data += ' <tr class="LastBuild">\n'
         data += td(topleft, align="right", colspan=2, class_="Project")
         for b in builders:
-            box = ITopBox(b).getBox()
+            box = ITopBox(b).getBox(request)
             data += box.td(align="center")
         data += " </tr>\n"
 
@@ -504,14 +499,13 @@
         data += " <tr>\n"
         TZ = time.tzname[time.daylight]
         data += td("time (%s)" % TZ, align="center", class_="Time")
-        name = changeNames[0]
-        data += td(
-                "<a href=\"%s\">%s</a>" % (urllib.quote(name, safe=''), name),
-                align="center", class_="Change")
+        data += td('<a href="%s">changes</a>' % request.childLink("../changes"),
+                   align="center", class_="Change")
         for name in builderNames:
             safename = urllib.quote(name, safe='')
-            data += td( "<a href=\"builders/%s\">%s</a>" % (safename, name),
-                        align="center", class_="Builder")
+            data += td('<a href="%s">%s</a>' %
+                       (request.childLink("../builders/%s" % safename), name),
+                       align="center", class_="Builder")
         data += " </tr>\n"
 
         if phase == 1:
@@ -572,11 +566,11 @@
 
 
         bburl = "http://buildbot.net/?bb-ver=%s" % urllib.quote(version)
-        data += "<a href=\"%s\">Buildbot-%s</a> " % (bburl, version)
+        data += '<a href="%s">Buildbot-%s</a> ' % (bburl, version)
         if projectName:
             data += "working for the "
             if projectURL:
-                data += "<a href=\"%s\">%s</a> project." % (projectURL,
+                data += '<a href="%s">%s</a> project.' % (projectURL,
                                                             projectName)
             else:
                 data += "%s project." % projectName
@@ -592,8 +586,7 @@
         # build the waterfall display
         data = ""
         data += "<h2>Basic display</h2>\n"
-        data += "<p>See <a href=\"%s\">here</a>" % \
-                urllib.quote(request.childLink("waterfall"))
+        data += '<p>See <a href="%s">here</a>' % request.childLink("../waterfall")
         data += " for the waterfall display</p>\n"
                 
         data += '<table border="0" cellspacing="0">\n'
@@ -618,9 +611,9 @@
         data += td("Time", align="center")
         data += td("Changes", align="center")
         for name in names:
-            data += td(
-                "<a href=\"%s\">%s</a>" % (urllib.quote(request.childLink(name)), name),
-                align="center")
+            data += td('<a href="%s">%s</a>' %
+                       (request.childLink("../" + urllib.quote(name)), name),
+                       align="center")
         data += " </tr>\n"
 
         # all further rows involve timestamps, commit events, and build events
@@ -834,7 +827,7 @@
                         data += td("")
                     else:
                         e = block[i-offset]
-                        box = IBox(e).getBox()
+                        box = IBox(e).getBox(request)
                         box.parms["show_idle"] = 1
                         data += box.td(valign="top", align="center")
                 data += " </tr>\n"
@@ -894,7 +887,7 @@
                     grid[c+1].append(None)
                 for i in range(len(block)):
                     # so the events are bottom-justified
-                    b = IBox(block[i]).getBox()
+                    b = IBox(block[i]).getBox(request)
                     b.parms['valign'] = "top"
                     b.parms['align'] = "center"
                     grid[c+1].append(b)
@@ -906,7 +899,7 @@
             assert(len(strip) == gridlen)
             if strip[-1] == None:
                 if sourceEvents[i-1]:
-                    filler = IBox(sourceEvents[i-1]).getBox()
+                    filler = IBox(sourceEvents[i-1]).getBox(request)
                 else:
                     # this can happen if you delete part of the build history
                     filler = Box(text=["?"], align="center")





More information about the Commits mailing list