⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 waterfall.py

📁 The BuildBot is a system to automate the compile/test cycle required by most software projects. CVS
💻 PY
📖 第 1 页 / 共 3 页
字号:
# -*- test-case-name: buildbot.test.test_web -*-from zope.interface import implementsfrom twisted.python import log, componentsfrom twisted.web import htmlimport urllibimport timeimport operatorfrom buildbot import interfaces, utilfrom buildbot import versionfrom buildbot.status import builderfrom buildbot.status.web.base import Box, HtmlResource, IBox, ICurrentBox, \     ITopBox, td, build_get_class, path_to_build, path_to_step, map_branchesclass CurrentBox(components.Adapter):    # this provides the "current activity" box, just above the builder name    implements(ICurrentBox)    def formatETA(self, prefix, eta):        if eta is None:            return []        if eta < 60:            return ["< 1 min"]        eta_parts = ["~"]        eta_secs = eta        if eta_secs > 3600:            eta_parts.append("%d hrs" % (eta_secs / 3600))            eta_secs %= 3600        if eta_secs > 60:            eta_parts.append("%d mins" % (eta_secs / 60))            eta_secs %= 60        abstime = time.strftime("%H:%M", time.localtime(util.now()+eta))        return [prefix, " ".join(eta_parts), "at %s" % abstime]    def getBox(self, status):        # getState() returns offline, idle, or building        state, builds = self.original.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 = self.original.getName()        for s in status.getSchedulers():            if builderName in s.listBuilderNames():                upcoming.extend(s.getPendingBuildTimes())        if state == "idle" and upcoming:            state = "waiting"        if state == "building":            text = ["building"]            if builds:                for b in builds:                    eta = b.getETA()                    text.extend(self.formatETA("ETA in", eta))        elif state == "offline":            text = ["offline"]        elif state == "idle":            text = ["idle"]        elif state == "waiting":            text = ["waiting"]        else:            # just in case I add a state and forget to update this            text = [state]        # 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)        pbs = self.original.getPendingBuilds()        if pbs:            text.append("%d pending" % len(pbs))        for t in upcoming:            eta = t - util.now()            text.extend(self.formatETA("next in", eta))        return Box(text, class_="Activity " + state)components.registerAdapter(CurrentBox, builder.BuilderStatus, ICurrentBox)class BuildTopBox(components.Adapter):    # this provides a per-builder box at the very top of the display,    # showing the results of the most recent build    implements(IBox)    def getBox(self, req):        assert interfaces.IBuilderStatus(self.original)        branches = [b for b in req.args.get("branch", []) if b]        builder = self.original        builds = list(builder.generateFinishedBuilds(map_branches(branches),                                                     num_builds=1))        if not builds:            return Box(["none"], class_="LastBuild")        b = builds[0]        name = b.getBuilder().getName()        number = b.getNumber()        url = path_to_build(req, b)        text = b.getText()        tests_failed = b.getSummaryStatistic('tests-failed', operator.add, 0)        if tests_failed: text.extend(["Failed tests: %d" % tests_failed])        # TODO: maybe add logs?        # TODO: add link to the per-build page at 'url'        class_ = build_get_class(b)        return Box(text, class_="LastBuild %s" % class_)components.registerAdapter(BuildTopBox, builder.BuilderStatus, ITopBox)class BuildBox(components.Adapter):    # this provides the yellow "starting line" box for each build    implements(IBox)    def getBox(self, req):        b = self.original        number = b.getNumber()        url = path_to_build(req, b)        reason = b.getReason()        text = ('<a title="Reason: %s" href="%s">Build %d</a>'                % (html.escape(reason), url, number))        class_ = "start"        if b.isFinished() and not b.getSteps():            # the steps have been pruned, so there won't be any indication            # of whether it succeeded or failed.            class_ = build_get_class(b)        return Box([text], class_="BuildStep " + class_)components.registerAdapter(BuildBox, builder.BuildStatus, IBox)class StepBox(components.Adapter):    implements(IBox)    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)            text = []        text = text[:]        logs = self.original.getLogs()        for num in range(len(logs)):            name = logs[num].getName()            if logs[num].hasContents():                url = urlbase + "/logs/%s" % urllib.quote(name)                text.append("<a href=\"%s\">%s</a>" % (url, html.escape(name)))            else:                text.append(html.escape(name))        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)))        class_ = "BuildStep " + build_get_class(self.original)        return Box(text, class_=class_)components.registerAdapter(StepBox, builder.BuildStepStatus, IBox)class EventBox(components.Adapter):    implements(IBox)    def getBox(self, req):        text = self.original.getText()        class_ = "Event"        return Box(text, class_=class_)components.registerAdapter(EventBox, builder.Event, IBox)        class Spacer:    implements(interfaces.IStatusEvent)    def __init__(self, start, finish):        self.started = start        self.finished = finish    def getTimes(self):        return (self.started, self.finished)    def getText(self):        return []class SpacerBox(components.Adapter):    implements(IBox)    def getBox(self, req):        #b = Box(["spacer"], "white")        b = Box([])        b.spacer = True        return bcomponents.registerAdapter(SpacerBox, Spacer, IBox)    def insertGaps(g, lastEventTime, idleGap=2):    debug = False    e = g.next()    starts, finishes = e.getTimes()    if debug: log.msg("E0", starts, finishes)    if finishes == 0:        finishes = starts    if debug: log.msg("E1 finishes=%s, gap=%s, lET=%s" % \                      (finishes, idleGap, lastEventTime))    if finishes is not None and finishes + idleGap < lastEventTime:        if debug: log.msg(" spacer0")        yield Spacer(finishes, lastEventTime)    followingEventStarts = starts    if debug: log.msg(" fES0", starts)    yield e    while 1:        e = g.next()        starts, finishes = e.getTimes()        if debug: log.msg("E2", starts, finishes)        if finishes == 0:            finishes = starts        if finishes is not None and finishes + idleGap < followingEventStarts:            # there is a gap between the end of this event and the beginning            # of the next one. Insert an idle event so the waterfall display            # shows a gap here.            if debug:                log.msg(" finishes=%s, gap=%s, fES=%s" % \                        (finishes, idleGap, followingEventStarts))            yield Spacer(finishes, followingEventStarts)        yield e        followingEventStarts = starts        if debug: log.msg(" fES1", starts)HELP = '''<form action="../waterfall" method="GET"><h1>The Waterfall Display</h1><p>The Waterfall display can be controlled by adding query arguments to theURL. For example, if your Waterfall is accessed via the URL<tt>http://buildbot.example.org:8080</tt>, then you could add a<tt>branch=</tt> argument (described below) by going to<tt>http://buildbot.example.org:8080?branch=beta4</tt> instead. Remember thatquery arguments are separated from each other with ampersands, but they areseparated from the main URL with a question mark, so to add a<tt>branch=</tt> and two <tt>builder=</tt> arguments, you would use<tt>http://buildbot.example.org:8080?branch=beta4&amp;builder=unix&amp;builder=macos</tt>.</p><h2>Limiting the Displayed Interval</h2><p>The <tt>last_time=</tt> argument is a unix timestamp (seconds since thestart of 1970) that will be used as an upper bound on the interval of eventsdisplayed: nothing will be shown that is more recent than the given time.When no argument is provided, all events up to and including the most recentsteps are included.</p><p>The <tt>first_time=</tt> argument provides the lower bound. No events willbe displayed that occurred <b>before</b> this timestamp. Instead of providing<tt>first_time=</tt>, you can provide <tt>show_time=</tt>: in this case,<tt>first_time</tt> will be set equal to <tt>last_time</tt> minus<tt>show_time</tt>. <tt>show_time</tt> overrides <tt>first_time</tt>.</p><p>The display normally shows the latest 200 events that occurred in thegiven interval, where each timestamp on the left hand edge counts as a singleevent. You can add a <tt>num_events=</tt> argument to override this this.</p><h2>Hiding non-Build events</h2><p>By passing <tt>show_events=false</tt>, you can remove the "buildslaveattached", "buildslave detached", and "builder reconfigured" events thatappear in-between the actual builds.</p>%(show_events_input)s<h2>Showing only Certain Branches</h2><p>If you provide one or more <tt>branch=</tt> arguments, the display will belimited to builds that used one of the given branches. If no <tt>branch=</tt>arguments are given, builds from all branches will be displayed.</p>Erase the text from these "Show Branch:" boxes to remove that branch filter.%(show_branches_input)s<h2>Limiting the Builders that are Displayed</h2><p>By adding one or more <tt>builder=</tt> arguments, the display will belimited to showing builds that ran on the given builders. This serves tolimit the display to the specific named columns. If no <tt>builder=</tt>arguments are provided, all Builders will be displayed.</p><p>To view a Waterfall page with only a subset of Builders displayed, selectthe Builders you are interested in here.</p>%(show_builders_input)s<h2>Auto-reloading the Page</h2><p>Adding a <tt>reload=</tt> argument will cause the page to automaticallyreload itself after that many seconds.</p>%(show_reload_input)s<h2>Reload Waterfall Page</h2><input type="submit" value="View Waterfall" /></form>'''class WaterfallHelp(HtmlResource):    title = "Waterfall Help"    def __init__(self, categories=None):        HtmlResource.__init__(self)        self.categories = categories    def body(self, request):        data = ''        status = self.getStatus(request)        showEvents_checked = 'checked="checked"'        if request.args.get("show_events", ["true"])[0].lower() == "true":            showEvents_checked = ''

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -