First attempt at timestamps
This patch can be applied to buildbot-0.7.9 to enable build-logs to use the format
1233811875.619101 | 'local-slave' | local-osx | 41 | pulling from http://hg.mozilla.org/mozilla-central/ 1233811875.9946001 | 'local-slave' | local-osx | 41 | searching for changes 1233811878.362067 | 'local-slave' | local-osx | 41 | adding changesets 1233811878.373631 | 'local-slave' | local-osx | 41 | adding manifests 1233811878.7630169 | 'local-slave' | local-osx | 41 | adding file changes 1233811881.238174 | 'local-slave' | local-osx | 41 | added 2 changesets with 4 changes to 4 files
The format is unixtime | build slave name | builder name | build number | output
The patch is applied against http://downloads.sourceforge.net/buildbot/buildbot-0.7.9.tar.gz
File:Timestamp.patch.zip
diff -r a9c16958d5dc buildbot/buildbot/status/builder.py --- a/buildbot/buildbot/status/builder.py Thu Feb 05 00:37:33 2009 -0500 +++ b/buildbot/buildbot/status/builder.py Thu Feb 05 00:39:45 2009 -0500 @@ -393,9 +393,9 @@ self.length += len(text) def addStdout(self, text): - self.addEntry(STDOUT, text) + self.addEntry(STDOUT, repr(time()) + ' | ' + repr(self.getStep().build.slavename) + ' | ' + self.getStep().build.getBuilder().getName() + ' | ' + repr(self.getStep().build.getNumber()) + ' | ' + text + '\n') def addStderr(self, text): - self.addEntry(STDERR, text) + self.addEntry(STDERR, repr(time()) + ' | ' + repr(self.getStep().build.slavename) + ' | ' + self.getStep().build.getBuilder().getName() + ' | ' + repr(self.getStep().build.getNumber()) + ' | ' + text + '\n') def addHeader(self, text): self.addEntry(HEADER, text)
New Timestamps Patch
This patch, applied to the previous patch changes the behaviour of the timestamps. Instead of having all information on ever line it only puts a timestamp on each line. Information that is constant throughout the run are only inserted once
diff -r 9a6c6b742a5a buildbot/buildbot/status/builder.py --- a/buildbot/buildbot/status/builder.py Sat Feb 07 12:22:37 2009 -0500 +++ b/buildbot/buildbot/status/builder.py Sat Feb 07 12:27:49 2009 -0500 @@ -6,6 +6,8 @@ from twisted.internet import reactor, defer from twisted.protocols import basic from buildbot.process.properties import Properties +# JOHNFORD +from time import time import os, shutil, sys, re, urllib, itertools from cPickle import load, dump @@ -232,6 +234,12 @@ self.runEntries = [] self.watchers = [] self.finishedWatchers = [] + # Add some information about the build to the log + self.addEntry(STDOUT, repr(time()) + ' | ' + + repr(self.getStep().build.slavename) + ' | ' + + self.getStep().build.getBuilder().getName() + ' | ' + + repr(self.getStep().build.getNumber()))# + ' | ' + #+ repr(getFilename())) def getFilename(self): return os.path.join(self.step.build.builder.basedir, self.filename) @@ -391,11 +399,11 @@ for w in self.watchers: w.logChunk(self.step.build, self.step, self, channel, text) self.length += len(text) - + def addStdout(self, text): - self.addEntry(STDOUT, repr(time()) + ' | ' + repr(self.getStep().build.slavename) + ' | ' + self.getStep().build.getBuilder().getName() + ' | ' + repr(self.getStep().build.getNumber()) + ' | ' + text + '\n') + self.addEntry(STDOUT, repr(time()) + ' | ' + text) def addStderr(self, text): - self.addEntry(STDERR, repr(time()) + ' | ' + repr(self.getStep().build.slavename) + ' | ' + self.getStep().build.getBuilder().getName() + ' | ' + repr(self.getStep().build.getNumber()) + ' | ' + text + '\n') + self.addEntry(STDERR, repr(time()) + ' | ' + text) def addHeader(self, text): self.addEntry(HEADER, text)
MozBuild.py and Master.cfg
This patch adds my master.cfg I am using locally on my OSX machine and a modified mozbuild.py. Mozbuild.py contains all the buildbot steps used to test Mozilla. I have removed all non-OSX applicable code to minimize errors.
diff -r 9e48e21dd4f6 buildbot/buildbot.egg-info/SOURCES.txt --- a/buildbot/buildbot.egg-info/SOURCES.txt Thu Feb 05 00:39:57 2009 -0500 +++ b/buildbot/buildbot.egg-info/SOURCES.txt Sat Feb 07 12:21:02 2009 -0500 @@ -16,6 +16,7 @@ buildbot/locks.py buildbot/manhole.py buildbot/master.py +buildbot/mozbuild.py buildbot/pbutil.py buildbot/scheduler.py buildbot/sourcestamp.py diff -r 9e48e21dd4f6 buildbot/buildbot/mozbuild.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/buildbot/buildbot/mozbuild.py Sat Feb 07 12:21:02 2009 -0500 @@ -0,0 +1,345 @@ +# -*- Python -*- + +from buildbot import steps +from buildbot.steps.shell import ShellCommand +from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, SKIPPED, EXCEPTION, HEADER +import re +import os +import copy + +MozillaEnvironments = { } + +MozillaEnvironments['osx'] = { + "MOZ_NO_REMOTE": '1', + "NO_EM_RESTART": '1', + "XPCOM_DEBUG_BREAK": 'warn', + "CVS_RSH": 'ssh' +} + + + +class ShellCommandReportTimeout(ShellCommand): + """We subclass ShellCommand so that we can bubble up the timeout errors + to tinderbox that normally only get appended to the buildbot slave logs. + """ + + def evaluateCommand(self, cmd): + superResult = ShellCommand.evaluateCommand(self, cmd) + for line in cmd.logs['stdio'].readlines(channel=HEADER): + if "command timed out" in line: + self.addCompleteLog('timeout', + 'buildbot.slave.commands.TimeoutError: ' + + line + + "TinderboxPrint: " + + self.name + ' <em class="testfail">timeout</em><br/>\n') + return WARNINGS + return superResult + +class CreateDir(ShellCommandReportTimeout): + name = "create dir" + haltOnFailure = False + warnOnFailure = True + + def __init__(self, **kwargs): + if not 'platform' in kwargs: + return FAILURE + self.platform = kwargs['platform'] + if 'dir' in kwargs: + self.dir = kwargs['dir'] + if self.platform.startswith('win'): + self.command = r'if not exist ' + self.dir + r' mkdir ' + self.dir + else: + self.command = ['mkdir', '-p', self.dir] + ShellCommandReportTimeout.__init__(self, **kwargs) + +class TinderboxShellCommand(ShellCommand): + haltOnFailure = False + + def evaluateCommand(self, cmd): + return SUCCESS + +class MozillaCheck(ShellCommandReportTimeout): + name = "check" + warnOnFailure = True + description = ["checking"] + descriptionDone = ["check complete"] + command = ["make", "-k", "check"] + + def createSummary(self, log): + passCount = 0 + failCount = 0 + for line in log.readlines(): + if "PASS" in line: + passCount = passCount + 1 + if "FAIL" in line: + failCount = failCount + 1 + summary = "TinderboxPrint: TUnit<br/>" + str(passCount) + "/" + str(failCount) + "\n" + self.addCompleteLog('summary', summary) + + def evaluateCommand(self, cmd): + superResult = ShellCommandReportTimeout.evaluateCommand(self, cmd) + if SUCCESS != superResult: + return WARNINGS + if None != re.search('FAIL', cmd.logs['stdio'].getText()): + return WARNINGS + return SUCCESS + +class MozillaReftest(ShellCommandReportTimeout): + warnOnFailure = True + name = "reftest" + description = ["reftest"] + descriptionDone = ["reftest complete"] + + def createSummary(self, log): + testCount = 0 + passCount = 0 + failCount = 0 + knownFailCount = 0 + for line in log.readlines(): + if "REFTEST" not in line: + continue + if "IMAGE" in line: + continue + if "RESULT EXPECTED TO BE RANDOM" in line: + continue + testCount += 1 + if "UNEXPECTED" in line: + failCount += 1 + continue + if "KNOWN FAIL" in line: + knownFailCount += 1 + else: + passCount += 1 + summary = "TinderboxPrint: " + self.name + "<br/>" + str(passCount) + \ + "/" + str(failCount) + "/" + str(knownFailCount) + "\n" + self.addCompleteLog('summary', summary) + + def evaluateCommand(self, cmd): + superResult = ShellCommandReportTimeout.evaluateCommand(self, cmd) + if SUCCESS != superResult: + return WARNINGS + if re.search('UNEXPECTED', cmd.logs['stdio'].getText()): + return WARNINGS + return SUCCESS + +class MozillaOSXReftest(MozillaReftest): + command = ["../../objdir/dist/Minefield.app/Contents/MacOS/firefox", + "-console", + "-P", + "default", + "-reftest", + "reftest.list"] + +class MozillaCrashtest(MozillaReftest): + name = "crashtest" + description = ["crashtest"] + descriptionDone = ["crashtest complete"] + +class MozillaOSXCrashtest(MozillaCrashtest): + command = ["../../objdir/dist/Minefield.app/Contents/MacOS/firefox", + "-console", + "-P", + "default", + "-reftest", + "crashtests.list"] + +class MozillaMochitest(ShellCommandReportTimeout): + name = "mochitest" + warnOnFailure = True + description = ["mochitest"] + descriptionDone = ["mochitest complete"] + command = ["python", + "runtests.py", + "--appname=../../../dist/bin/firefox", + "--autorun", + "--console-level=INFO", + "--close-when-done"] + + def createSummary(self, log): + passCount = 0 + failCount = 0 + todoCount = 0 + for line in log.readlines(): + if "INFO Passed:" in line: + passCount = int(line.split()[-1]) + if "INFO Failed:" in line: + failCount = int(line.split()[-1]) + if "INFO Todo:" in line: + todoCount = int(line.split()[-1]) + summary = "TinderboxPrint: mochitest<br/>" + if not (passCount + failCount + todoCount): + summary += "FAIL\n" + else: + summary += str(passCount) + "/" + str(failCount) + "/" + str(todoCount) + "\n" + self.addCompleteLog('summary', summary) + + def evaluateCommand(self, cmd): + superResult = ShellCommandReportTimeout.evaluateCommand(self, cmd) + if SUCCESS != superResult: + return WARNINGS + if re.search('ERROR FAIL', cmd.logs['stdio'].getText()): + return WARNINGS + if re.search('ERROR TODO WORKED', cmd.logs['stdio'].getText()): + return WARNINGS + if re.search('FAIL Exited', cmd.logs['stdio'].getText()): + return WARNINGS + if not re.search('INFO PASS', cmd.logs['stdio'].getText()): + return WARNINGS + return SUCCESS + +class MozillaOSXMochitest(MozillaMochitest): + command = ["python", + "runtests.py", + "--appname=../../../objdir/dist/Minefield.app/Contents/MacOS/firefox", + "--autorun", + "--console-level=INFO", + "--close-when-done"] + +class MozillaMochichrome(ShellCommandReportTimeout): + name = "mochichrome" + warnOnFailure = True + description = ["mochichrome"] + descriptionDone = ["mochichrome complete"] + command = ["python", + "runtests.py", + "--appname=objdir/dist/bin/firefox", + "--chrome", + "--autorun", + "--console-level=INFO", + "--close-when-done"] + + def createSummary(self, log): + passCount = 0 + failCount = 0 + todoCount = 0 + for line in log.readlines(): + if "INFO Passed:" in line: + passCount = int(line.split()[-1]) + if "INFO Failed:" in line: + failCount = int(line.split()[-1]) + if "INFO Todo:" in line: + todoCount = int(line.split()[-1]) + summary = "TinderboxPrint: chrome<br/>" + if not (passCount + failCount + todoCount): + summary += "FAIL\n" + else: + summary += str(passCount) + "/" + str(failCount) + "/" + str(todoCount) + "\n" + self.addCompleteLog('summary', summary) + + def evaluateCommand(self, cmd): + superResult = ShellCommandReportTimeout.evaluateCommand(self, cmd) + if SUCCESS != superResult: + return WARNINGS + if re.search('ERROR FAIL', cmd.logs['stdio'].getText()): + return WARNINGS + if re.search('FAIL Exited', cmd.logs['stdio'].getText()): + return WARNINGS + if not re.search('INFO PASS', cmd.logs['stdio'].getText()): + return WARNINGS + return SUCCESS + + +class MozillaOSXMochichrome(MozillaMochichrome): + command = ["python", + "runtests.py", + "--appname=../../../objdir/dist/Minefield.app/Contents/MacOS/firefox", + "--chrome", + "--autorun", + "--console-level=INFO", + "--close-when-done"] + +class MozillaBrowserChromeTest(ShellCommandReportTimeout): + name = "browser chrome test" + warnOnFailure = True + description = ["browser chrome test"] + descriptionDone = ["browser chrome test complete"] + command = ["python", + "runtests.py", + "--appname=../../../dist/bin/firefox", + "--autorun", + "--browser-chrome", + "--close-when-done"] + + def createSummary(self, log): + passCount = 0 + failCount = 0 + todoCount = 0 + for line in log.readlines(): + if "Pass:" in line: + passCount = int(line.split()[-1]) + if "Fail:" in line: + failCount = int(line.split()[-1]) + if "Todo:" in line: + todoCount = int(line.split()[-1]) + summary = "TinderboxPrint: browser<br/>" + if not (passCount + failCount + todoCount): + summary += "FAIL\n" + else: + summary += str(passCount) + "/" + str(failCount) + "/" + str(todoCount) + "\n" + self.addCompleteLog('summary', summary) + + def evaluateCommand(self, cmd): + superResult = ShellCommandReportTimeout.evaluateCommand(self, cmd) + if SUCCESS != superResult: + return WARNINGS + if re.search('FAIL -', cmd.logs['stdio'].getText()): + return WARNINGS + if re.search('FAIL Exited', cmd.logs['stdio'].getText()): + return WARNINGS + return SUCCESS + +class MozillaOSXBrowserChromeTest(MozillaBrowserChromeTest): + command = ["python", + "runtests.py", + "--appname=../../../objdir/dist/Minefield.app/Contents/MacOS/firefox", + "--autorun", + "--browser-chrome", + "--close-when-done"] + +class MozillaA11YTest(MozillaMochichrome): + name = "a11y test" + warnOnFailure = True + description = ["a11y test"] + descriptionDone = ["a11y test complete"] + command = ["python", + "runtests.py", + "--appname=objdir/dist/bin/firefox", + "--console-level=INFO", + "--autorun", + "--a11y", + "--close-when-done"] + + def createSummary(self, log): + passCount = 0 + failCount = 0 + todoCount = 0 + for line in log.readlines(): + if "INFO Passed:" in line: + passCount = int(line.split()[-1]) + if "INFO Failed:" in line: + failCount = int(line.split()[-1]) + if "INFO Todo:" in line: + todoCount = int(line.split()[-1]) + summary = "TinderboxPrint: a11y<br/>" + if not (passCount + failCount + todoCount): + summary += "FAIL\n" + else: + summary += str(passCount) + "/" + str(failCount) + "/" + str(todoCount) + "\n" + self.addCompleteLog('summary', summary) + + +class MozillaOSXA11YTest(MozillaA11YTest): + command = ["python", + "runtests.py", + "--appname=../../../dist/Minefield.app/Contents/MacOS/firefox", + "--a11y", + "--autorun", + "--console-level=INFO", + "--close-when-done"] + +class CreateProfile(ShellCommandReportTimeout): + name = "create profile" + warnOnFailure = True + description = ["create profile"] + descriptionDone = ["create profile complete"] + command = r'python testing/tools/profiles/createTestingProfile.py --clobber --binary objdir/dist/bin/firefox' diff -r 9e48e21dd4f6 master.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/master.cfg Sat Feb 07 12:21:02 2009 -0500 @@ -0,0 +1,238 @@ +# -*- python -*- +# ex: set syntax=python: + +# This is a sample buildmaster config file. It must be installed as +# 'master.cfg' in your buildmaster's base directory (although the filename +# can be changed with the --basedir option to 'mktap buildbot master'). + +# It has one job: define a dictionary named BuildmasterConfig. This +# dictionary has a variety of keys to control different aspects of the +# buildmaster. They are documented in docs/config.xhtml . + + +# This is the dictionary that the buildmaster pays attention to. We also use +# a shorter alias to save typing. +c = BuildmasterConfig = {} + +####### BUILDSLAVES + +# the 'slaves' list defines the set of allowable buildslaves. Each element is +# a tuple of bot-name and bot-password. These correspond to values given to +# the buildslave's mktap invocation. +from buildbot.buildslave import BuildSlave +#c['slaves'] = [BuildSlave("p6t-slave", "mozilla"), BuildSlave("macbook-slave", "mozilla"), BuildSlave("neb-slave", "mozilla")] +c['slaves'] = [BuildSlave("local-slave", "mozilla")] + +# to limit to two concurrent builds on a slave, use +# c['slaves'] = [BuildSlave("bot1name", "bot1passwd", max_builds=2)] + + +# 'slavePortnum' defines the TCP port to listen on. This must match the value +# configured into the buildslaves (with their --master option) + +c['slavePortnum'] = 9989 + +####### CHANGESOURCES + +# the 'change_source' setting tells the buildmaster how it should find out +# about source code changes. Any class which implements IChangeSource can be +# put here: there are several in buildbot/changes/*.py to choose from. + +#from buildbot.changes.pb import PBChangeSource +#c['change_source'] = [] +#c['change_source'].append(PBChangeSource()) + +# For example, if you had CVSToys installed on your repository, and your +# CVSROOT/freshcfg file had an entry like this: +#pb = ConfigurationSet([ +# (None, None, None, PBService(userpass=('foo', 'bar'), port=4519)), +# ]) + +# then you could use the following buildmaster Change Source to subscribe to +# the FreshCVS daemon and be notified on every commit: +# +#from buildbot.changes.freshcvs import FreshCVSSource +#fc_source = FreshCVSSource("cvs.example.com", 4519, "foo", "bar") +#c['change_source'] = fc_source + +# or, use a PBChangeSource, and then have your repository's commit script run +# 'buildbot sendchange', or use contrib/svn_buildbot.py, or +# contrib/arch_buildbot.py : +# +#from buildbot.changes.pb import PBChangeSource +#c['change_source'] = PBChangeSource() + + + +####### SCHEDULERS + +## configure the Schedulers + +from buildbot.scheduler import Scheduler +c['schedulers'] = [] +#c['schedulers'].append(Scheduler(name="all", branch=None, +# treeStableTimer=5, +# builderNames=["buildbot-f10", "buildbot-osx", "buildbot-xp"])) +# + +####### BUILDERS + +# the 'builders' list defines the Builders. Each one is configured with a +# dictionary, using the following keys: +# name (required): the name used to describe this bilder +# slavename (required): which slave to use, must appear in c['bots'] +# builddir (required): which subdirectory to run the builder in +# factory (required): a BuildFactory to define how the build is run +# periodicBuildTime (optional): if set, force a build every N seconds + +# buildbot/process/factory.py provides several BuildFactory classes you can +# start with, which implement build processes for common targets (GNU +# autoconf projects, CPAN perl modules, etc). The factory.BuildFactory is the +# base class, and is configured with a series of BuildSteps. When the build +# is run, the appropriate buildslave is told to execute each Step in turn. + +# the first BuildStep is typically responsible for obtaining a copy of the +# sources. There are source-obtaining Steps in buildbot/steps/source.py for +# CVS, SVN, and others. + +#John's Simple Factory +from buildbot.process import factory +from buildbot.steps.shell import ShellCommand, Compile +from buildbot.steps.source import Mercurial +f1 = factory.BuildFactory() +import os +f1.addStep(ShellCommand, name='current date-time', command='~/pytime.py') +f1.addStep(Mercurial, repourl="http://hg.mozilla.org/mozilla-central/", mode='update'); +f1.addStep(ShellCommand, flunkOnFail=False, haltOnFail=False, name='clean-obj', command='rm -rf obj',) +f1.addStep(Compile, name='build', command=['make', '-f', 'client.mk', 'build']) + + +## +## Mac OS X +## +from buildbot.mozbuild import * + +osxFactory = factory.BuildFactory() + +osxFactory.addStep(ShellCommand, name='current date-time', command='~/pytime.py') +osxFactory.addStep(ShellCommand, name="mozconfig contents", + command=["cat","/home/jhford/.mozconfig"]) +osxFactory.addStep(Mercurial, repourl="http://hg.mozilla.org/mozilla-central/", mode='update'); +osxFactory.addStep(ShellCommand, flunkOnFail=False, haltOnFail=False, name='clean-obj', command='rm -rf obj',) +osxFactory.addStep(Compile, name='build', command=['make', '-f', 'client.mk', 'build']) +osxFactory.addStep(ShellCommand, name='check', workdir='build/objdir', command='make -k check') +osxFactory.addStep(CreateProfile, + warnOnWarnings=True, + env=MozillaEnvironments['osx'], + clobber=True) +osxFactory.addStep(MozillaOSXReftest, warnOnWarnings=True, + workdir="build/layout/reftests", + env=MozillaEnvironments['osx']) +osxFactory.addStep(MozillaOSXCrashtest, warnOnWarnings=True, + workdir="build/testing/crashtest", + env=MozillaEnvironments['osx']) +#osxFactory.addStep(MozillaMochitest, warnOnWarnings=True, +# workdir="build/objdir/_tests/testing/mochitest", +# env=MozillaEnvironments['osx']) +#osxFactory.addStep(MozillaMochichrome, warnOnWarnings=True, +# workdir="build/objdir/_tests/testing/mochitest", +# env=MozillaEnvironments['osx']) +#osxFactory.addStep(MozillaBrowserChromeTest, warnOnWarnings=True, +# workdir="build/objdir/_tests/testing/mochitest", +# env=MozillaEnvironments['osx']) + + +b1 = {'name': "local-osx", + 'slavename': "local-slave", + 'builddir': "osx", + 'factory': f1, + } + +mozbuilder = {'name': "mozilla-osx", + 'slavename': "local-slave", + 'builddir': "moz-osx", + 'factory': osxFactory, + } + + +b2 = {'name': "buildbot-f10", + 'slavename': "p6t-slave", + 'builddir': "linux", + 'factory': f1, + } + +#b3 = {'name': "buildbot-xp", +# 'slavename': "neb-slave", +# 'builddir': "xp", +# 'factory': f1, +# } + + +c['builders'] = [] +c['builders'].append(b1) +c['builders'].append(mozbuilder) +#c['builders'].append(b2) +#c['builders'].append(b3) + + +####### STATUS TARGETS + +# 'status' is a list of Status Targets. The results of each build will be +# pushed to these targets. buildbot/status/*.py has a variety to choose from, +# including web pages, email senders, and IRC bots. + +c['status'] = [] + +from buildbot.status import html +c['status'].append(html.WebStatus(http_port=8010, allowForce=True)) + +from buildbot.status import mail +c['status'].append(mail.MailNotifier(fromaddr="buildbot@localhost", + extraRecipients=["buildlogs@johnford.info"], + sendToInterestedUsers=False)) + +from buildbot.status import words +c['status'].append(words.IRC(host="irc.mozilla.org", nick="buildbot-seneca", + channels=["#seneca-build"])) + +from buildbot.status import client +c['status'].append(client.PBListener(9988)) + + +####### DEBUGGING OPTIONS + +# if you set 'debugPassword', then you can connect to the buildmaster with +# the diagnostic tool in contrib/debugclient.py . From this tool, you can +# manually force builds and inject changes, which may be useful for testing +# your buildmaster without actually commiting changes to your repository (or +# before you have a functioning 'sources' set up). The debug tool uses the +# same port number as the slaves do: 'slavePortnum'. + +#c['debugPassword'] = "debugpassword" + +# if you set 'manhole', you can ssh into the buildmaster and get an +# interactive python shell, which may be useful for debugging buildbot +# internals. It is probably only useful for buildbot developers. You can also +# use an authorized_keys file, or plain telnet. +#from buildbot import manhole +#c['manhole'] = manhole.PasswordManhole("tcp:9999:interface=127.0.0.1", +# "admin", "password") + + +####### PROJECT IDENTITY + +# the 'projectName' string will be used to describe the project that this +# buildbot is working on. For example, it is used as the title of the +# waterfall HTML page. The 'projectURL' string will be used to provide a link +# from buildbot HTML pages to your project's home page. + +c['projectName'] = "Mozilla @ Seneca" +c['projectURL'] = "http://zenit.senecac.on.ca/wiki" + +# the 'buildbotURL' string should point to the location where the buildbot's +# internal web server (usually the html.Waterfall page) is visible. This +# typically uses the port number set in the Waterfall 'status' entry, but +# with an externally-visible host name which the buildbot cannot figure out +# without some help. + +c['buildbotURL'] = "http://localhost:8010/"