📄 baseweb.py
字号:
import os, sys, urllib, weakreffrom itertools import countfrom zope.interface import implementsfrom twisted.python import logfrom twisted.application import strports, servicefrom twisted.web import server, distrib, static, htmlfrom twisted.spread import pbfrom buildbot.interfaces import IControl, IStatusReceiverfrom buildbot.status.web.base import HtmlResource, Box, \ build_get_class, ICurrentBox, OneLineMixin, map_branches, \ make_stop_form, make_force_build_formfrom buildbot.status.web.feeds import Rss20StatusResource, \ Atom10StatusResourcefrom buildbot.status.web.waterfall import WaterfallStatusResourcefrom buildbot.status.web.grid import GridStatusResourcefrom buildbot.status.web.changes import ChangesResourcefrom buildbot.status.web.builder import BuildersResourcefrom buildbot.status.web.slaves import BuildSlavesResourcefrom buildbot.status.web.xmlrpc import XMLRPCServerfrom buildbot.status.web.about import AboutBuildbot# this class contains the status services (WebStatus and the older Waterfall)# which can be put in c['status']. It also contains some of the resources# that are attached to the WebStatus at various well-known URLs, which the# admin might wish to attach (using WebStatus.putChild) at other URLs.class LastBuild(HtmlResource): def body(self, request): return "missing\n"def getLastNBuilds(status, numbuilds, builders=[], branches=[]): """Return a list with the last few Builds, sorted by start time. builder_names=None means all builders """ # TODO: this unsorts the list of builder names, ick builder_names = set(status.getBuilderNames()) if builders: builder_names = builder_names.intersection(set(builders)) # to make sure that we get everything, we must get 'numbuilds' builds # from *each* source, then sort by ending time, then trim to the last # 20. We could be more efficient, but it would require the same # gnarly code that the Waterfall uses to generate one event at a # time. TODO: factor that code out into some useful class. events = [] for builder_name in builder_names: builder = status.getBuilder(builder_name) for build_number in count(1): if build_number > numbuilds: break # enough from this builder, move on to another build = builder.getBuild(-build_number) if not build: break # no more builds here, move on to the next builder #if not build.isFinished(): # continue (build_start, build_end) = build.getTimes() event = (build_start, builder_name, build) events.append(event) def _sorter(a, b): return cmp( a[:2], b[:2] ) events.sort(_sorter) # now only return the actual build, and only return some of them return [e[2] for e in events[-numbuilds:]]# /one_line_per_build# accepts builder=, branch=, numbuilds=class OneLinePerBuild(HtmlResource, OneLineMixin): """This shows one line per build, combining all builders together. Useful query arguments: numbuilds=: how many lines to display builder=: show only builds for this builder. Multiple builder= arguments can be used to see builds from any builder in the set. """ title = "Recent Builds" def __init__(self, numbuilds=20): HtmlResource.__init__(self) self.numbuilds = numbuilds def getChild(self, path, req): status = self.getStatus(req) builder = status.getBuilder(path) return OneLinePerBuildOneBuilder(builder) def body(self, req): status = self.getStatus(req) control = self.getControl(req) numbuilds = int(req.args.get("numbuilds", [self.numbuilds])[0]) builders = req.args.get("builder", []) branches = [b for b in req.args.get("branch", []) if b] g = status.generateFinishedBuilds(builders, map_branches(branches), numbuilds) data = "" # really this is "up to %d builds" data += "<h1>Last %d finished builds: %s</h1>\n" % \ (numbuilds, ", ".join(branches)) if builders: data += ("<p>of builders: %s</p>\n" % (", ".join(builders))) data += "<ul>\n" got = 0 building = False online = 0 for build in g: got += 1 data += " <li>" + self.make_line(req, build) + "</li>\n" builder_status = build.getBuilder().getState()[0] if builder_status == "building": building = True online += 1 elif builder_status != "offline": online += 1 if not got: data += " <li>No matching builds found</li>\n" data += "</ul>\n" if control is not None: if building: stopURL = "builders/_all/stop" data += make_stop_form(stopURL, True, "Builds") if online: forceURL = "builders/_all/force" data += make_force_build_form(forceURL, True) return data# /one_line_per_build/$BUILDERNAME# accepts branch=, numbuilds=class OneLinePerBuildOneBuilder(HtmlResource, OneLineMixin): def __init__(self, builder, numbuilds=20): HtmlResource.__init__(self) self.builder = builder self.builder_name = builder.getName() self.numbuilds = numbuilds self.title = "Recent Builds of %s" % self.builder_name def body(self, req): status = self.getStatus(req) numbuilds = int(req.args.get("numbuilds", [self.numbuilds])[0]) branches = [b for b in req.args.get("branch", []) if b] # walk backwards through all builds of a single builder g = self.builder.generateFinishedBuilds(map_branches(branches), numbuilds) data = "" data += ("<h1>Last %d builds of builder %s: %s</h1>\n" % (numbuilds, self.builder_name, ", ".join(branches))) data += "<ul>\n" got = 0 for build in g: got += 1 data += " <li>" + self.make_line(req, build) + "</li>\n" if not got: data += " <li>No matching builds found</li>\n" data += "</ul>\n" return data# /one_box_per_builder# accepts builder=, branch=class OneBoxPerBuilder(HtmlResource): """This shows a narrow table with one row per builder. The leftmost column contains the builder name. The next column contains the results of the most recent build. The right-hand column shows the builder's current activity. builder=: show only builds for this builder. Multiple builder= arguments can be used to see builds from any builder in the set. """ title = "Latest Build" def body(self, req): status = self.getStatus(req) control = self.getControl(req) builders = req.args.get("builder", status.getBuilderNames()) branches = [b for b in req.args.get("branch", []) if b] data = "" data += "<h2>Latest builds: %s</h2>\n" % ", ".join(branches) data += "<table>\n" building = False online = 0 base_builders_url = self.path_to_root(req) + "builders/" for bn in builders: base_builder_url = base_builders_url + urllib.quote(bn, safe='') builder = status.getBuilder(bn) data += "<tr>\n" data += '<td class="box"><a href="%s">%s</a></td>\n' \ % (base_builder_url, html.escape(bn)) builds = list(builder.generateFinishedBuilds(map_branches(branches), num_builds=1)) if builds: b = builds[0] url = (base_builder_url + "/builds/%d" % b.getNumber()) try: label = b.getProperty("got_revision") except KeyError: label = None if not label or len(str(label)) > 20: label = "#%d" % b.getNumber() text = ['<a href="%s">%s</a>' % (url, label)] text.extend(b.getText()) box = Box(text, class_="LastBuild box %s" % build_get_class(b)) data += box.td(align="center") else: data += '<td class="LastBuild box" >no build</td>\n' current_box = ICurrentBox(builder).getBox(status) data += current_box.td(align="center") builder_status = builder.getState()[0] if builder_status == "building": building = True online += 1 elif builder_status != "offline": online += 1 data += "</table>\n" if control is not None: if building: stopURL = "builders/_all/stop" data += make_stop_form(stopURL, True, "Builds") if online: forceURL = "builders/_all/force" data += make_force_build_form(forceURL, True) return dataHEADER = '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">'''HEAD_ELEMENTS = [ '<title>%(title)s</title>', '<link href="%(root)sbuildbot.css" rel="stylesheet" type="text/css" />', ]BODY_ATTRS = { 'vlink': "#800080", }FOOTER = '''</html>'''class WebStatus(service.MultiService): implements(IStatusReceiver) # TODO: IStatusReceiver is really about things which subscribe to hear # about buildbot events. We need a different interface (perhaps a parent # of IStatusReceiver) for status targets that don't subscribe, like the # WebStatus class. buildbot.master.BuildMaster.loadConfig:737 asserts # that everything in c['status'] provides IStatusReceiver, but really it # should check that they provide IStatusTarget instead. """ The webserver provided by this class has the following resources: /waterfall : the big time-oriented 'waterfall' display, with links to individual changes, builders, builds, steps, and logs. A number of query-arguments can be added to influence the display. /rss : a rss feed summarizing all failed builds. The same query-arguments used by 'waterfall' can be added to influence the feed output. /atom : an atom feed summarizing all failed builds. The same query-arguments used by 'waterfall' can be added to influence the feed output. /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 buildslaves are designated or attached, and a summary of the build process it uses. /builders/BUILDERNAME/builds/NUM: a page describing a single Build /builders/BUILDERNAME/builds/NUM/steps/STEPNAME: describes a single step /builders/BUILDERNAME/builds/NUM/steps/STEPNAME/logs/LOGNAME: a StatusLog /builders/BUILDERNAME/builds/NUM/tests : summarize test results /builders/BUILDERNAME/builds/NUM/tests/TEST.NAME: results of one test
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -