[Buildbot-commits] buildbot/buildbot/status/web grid.py, NONE, 1.1 baseweb.py, 1.34, 1.35 build.py, 1.16, 1.17 builder.py, 1.17, 1.18 classic.css, 1.2, 1.3 index.html, 1.4, 1.5 waterfall.py, 1.28, 1.29
Brian Warner
warner at users.sourceforge.net
Thu May 22 22:11:27 UTC 2008
Update of /cvsroot/buildbot/buildbot/buildbot/status/web
In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv16506/buildbot/status/web
Modified Files:
baseweb.py build.py builder.py classic.css index.html
waterfall.py
Added Files:
grid.py
Log Message:
[project @ #72:statusgrid.patch]
Add a "status grid" web display which has a row for each builder and a column
for each distinct sourcestamp.
Original author: dustin at v.igoro.us
Date: 2008-02-17 18:16:14+00:00
Index: baseweb.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/baseweb.py,v
retrieving revision 1.34
retrieving revision 1.35
diff -u -d -r1.34 -r1.35
--- baseweb.py 23 Mar 2008 03:14:52 -0000 1.34
+++ baseweb.py 22 May 2008 22:11:25 -0000 1.35
@@ -14,6 +14,7 @@
build_get_class, ICurrentBox, OneLineMixin, map_branches, \
make_stop_form, make_force_build_form
from buildbot.status.web.waterfall import WaterfallStatusResource
+from buildbot.status.web.grid import GridStatusResource
from buildbot.status.web.changes import ChangesResource
from buildbot.status.web.builder import BuildersResource
from buildbot.status.web.slaves import BuildSlavesResource
@@ -280,6 +281,9 @@
to individual changes, builders, builds, steps, and logs.
A number of query-arguments can be added to influence
the display.
+ /grid : another summary display that shows a grid of builds, with
+ sourcestamps on the x axis, and builders on the y. Query
+ arguments similar to those for the waterfall can be added.
/builders/BUILDERNAME: a page summarizing the builder. This includes
references to the Schedulers that feed it,
any builds currently in the queue, which
@@ -431,6 +435,7 @@
def setupUsualPages(self):
#self.putChild("", IndexOrWaterfallRedirection())
self.putChild("waterfall", WaterfallStatusResource())
+ self.putChild("grid", GridStatusResource())
self.putChild("builders", BuildersResource()) # has builds/steps/logs
self.putChild("changes", ChangesResource())
self.putChild("buildslaves", BuildSlavesResource())
Index: build.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/build.py,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- build.py 21 Nov 2007 09:23:34 -0000 1.16
+++ build.py 22 May 2008 22:11:25 -0000 1.17
@@ -10,6 +10,7 @@
from buildbot.status.web.tests import TestsResource
from buildbot.status.web.step import StepsResource
+from buildbot import version, util
# /builders/$builder/builds/$buildnum
class StatusResourceBuild(HtmlResource):
@@ -29,6 +30,7 @@
def body(self, req):
b = self.build_status
status = self.getStatus(req)
+ projectURL = status.getProjectURL()
projectName = status.getProjectName()
data = ('<div class="title"><a href="%s">%s</a></div>\n'
% (self.path_to_root(req), projectName))
@@ -136,7 +138,6 @@
data += "</ol>\n"
#data += html.PRE(b.changesText()) # TODO
-
if b.isFinished() and self.builder_control is not None:
data += "<h3>Resubmit Build:</h3>\n"
# can we rebuild it exactly?
@@ -163,6 +164,29 @@
data += '<input type="submit" value="Rebuild" />\n'
data += '</form>\n'
+ # TODO: this stuff should be generated by a template of some sort
+ data += '<hr /><div class="footer">\n'
+
+ welcomeurl = self.path_to_root(req) + "index.html"
+ data += '[<a href="%s">welcome</a>]\n' % welcomeurl
+ data += "<br />\n"
+
+ data += '<a href="http://buildbot.sourceforge.net/">Buildbot</a>'
+ data += "-%s " % version
+ if projectName:
+ data += "working for the "
+ if projectURL:
+ data += "<a href=\"%s\">%s</a> project." % (projectURL,
+ projectName)
+ else:
+ data += "%s project." % projectName
+ data += "<br />\n"
+ data += ("Page built: " +
+ time.strftime("%a %d %b %Y %H:%M:%S",
+ time.localtime(util.now()))
+ + "\n")
+ data += '</div>\n'
+
return data
def stop(self, req):
@@ -176,11 +200,8 @@
"'%s': %s\n" % (name, comments))
c.stopBuild(reason)
# we're at http://localhost:8080/svn-hello/builds/5/stop?[args] and
- # we want to go to: http://localhost:8080/svn-hello/builds/5 or
- # http://localhost:8080/waterfall
- #
- #return Redirect("../%d" % self.build.getNumber())
- r = Redirect("../../../../waterfall")
+ # we want to go to: http://localhost:8080/svn-hello
+ return Redirect("../..")
d = defer.Deferred()
reactor.callLater(1, d.callback, r)
return DeferredResource(d)
@@ -209,11 +230,7 @@
# have to wait for a current build to finish). The next-most
# preferred place is somewhere that the user can see tangible
# evidence of their build starting (or to see the reason that it
- # didn't start). This could either be the Builder page, or the
- # waterfall.
- #r = Redirect("../../../..") # this takes us back to the welcome page
- #r = Redirect("../../../../waterfall") # or the Waterfall
- #r = Redirect("../../../../waterfall?show=%s" % builder_name)
+ # didn't start). This should be the Builder page.
r = Redirect("../..") # the Builder's page
d = defer.Deferred()
reactor.callLater(1, d.callback, r)
Index: builder.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/builder.py,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -d -r1.17 -r1.18
--- builder.py 21 Nov 2007 09:24:18 -0000 1.17
+++ builder.py 22 May 2008 22:11:25 -0000 1.18
@@ -10,6 +10,7 @@
make_force_build_form, OneLineMixin
from buildbot.process.base import BuildRequest
from buildbot.sourcestamp import SourceStamp
+from buildbot import version, util
from buildbot.status.web.build import BuildsResource, StatusResourceBuild
@@ -124,6 +125,31 @@
</form>
""" % pingURL
+ # TODO: this stuff should be generated by a template of some sort
+ projectURL = status.getProjectURL()
+ projectName = status.getProjectName()
+ data += '<hr /><div class="footer">\n'
+
+ welcomeurl = self.path_to_root(req) + "index.html"
+ data += '[<a href="%s">welcome</a>]\n' % welcomeurl
+ data += "<br />\n"
+
+ data += '<a href="http://buildbot.sourceforge.net/">Buildbot</a>'
+ data += "-%s " % version
+ if projectName:
+ data += "working for the "
+ if projectURL:
+ data += "<a href=\"%s\">%s</a> project." % (projectURL,
+ projectName)
+ else:
+ data += "%s project." % projectName
+ data += "<br />\n"
+ data += ("Page built: " +
+ time.strftime("%a %d %b %Y %H:%M:%S",
+ time.localtime(util.now()))
+ + "\n")
+ data += '</div>\n'
+
return data
def force(self, req):
@@ -166,12 +192,14 @@
# TODO: tell the web user that their request could not be
# honored
pass
- return Redirect("../../waterfall")
+ # send the user back to the builder page
+ return Redirect(".")
def ping(self, req):
log.msg("web ping of builder '%s'" % self.builder_status.getName())
self.builder_control.ping() # TODO: there ought to be an ISlaveControl
- return Redirect("../../waterfall")
+ # send the user back to the builder page
+ return Redirect(".")
def getChild(self, path, req):
if path == "force":
@@ -229,7 +257,8 @@
builder_control = c.getBuilder(bname)
build = StatusResourceBuilder(builder_status, builder_control)
build.force(req)
- return Redirect("../../waterfall")
+ # back to the welcome page
+ return Redirect("../..")
def stop(self, req):
for bname in self.status.getBuilderNames():
@@ -252,7 +281,8 @@
build = StatusResourceBuild(build_status, build_control,
builder_control)
build.stop(req)
- return Redirect("../../waterfall")
+ # go back to the welcome page
+ return Redirect("../..")
# /builders
@@ -275,6 +305,56 @@
urllib.quote(bname, safe='')))
data += "</ol>\n"
+ # TODO: this stuff should be generated by a template of some sort
+ projectURL = s.getProjectURL()
+ projectName = s.getProjectName()
+ data += '<hr /><div class="footer">\n'
+
+ welcomeurl = self.path_to_root(req) + "index.html"
+ data += '[<a href="%s">welcome</a>]\n' % welcomeurl
+ data += "<br />\n"
+
+ data += '<a href="http://buildbot.sourceforge.net/">Buildbot</a>'
+ data += "-%s " % version
+ if projectName:
+ data += "working for the "
+ if projectURL:
+ data += "<a href=\"%s\">%s</a> project." % (projectURL,
+ projectName)
+ else:
+ data += "%s project." % projectName
+ data += "<br />\n"
+ data += ("Page built: " +
+ time.strftime("%a %d %b %Y %H:%M:%S",
+ time.localtime(util.now()))
+ + "\n")
+ data += '</div>\n'
+
+ # TODO: this stuff should be generated by a template of some sort
+ projectURL = s.getProjectURL()
+ projectName = s.getProjectName()
+ data += '<hr /><div class="footer">\n'
+
+ welcomeurl = self.path_to_root(req) + "index.html"
+ data += '[<a href="%s">welcome</a>]\n' % welcomeurl
+ data += "<br />\n"
+
+ data += '<a href="http://buildbot.sourceforge.net/">Buildbot</a>'
+ data += "-%s " % version
+ if projectName:
+ data += "working for the "
+ if projectURL:
+ data += "<a href=\"%s\">%s</a> project." % (projectURL,
+ projectName)
+ else:
+ data += "%s project." % projectName
+ data += "<br />\n"
+ data += ("Page built: " +
+ time.strftime("%a %d %b %Y %H:%M:%S",
+ time.localtime(util.now()))
+ + "\n")
+ data += '</div>\n'
+
return data
def getChild(self, path, req):
Index: classic.css
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/classic.css,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- classic.css 30 Sep 2007 09:23:57 -0000 1.2
+++ classic.css 22 May 2008 22:11:25 -0000 1.3
@@ -41,3 +41,38 @@
.start,.running {
background-color: yellow;
}
+
+/* grid styles */
+
+table.Grid {
+ border-collapse: collapse;
+}
+
+table.Grid tr td {
+ padding: 0.2em;
+ margin: 0px;
+ text-align: center;
+}
+
+table.Grid tr td.title {
+ font-size: 90%;
+ border-right: 1px gray solid;
+ border-bottom: 1px gray solid;
+}
+
+table.Grid tr td.sourcestamp {
+ font-size: 90%;
+}
+
+table.Grid tr td.builder {
+ text-align: right;
+ font-size: 90%;
+}
+
+table.Grid tr td.build {
+ border: 1px gray solid;
+}
+
+div.footer {
+ font-size: 80%;
+}
--- NEW FILE: grid.py ---
from __future__ import generators
import sys, string, types, time, os.path
import urllib
from buildbot import interfaces, util
from buildbot import version
from buildbot.status.web.base import HtmlResource
# set grid_css to the full pathname of the css file
if hasattr(sys, "frozen"):
# all 'data' files are in the directory of our executable
here = os.path.dirname(sys.executable)
grid_css = os.path.abspath(os.path.join(here, "grid.css"))
else:
# running from source; look for a sibling to __file__
up = os.path.dirname
grid_css = os.path.abspath(os.path.join(up(__file__), "grid.css"))
class ANYBRANCH: pass # a flag value, used below
class GridStatusResource(HtmlResource):
# TODO: docs
status = None
control = None
changemaster = None
def __init__(self, allowForce=True, css=None):
HtmlResource.__init__(self)
self.allowForce = allowForce
self.css = css or grid_css
def getTitle(self, request):
status = self.getStatus(request)
p = status.getProjectName()
if p:
return "BuildBot: %s" % p
else:
return "BuildBot"
def getChangemaster(self, request):
# TODO: this wants to go away, access it through IStatus
return request.site.buildbot_service.parent.change_svc
# handle reloads through an http header
# TODO: send this as a real header, rather than a tag
def get_reload_time(self, request):
if "reload" in request.args:
try:
reload_time = int(request.args["reload"][0])
return max(reload_time, 15)
except ValueError:
pass
return None
def head(self, request):
head = ''
reload_time = self.get_reload_time(request)
if reload_time is not None:
head += '<meta http-equiv="refresh" content="%d">\n' % reload_time
return head
# def setBuildmaster(self, buildmaster):
# self.status = buildmaster.getStatus()
# if self.allowForce:
# self.control = interfaces.IControl(buildmaster)
# else:
# self.control = None
# self.changemaster = buildmaster.change_svc
#
# # try to set the page title
# p = self.status.getProjectName()
# if p:
# self.title = "BuildBot: %s" % p
#
def build_td(self, request, build):
if not build:
return '<td class="build"> </td>\n'
if build.isFinished():
color = build.getColor()
if color == 'green': color = '#72ff75' # the "Buildbot Green"
# get the text and annotate the first line with a link
text = build.getText()
if not text: text = [ "(no information)" ]
if text == [ "build", "successful" ]: text = [ "OK" ]
else:
color = 'yellow' # to match the yellow of the builder
text = [ 'building' ]
name = build.getBuilder().getName()
number = build.getNumber()
url = "builders/%s/builds/%d" % (name, number)
text[0] = '<a href="%s">%s</a>' % (url, text[0])
text = '<br />\n'.join(text)
return '<td class="build" bgcolor="%s">%s</td>\n' % (color, text)
def builder_td(self, request, builder):
state, builds = builder.getState()
# look for upcoming builds. We say the state is "waiting" if the
# builder is otherwise idle and there is a scheduler which tells us a
# build will be performed some time in the near future. TODO: this
# functionality used to be in BuilderStatus.. maybe this code should
# be merged back into it.
upcoming = []
builderName = builder.getName()
for s in self.getStatus(request).getSchedulers():
if builderName in s.listBuilderNames():
upcoming.extend(s.getPendingBuildTimes())
if state == "idle" and upcoming:
state = "waiting"
if state == "building":
color = "yellow"
elif state == "offline":
color = "red"
elif state == "idle":
color = "white"
elif state == "waiting":
color = "yellow"
else:
color = "white"
# TODO: for now, this pending/upcoming stuff is in the "current
# activity" box, but really it should go into a "next activity" row
# instead. The only times it should show up in "current activity" is
# when the builder is otherwise idle.
# are any builds pending? (waiting for a slave to be free)
url = 'builders/%s/' % urllib.quote(builder.getName(), safe='')
text = '<a href="%s">%s</a>' % (url, builder.getName())
pbs = builder.getPendingBuilds()
if state != 'idle' or pbs:
if pbs:
text += "<br />(%s with %d pending)" % (state, len(pbs))
else:
text += "<br />(%s)" % state
return '<td valign="center" bgcolor="%s" class="builder">%s</td>\n' % \
(color, text)
def stamp_td(self, stamp):
text = stamp.getText()
return '<td valign="bottom" class="sourcestamp">%s</td>\n' % \
"<br />".join(text)
def body(self, request):
"This method builds the main waterfall display."
# get url parameters
numBuilds = int(request.args.get("width", [5])[0])
categories = request.args.get("category", [])
branch = request.args.get("branch", [ANYBRANCH])[0]
if branch == 'trunk': branch = None
# and the data we want to render
status = self.getStatus(request)
stamps = self.getRecentSourcestamps(status, numBuilds, categories, branch)
projectURL = status.getProjectURL()
projectName = status.getProjectName()
data = '<table class="Grid" border="0" cellspacing="0">\n'
data += '<tr>\n'
data += '<td class="title"><a href="%s">%s</a>' % (projectURL, projectName)
if categories:
if len(categories) > 1:
data += '\n<br /><b>Categories:</b><br/>%s' % ('<br/>'.join(categories))
else:
data += '\n<br /><b>Category:</b> %s' % categories[0]
if branch != ANYBRANCH:
data += '\n<br /><b>Branch:</b> %s' % (branch or 'trunk')
data += '</td>\n'
for stamp in stamps:
data += self.stamp_td(stamp)
data += '</tr>\n'
for bn in status.getBuilderNames():
builds = [None] * len(stamps)
builder = status.getBuilder(bn)
if categories and builder.category not in categories:
continue
build = builder.getBuild(-1)
while build and None in builds:
ss = build.getSourceStamp(specific=True)
for i in range(len(stamps)):
if ss == stamps[i] and builds[i] is None:
builds[i] = build
build = build.getPreviousBuild()
data += '<tr>\n'
data += self.builder_td(request, builder)
for build in builds:
data += self.build_td(request, build)
data += '</tr>\n'
data += '</table>\n'
# TODO: this stuff should be generated by a template of some sort
data += '<hr /><div class="footer">\n'
welcomeurl = self.path_to_root(request) + "index.html"
data += '[<a href="%s">welcome</a>]\n' % welcomeurl
data += "<br />\n"
data += '<a href="http://buildbot.sourceforge.net/">Buildbot</a>'
data += "-%s " % version
if projectName:
data += "working for the "
if projectURL:
data += "<a href=\"%s\">%s</a> project." % (projectURL,
projectName)
else:
data += "%s project." % projectName
data += "<br />\n"
data += ("Page built: " +
time.strftime("%a %d %b %Y %H:%M:%S",
time.localtime(util.now()))
+ "\n")
data += '</div>\n'
return data
def getRecentSourcestamps(self, status, numBuilds, categories, branch):
"""
get a list of the most recent NUMBUILDS SourceStamp tuples, sorted
by the earliest start we've seen for them
"""
# TODO: use baseweb's getLastNBuilds?
sourcestamps = { } # { ss-tuple : earliest time }
for bn in status.getBuilderNames():
builder = status.getBuilder(bn)
if categories and builder.category not in categories:
continue
build = builder.getBuild(-1)
while build:
ss = build.getSourceStamp(specific=True)
start = build.getTimes()[0]
build = build.getPreviousBuild()
# skip un-started builds
if not start: continue
# skip non-matching branches
if branch != ANYBRANCH and ss.branch != branch: continue
sourcestamps[ss] = min(sourcestamps.get(ss, sys.maxint), start)
# now sort those and take the NUMBUILDS most recent
sourcestamps = sourcestamps.items()
sourcestamps.sort(lambda x, y: cmp(x[1], y[1]))
sourcestamps = map(lambda tup : tup[0], sourcestamps)
sourcestamps = sourcestamps[-numBuilds:]
return sourcestamps
Index: index.html
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/index.html,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- index.html 26 Sep 2007 06:32:24 -0000 1.4
+++ index.html 22 May 2008 22:11:25 -0000 1.5
@@ -12,6 +12,9 @@
<li>the <a href="waterfall">Waterfall Display</a> will give you a
time-oriented summary of recent buildbot activity.</li>
+ <li>the <a href="grid">Grid Display</a> will give you a
+ developer-oriented summary of recent buildbot activity.</li>
+
<li>The <a href="one_box_per_builder">Latest Build</a> for each builder is
here.</li>
Index: waterfall.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/web/waterfall.py,v
retrieving revision 1.28
retrieving revision 1.29
diff -u -d -r1.28 -r1.29
--- waterfall.py 30 Mar 2008 02:27:53 -0000 1.28
+++ waterfall.py 22 May 2008 22:11:25 -0000 1.29
@@ -528,7 +528,7 @@
data += "</table>\n"
- data += "<hr />\n"
+ data += '<hr /><div class="footer"\n'
def with_args(req, remove_args=[], new_args=[], new_path=None):
# sigh, nevow makes this sort of manipulation easier
@@ -591,6 +591,7 @@
time.strftime("%a %d %b %Y %H:%M:%S",
time.localtime(util.now()))
+ "\n")
+ data += '</div>\n'
return data
def body0(self, request, builders):
More information about the Commits
mailing list