diff options
Diffstat (limited to 'lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status')
95 files changed, 0 insertions, 17618 deletions
diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/__init__.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/__init__.py deleted file mode 100644 index 080f18f1..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -import build, builder, buildstep, buildset, testresult, logfile -import slave, master, buildrequest - -# styles.Versioned requires this, as it keys the version numbers on the fully -# qualified class name; see master/buildbot/test/regressions/test_unpickling.py -buildstep.BuildStepStatus.__module__ = 'buildbot.status.builder' -build.BuildStatus.__module__ = 'buildbot.status.builder' - -# add all of these classes to builder; this is a form of late binding to allow -# circular module references among the status modules -builder.BuildStepStatus = buildstep.BuildStepStatus -builder.BuildSetStatus = buildset.BuildSetStatus -builder.TestResult = testresult.TestResult -builder.LogFile = logfile.LogFile -builder.HTMLLogFile = logfile.HTMLLogFile -builder.SlaveStatus = slave.SlaveStatus -builder.Status = master.Status -builder.BuildStatus = build.BuildStatus -builder.BuildRequestStatus = buildrequest.BuildRequestStatus diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/base.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/base.py deleted file mode 100644 index 5efaf20f..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/base.py +++ /dev/null @@ -1,104 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -from zope.interface import implements -from twisted.application import service - -from buildbot.interfaces import IStatusReceiver -from buildbot import util, pbutil - -class StatusReceiverBase: - implements(IStatusReceiver) - - def requestSubmitted(self, request): - pass - - def requestCancelled(self, builder, request): - pass - - def buildsetSubmitted(self, buildset): - pass - - def builderAdded(self, builderName, builder): - pass - - def builderChangedState(self, builderName, state): - pass - - def buildStarted(self, builderName, build): - pass - - def buildETAUpdate(self, build, ETA): - pass - - def changeAdded(self, change): - pass - - def stepStarted(self, build, step): - pass - - def stepTextChanged(self, build, step, text): - pass - - def stepText2Changed(self, build, step, text2): - pass - - def stepETAUpdate(self, build, step, ETA, expectations): - pass - - def logStarted(self, build, step, log): - pass - - def logChunk(self, build, step, log, channel, text): - pass - - def logFinished(self, build, step, log): - pass - - def stepFinished(self, build, step, results): - pass - - def buildFinished(self, builderName, build, results): - pass - - def builderRemoved(self, builderName): - pass - - def slaveConnected(self, slaveName): - pass - - def slaveDisconnected(self, slaveName): - pass - - def checkConfig(self, otherStatusReceivers): - pass - -class StatusReceiverMultiService(StatusReceiverBase, service.MultiService, - util.ComparableMixin): - - def __init__(self): - service.MultiService.__init__(self) - -class StatusReceiverService(StatusReceiverBase, service.Service, - util.ComparableMixin): - pass - -StatusReceiver = StatusReceiverService - - -class StatusReceiverPerspective(StatusReceiver, pbutil.NewCredPerspective): - implements(IStatusReceiver) - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/build.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/build.py deleted file mode 100644 index d1efa96d..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/build.py +++ /dev/null @@ -1,479 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -from __future__ import with_statement - -import os, shutil, re -from cPickle import dump -from zope.interface import implements -from twisted.python import log, runtime, components -from twisted.persisted import styles -from twisted.internet import reactor, defer -from buildbot import interfaces, util, sourcestamp -from buildbot.process import properties -from buildbot.status.buildstep import BuildStepStatus - -class BuildStatus(styles.Versioned, properties.PropertiesMixin): - implements(interfaces.IBuildStatus, interfaces.IStatusEvent) - - persistenceVersion = 4 - persistenceForgets = ( 'wasUpgraded', ) - - sources = None - reason = None - changes = [] - blamelist = [] - progress = None - started = None - finished = None - currentStep = None - text = [] - results = None - slavename = "???" - - set_runtime_properties = True - - # these lists/dicts are defined here so that unserialized instances have - # (empty) values. They are set in __init__ to new objects to make sure - # each instance gets its own copy. - watchers = [] - updates = {} - finishedWatchers = [] - testResults = {} - - def __init__(self, parent, master, number): - """ - @type parent: L{BuilderStatus} - @type number: int - """ - assert interfaces.IBuilderStatus(parent) - self.builder = parent - self.master = master - self.number = number - self.watchers = [] - self.updates = {} - self.finishedWatchers = [] - self.steps = [] - self.testResults = {} - self.properties = properties.Properties() - - def __repr__(self): - return "<%s #%s>" % (self.__class__.__name__, self.number) - - # IBuildStatus - - def getBuilder(self): - """ - @rtype: L{BuilderStatus} - """ - return self.builder - - def getNumber(self): - return self.number - - def getPreviousBuild(self): - if self.number == 0: - return None - return self.builder.getBuild(self.number-1) - - def getAllGotRevisions(self): - all_got_revisions = self.properties.getProperty('got_revision', {}) - # For backwards compatibility all_got_revisions is a string if codebases - # are not used. Convert to the default internal type (dict) - if not isinstance(all_got_revisions, dict): - all_got_revisions = {'': all_got_revisions} - return all_got_revisions - - def getSourceStamps(self, absolute=False): - sourcestamps = [] - if not absolute: - sourcestamps.extend(self.sources) - else: - all_got_revisions = self.getAllGotRevisions() or {} - # always make a new instance - for ss in self.sources: - if ss.codebase in all_got_revisions: - got_revision = all_got_revisions[ss.codebase] - sourcestamps.append(ss.getAbsoluteSourceStamp(got_revision)) - else: - # No absolute revision information available - # Probably build has been stopped before ending all sourcesteps - # Return a clone with original revision - sourcestamps.append(ss.clone()) - return sourcestamps - - def getReason(self): - return self.reason - - def getChanges(self): - return self.changes - - def getRevisions(self): - revs = [] - for c in self.changes: - rev = str(c.revision) - if rev > 7: # for long hashes - rev = rev[:7] - revs.append(rev) - return ", ".join(revs) - - def getResponsibleUsers(self): - return self.blamelist - - def getInterestedUsers(self): - # TODO: the Builder should add others: sheriffs, domain-owners - return self.properties.getProperty('owners', []) - - def getSteps(self): - """Return a list of IBuildStepStatus objects. For invariant builds - (those which always use the same set of Steps), this should be the - complete list, however some of the steps may not have started yet - (step.getTimes()[0] will be None). For variant builds, this may not - be complete (asking again later may give you more of them).""" - return self.steps - - def getTimes(self): - return (self.started, self.finished) - - _sentinel = [] # used as a sentinel to indicate unspecified initial_value - def getSummaryStatistic(self, name, summary_fn, initial_value=_sentinel): - """Summarize the named statistic over all steps in which it - exists, using combination_fn and initial_value to combine multiple - results into a single result. This translates to a call to Python's - X{reduce}:: - return reduce(summary_fn, step_stats_list, initial_value) - """ - step_stats_list = [ - st.getStatistic(name) - for st in self.steps - if st.hasStatistic(name) ] - if initial_value is self._sentinel: - return reduce(summary_fn, step_stats_list) - else: - return reduce(summary_fn, step_stats_list, initial_value) - - def isFinished(self): - return (self.finished is not None) - - def waitUntilFinished(self): - if self.finished: - d = defer.succeed(self) - else: - d = defer.Deferred() - self.finishedWatchers.append(d) - return d - - # while the build is running, the following methods make sense. - # Afterwards they return None - - def getETA(self): - if self.finished is not None: - return None - if not self.progress: - return None - eta = self.progress.eta() - if eta is None: - return None - return eta - util.now() - - def getCurrentStep(self): - return self.currentStep - - # Once you know the build has finished, the following methods are legal. - # Before ths build has finished, they all return None. - - def getText(self): - text = [] - text.extend(self.text) - for s in self.steps: - text.extend(s.text2) - return text - - def getResults(self): - return self.results - - def getSlavename(self): - return self.slavename - - def getTestResults(self): - return self.testResults - - def getLogs(self): - logs = [] - for s in self.steps: - for loog in s.getLogs(): - logs.append(loog) - return logs - - # subscription interface - - def subscribe(self, receiver, updateInterval=None): - # will receive stepStarted and stepFinished messages - # and maybe buildETAUpdate - self.watchers.append(receiver) - if updateInterval is not None: - self.sendETAUpdate(receiver, updateInterval) - - def sendETAUpdate(self, receiver, updateInterval): - self.updates[receiver] = None - ETA = self.getETA() - if ETA is not None: - receiver.buildETAUpdate(self, self.getETA()) - # they might have unsubscribed during buildETAUpdate - if receiver in self.watchers: - self.updates[receiver] = reactor.callLater(updateInterval, - self.sendETAUpdate, - receiver, - updateInterval) - - def unsubscribe(self, receiver): - if receiver in self.watchers: - self.watchers.remove(receiver) - if receiver in self.updates: - if self.updates[receiver] is not None: - self.updates[receiver].cancel() - del self.updates[receiver] - - # methods for the base.Build to invoke - - def addStepWithName(self, name): - """The Build is setting up, and has added a new BuildStep to its - list. Create a BuildStepStatus object to which it can send status - updates.""" - - s = BuildStepStatus(self, self.master, len(self.steps)) - s.setName(name) - self.steps.append(s) - return s - - def addTestResult(self, result): - self.testResults[result.getName()] = result - - def setSourceStamps(self, sourceStamps): - self.sources = sourceStamps - self.changes = [] - for source in self.sources: - self.changes.extend(source.changes) - - def setReason(self, reason): - self.reason = reason - def setBlamelist(self, blamelist): - self.blamelist = blamelist - def setProgress(self, progress): - self.progress = progress - - def buildStarted(self, build): - """The Build has been set up and is about to be started. It can now - be safely queried, so it is time to announce the new build.""" - - self.started = util.now() - # now that we're ready to report status, let the BuilderStatus tell - # the world about us - self.builder.buildStarted(self) - - def setSlavename(self, slavename): - self.slavename = slavename - - def setText(self, text): - assert isinstance(text, (list, tuple)) - self.text = text - def setResults(self, results): - self.results = results - - def buildFinished(self): - self.currentStep = None - self.finished = util.now() - - for r in self.updates.keys(): - if self.updates[r] is not None: - self.updates[r].cancel() - del self.updates[r] - - watchers = self.finishedWatchers - self.finishedWatchers = [] - for w in watchers: - w.callback(self) - - # methods called by our BuildStepStatus children - - def stepStarted(self, step): - self.currentStep = step - for w in self.watchers: - receiver = w.stepStarted(self, step) - if receiver: - if type(receiver) == type(()): - step.subscribe(receiver[0], receiver[1]) - else: - step.subscribe(receiver) - d = step.waitUntilFinished() - d.addCallback(lambda step: step.unsubscribe(receiver)) - - step.waitUntilFinished().addCallback(self._stepFinished) - - def _stepFinished(self, step): - results = step.getResults() - for w in self.watchers: - w.stepFinished(self, step, results) - - # methods called by our BuilderStatus parent - - def pruneSteps(self): - # this build is very old: remove the build steps too - self.steps = [] - - # persistence stuff - - def generateLogfileName(self, stepname, logname): - """Return a filename (relative to the Builder's base directory) where - the logfile's contents can be stored uniquely. - - The base filename is made by combining our build number, the Step's - name, and the log's name, then removing unsuitable characters. The - filename is then made unique by appending _0, _1, etc, until it does - not collide with any other logfile. - - These files are kept in the Builder's basedir (rather than a - per-Build subdirectory) because that makes cleanup easier: cron and - find will help get rid of the old logs, but the empty directories are - more of a hassle to remove.""" - - starting_filename = "%d-log-%s-%s" % (self.number, stepname, logname) - starting_filename = re.sub(r'[^\w\.\-]', '_', starting_filename) - # now make it unique - unique_counter = 0 - filename = starting_filename - while filename in [l.filename - for step in self.steps - for l in step.getLogs() - if l.filename]: - filename = "%s_%d" % (starting_filename, unique_counter) - unique_counter += 1 - return filename - - def __getstate__(self): - d = styles.Versioned.__getstate__(self) - # for now, a serialized Build is always "finished". We will never - # save unfinished builds. - if not self.finished: - d['finished'] = util.now() - # TODO: push an "interrupted" step so it is clear that the build - # was interrupted. The builder will have a 'shutdown' event, but - # someone looking at just this build will be confused as to why - # the last log is truncated. - for k in [ 'builder', 'watchers', 'updates', 'finishedWatchers', - 'master' ]: - if k in d: del d[k] - return d - - def __setstate__(self, d): - styles.Versioned.__setstate__(self, d) - self.watchers = [] - self.updates = {} - self.finishedWatchers = [] - - def setProcessObjects(self, builder, master): - self.builder = builder - self.master = master - for step in self.steps: - step.setProcessObjects(self, master) - def upgradeToVersion1(self): - if hasattr(self, "sourceStamp"): - # the old .sourceStamp attribute wasn't actually very useful - maxChangeNumber, patch = self.sourceStamp - changes = getattr(self, 'changes', []) - source = sourcestamp.SourceStamp(branch=None, - revision=None, - patch=patch, - changes=changes) - self.source = source - self.changes = source.changes - del self.sourceStamp - self.wasUpgraded = True - - def upgradeToVersion2(self): - self.properties = {} - self.wasUpgraded = True - - def upgradeToVersion3(self): - # in version 3, self.properties became a Properties object - propdict = self.properties - self.properties = properties.Properties() - self.properties.update(propdict, "Upgrade from previous version") - self.wasUpgraded = True - - def upgradeToVersion4(self): - # buildstatus contains list of sourcestamps, convert single to list - if hasattr(self, "source"): - self.sources = [self.source] - del self.source - self.wasUpgraded = True - - def checkLogfiles(self): - # check that all logfiles exist, and remove references to any that - # have been deleted (e.g., by purge()) - for s in self.steps: - s.checkLogfiles() - - def saveYourself(self): - filename = os.path.join(self.builder.basedir, "%d" % self.number) - if os.path.isdir(filename): - # leftover from 0.5.0, which stored builds in directories - shutil.rmtree(filename, ignore_errors=True) - tmpfilename = filename + ".tmp" - try: - with open(tmpfilename, "wb") as f: - dump(self, f, -1) - if runtime.platformType == 'win32': - # windows cannot rename a file on top of an existing one, so - # fall back to delete-first. There are ways this can fail and - # lose the builder's history, so we avoid using it in the - # general (non-windows) case - if os.path.exists(filename): - os.unlink(filename) - os.rename(tmpfilename, filename) - except: - log.msg("unable to save build %s-#%d" % (self.builder.name, - self.number)) - log.err() - - def asDict(self): - result = {} - # Constant - result['builderName'] = self.builder.name - result['number'] = self.getNumber() - result['sourceStamps'] = [ss.asDict() for ss in self.getSourceStamps()] - result['reason'] = self.getReason() - result['blame'] = self.getResponsibleUsers() - - # Transient - result['properties'] = self.getProperties().asList() - result['times'] = self.getTimes() - result['text'] = self.getText() - result['results'] = self.getResults() - result['slave'] = self.getSlavename() - # TODO(maruel): Add. - #result['test_results'] = self.getTestResults() - result['logs'] = [[l.getName(), - self.builder.status.getURLForThing(l)] for l in self.getLogs()] - result['eta'] = self.getETA() - result['steps'] = [bss.asDict() for bss in self.steps] - if self.getCurrentStep(): - result['currentStep'] = self.getCurrentStep().asDict() - else: - result['currentStep'] = None - return result - -components.registerAdapter(lambda build_status : build_status.properties, - BuildStatus, interfaces.IProperties) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/builder.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/builder.py deleted file mode 100644 index 8430d13e..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/builder.py +++ /dev/null @@ -1,583 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -from __future__ import with_statement - - -import os, re, itertools -from cPickle import load, dump - -from zope.interface import implements -from twisted.python import log, runtime -from twisted.persisted import styles -from buildbot import interfaces, util -from buildbot.util.lru import LRUCache -from buildbot.status.event import Event -from buildbot.status.build import BuildStatus -from buildbot.status.buildrequest import BuildRequestStatus - -# user modules expect these symbols to be present here -from buildbot.status.results import SUCCESS, WARNINGS, FAILURE, SKIPPED -from buildbot.status.results import EXCEPTION, RETRY, Results, worst_status -_hush_pyflakes = [ SUCCESS, WARNINGS, FAILURE, SKIPPED, - EXCEPTION, RETRY, Results, worst_status ] - -class BuilderStatus(styles.Versioned): - """I handle status information for a single process.build.Builder object. - That object sends status changes to me (frequently as Events), and I - provide them on demand to the various status recipients, like the HTML - waterfall display and the live status clients. It also sends build - summaries to me, which I log and provide to status clients who aren't - interested in seeing details of the individual build steps. - - I am responsible for maintaining the list of historic Events and Builds, - pruning old ones, and loading them from / saving them to disk. - - I live in the buildbot.process.build.Builder object, in the - .builder_status attribute. - - @type category: string - @ivar category: user-defined category this builder belongs to; can be - used to filter on in status clients - """ - - implements(interfaces.IBuilderStatus, interfaces.IEventSource) - - persistenceVersion = 1 - persistenceForgets = ( 'wasUpgraded', ) - - category = None - currentBigState = "offline" # or idle/waiting/interlocked/building - basedir = None # filled in by our parent - - def __init__(self, buildername, category, master, description): - self.name = buildername - self.category = category - self.description = description - self.master = master - - self.slavenames = [] - self.events = [] - # these three hold Events, and are used to retrieve the current - # state of the boxes. - self.lastBuildStatus = None - #self.currentBig = None - #self.currentSmall = None - self.currentBuilds = [] - self.nextBuild = None - self.watchers = [] - self.buildCache = LRUCache(self.cacheMiss) - - # persistence - - def __getstate__(self): - # when saving, don't record transient stuff like what builds are - # currently running, because they won't be there when we start back - # up. Nor do we save self.watchers, nor anything that gets set by our - # parent like .basedir and .status - d = styles.Versioned.__getstate__(self) - d['watchers'] = [] - del d['buildCache'] - for b in self.currentBuilds: - b.saveYourself() - # TODO: push a 'hey, build was interrupted' event - del d['currentBuilds'] - d.pop('pendingBuilds', None) - del d['currentBigState'] - del d['basedir'] - del d['status'] - del d['nextBuildNumber'] - del d['master'] - return d - - def __setstate__(self, d): - # when loading, re-initialize the transient stuff. Remember that - # upgradeToVersion1 and such will be called after this finishes. - styles.Versioned.__setstate__(self, d) - self.buildCache = LRUCache(self.cacheMiss) - self.currentBuilds = [] - self.watchers = [] - self.slavenames = [] - # self.basedir must be filled in by our parent - # self.status must be filled in by our parent - # self.master must be filled in by our parent - - def upgradeToVersion1(self): - if hasattr(self, 'slavename'): - self.slavenames = [self.slavename] - del self.slavename - if hasattr(self, 'nextBuildNumber'): - del self.nextBuildNumber # determineNextBuildNumber chooses this - self.wasUpgraded = True - - def determineNextBuildNumber(self): - """Scan our directory of saved BuildStatus instances to determine - what our self.nextBuildNumber should be. Set it one larger than the - highest-numbered build we discover. This is called by the top-level - Status object shortly after we are created or loaded from disk. - """ - existing_builds = [int(f) - for f in os.listdir(self.basedir) - if re.match("^\d+$", f)] - if existing_builds: - self.nextBuildNumber = max(existing_builds) + 1 - else: - self.nextBuildNumber = 0 - - def saveYourself(self): - for b in self.currentBuilds: - if not b.isFinished: - # interrupted build, need to save it anyway. - # BuildStatus.saveYourself will mark it as interrupted. - b.saveYourself() - filename = os.path.join(self.basedir, "builder") - tmpfilename = filename + ".tmp" - try: - with open(tmpfilename, "wb") as f: - dump(self, f, -1) - if runtime.platformType == 'win32': - # windows cannot rename a file on top of an existing one - if os.path.exists(filename): - os.unlink(filename) - os.rename(tmpfilename, filename) - except: - log.msg("unable to save builder %s" % self.name) - log.err() - - # build cache management - - def setCacheSize(self, size): - self.buildCache.set_max_size(size) - - def makeBuildFilename(self, number): - return os.path.join(self.basedir, "%d" % number) - - def getBuildByNumber(self, number): - return self.buildCache.get(number) - - def loadBuildFromFile(self, number): - filename = self.makeBuildFilename(number) - try: - log.msg("Loading builder %s's build %d from on-disk pickle" - % (self.name, number)) - with open(filename, "rb") as f: - build = load(f) - build.setProcessObjects(self, self.master) - - # (bug #1068) if we need to upgrade, we probably need to rewrite - # this pickle, too. We determine this by looking at the list of - # Versioned objects that have been unpickled, and (after doUpgrade) - # checking to see if any of them set wasUpgraded. The Versioneds' - # upgradeToVersionNN methods all set this. - versioneds = styles.versionedsToUpgrade - styles.doUpgrade() - if True in [ hasattr(o, 'wasUpgraded') for o in versioneds.values() ]: - log.msg("re-writing upgraded build pickle") - build.saveYourself() - - # check that logfiles exist - build.checkLogfiles() - return build - except IOError: - raise IndexError("no such build %d" % number) - except EOFError: - raise IndexError("corrupted build pickle %d" % number) - - def cacheMiss(self, number, **kwargs): - # If kwargs['val'] exists, this is a new value being added to - # the cache. Just return it. - if 'val' in kwargs: - return kwargs['val'] - - # first look in currentBuilds - for b in self.currentBuilds: - if b.number == number: - return b - - # then fall back to loading it from disk - return self.loadBuildFromFile(number) - - def prune(self, events_only=False): - # begin by pruning our own events - eventHorizon = self.master.config.eventHorizon - self.events = self.events[-eventHorizon:] - - if events_only: - return - - # get the horizons straight - buildHorizon = self.master.config.buildHorizon - if buildHorizon is not None: - earliest_build = self.nextBuildNumber - buildHorizon - else: - earliest_build = 0 - - logHorizon = self.master.config.logHorizon - if logHorizon is not None: - earliest_log = self.nextBuildNumber - logHorizon - else: - earliest_log = 0 - - if earliest_log < earliest_build: - earliest_log = earliest_build - - if earliest_build == 0: - return - - # skim the directory and delete anything that shouldn't be there anymore - build_re = re.compile(r"^([0-9]+)$") - build_log_re = re.compile(r"^([0-9]+)-.*$") - # if the directory doesn't exist, bail out here - if not os.path.exists(self.basedir): - return - - for filename in os.listdir(self.basedir): - num = None - mo = build_re.match(filename) - is_logfile = False - if mo: - num = int(mo.group(1)) - else: - mo = build_log_re.match(filename) - if mo: - num = int(mo.group(1)) - is_logfile = True - - if num is None: continue - if num in self.buildCache.cache: continue - - if (is_logfile and num < earliest_log) or num < earliest_build: - pathname = os.path.join(self.basedir, filename) - log.msg("pruning '%s'" % pathname) - try: os.unlink(pathname) - except OSError: pass - - # IBuilderStatus methods - def getName(self): - # if builderstatus page does show not up without any reason then - # str(self.name) may be a workaround - return self.name - - def setDescription(self, description): - # used during reconfig - self.description = description - - def getDescription(self): - return self.description - - def getState(self): - return (self.currentBigState, self.currentBuilds) - - def getSlaves(self): - return [self.status.getSlave(name) for name in self.slavenames] - - def getPendingBuildRequestStatuses(self): - db = self.status.master.db - d = db.buildrequests.getBuildRequests(claimed=False, - buildername=self.name) - def make_statuses(brdicts): - return [BuildRequestStatus(self.name, brdict['brid'], - self.status) - for brdict in brdicts] - d.addCallback(make_statuses) - return d - - def getCurrentBuilds(self): - return self.currentBuilds - - def getLastFinishedBuild(self): - b = self.getBuild(-1) - if not (b and b.isFinished()): - b = self.getBuild(-2) - return b - - def setCategory(self, category): - # used during reconfig - self.category = category - - def getCategory(self): - return self.category - - def getBuild(self, number): - if number < 0: - number = self.nextBuildNumber + number - if number < 0 or number >= self.nextBuildNumber: - return None - - try: - return self.getBuildByNumber(number) - except IndexError: - return None - - def getEvent(self, number): - try: - return self.events[number] - except IndexError: - return None - - def _getBuildBranches(self, build): - return set([ ss.branch - for ss in build.getSourceStamps() ]) - - def generateFinishedBuilds(self, branches=[], - num_builds=None, - max_buildnum=None, - finished_before=None, - results=None, - max_search=200): - got = 0 - branches = set(branches) - for Nb in itertools.count(1): - if Nb > self.nextBuildNumber: - break - if Nb > max_search: - break - build = self.getBuild(-Nb) - if build is None: - continue - if max_buildnum is not None: - if build.getNumber() > max_buildnum: - continue - if not build.isFinished(): - continue - if finished_before is not None: - start, end = build.getTimes() - if end >= finished_before: - continue - # if we were asked to filter on branches, and none of the - # sourcestamps match, skip this build - if branches and not branches & self._getBuildBranches(build): - continue - if results is not None: - if build.getResults() not in results: - continue - got += 1 - yield build - if num_builds is not None: - if got >= num_builds: - return - - def eventGenerator(self, branches=[], categories=[], committers=[], minTime=0): - """This function creates a generator which will provide all of this - Builder's status events, starting with the most recent and - progressing backwards in time. """ - - # remember the oldest-to-earliest flow here. "next" means earlier. - - # TODO: interleave build steps and self.events by timestamp. - # TODO: um, I think we're already doing that. - - # TODO: there's probably something clever we could do here to - # interleave two event streams (one from self.getBuild and the other - # from self.getEvent), which would be simpler than this control flow - - eventIndex = -1 - e = self.getEvent(eventIndex) - branches = set(branches) - for Nb in range(1, self.nextBuildNumber+1): - b = self.getBuild(-Nb) - if not b: - # HACK: If this is the first build we are looking at, it is - # possible it's in progress but locked before it has written a - # pickle; in this case keep looking. - if Nb == 1: - continue - break - if b.getTimes()[0] < minTime: - break - # if we were asked to filter on branches, and none of the - # sourcestamps match, skip this build - if branches and not branches & self._getBuildBranches(b): - continue - if categories and not b.getBuilder().getCategory() in categories: - continue - if committers and not [True for c in b.getChanges() if c.who in committers]: - continue - steps = b.getSteps() - for Ns in range(1, len(steps)+1): - if steps[-Ns].started: - step_start = steps[-Ns].getTimes()[0] - while e is not None and e.getTimes()[0] > step_start: - yield e - eventIndex -= 1 - e = self.getEvent(eventIndex) - yield steps[-Ns] - yield b - while e is not None: - yield e - eventIndex -= 1 - e = self.getEvent(eventIndex) - if e and e.getTimes()[0] < minTime: - break - - def subscribe(self, receiver): - # will get builderChangedState, buildStarted, buildFinished, - # requestSubmitted, requestCancelled. Note that a request which is - # resubmitted (due to a slave disconnect) will cause requestSubmitted - # to be invoked multiple times. - self.watchers.append(receiver) - self.publishState(receiver) - # our parent Status provides requestSubmitted and requestCancelled - self.status._builder_subscribe(self.name, receiver) - - def unsubscribe(self, receiver): - self.watchers.remove(receiver) - self.status._builder_unsubscribe(self.name, receiver) - - ## Builder interface (methods called by the Builder which feeds us) - - def setSlavenames(self, names): - self.slavenames = names - - def addEvent(self, text=[]): - # this adds a duration event. When it is done, the user should call - # e.finish(). They can also mangle it by modifying .text - e = Event() - e.started = util.now() - e.text = text - self.events.append(e) - self.prune(events_only=True) - return e # they are free to mangle it further - - def addPointEvent(self, text=[]): - # this adds a point event, one which occurs as a single atomic - # instant of time. - e = Event() - e.started = util.now() - e.finished = 0 - e.text = text - self.events.append(e) - self.prune(events_only=True) - return e # for consistency, but they really shouldn't touch it - - def setBigState(self, state): - needToUpdate = state != self.currentBigState - self.currentBigState = state - if needToUpdate: - self.publishState() - - def publishState(self, target=None): - state = self.currentBigState - - if target is not None: - # unicast - target.builderChangedState(self.name, state) - return - for w in self.watchers: - try: - w.builderChangedState(self.name, state) - except: - log.msg("Exception caught publishing state to %r" % w) - log.err() - - def newBuild(self): - """The Builder has decided to start a build, but the Build object is - not yet ready to report status (it has not finished creating the - Steps). Create a BuildStatus object that it can use.""" - number = self.nextBuildNumber - self.nextBuildNumber += 1 - # TODO: self.saveYourself(), to make sure we don't forget about the - # build number we've just allocated. This is not quite as important - # as it was before we switch to determineNextBuildNumber, but I think - # it may still be useful to have the new build save itself. - s = BuildStatus(self, self.master, number) - s.waitUntilFinished().addCallback(self._buildFinished) - return s - - # buildStarted is called by our child BuildStatus instances - def buildStarted(self, s): - """Now the BuildStatus object is ready to go (it knows all of its - Steps, its ETA, etc), so it is safe to notify our watchers.""" - - assert s.builder is self # paranoia - assert s not in self.currentBuilds - self.currentBuilds.append(s) - self.buildCache.get(s.number, val=s) - - # now that the BuildStatus is prepared to answer queries, we can - # announce the new build to all our watchers - - for w in self.watchers: # TODO: maybe do this later? callLater(0)? - try: - receiver = w.buildStarted(self.getName(), s) - if receiver: - if type(receiver) == type(()): - s.subscribe(receiver[0], receiver[1]) - else: - s.subscribe(receiver) - d = s.waitUntilFinished() - d.addCallback(lambda s: s.unsubscribe(receiver)) - except: - log.msg("Exception caught notifying %r of buildStarted event" % w) - log.err() - - def _buildFinished(self, s): - assert s in self.currentBuilds - s.saveYourself() - self.currentBuilds.remove(s) - - name = self.getName() - results = s.getResults() - for w in self.watchers: - try: - w.buildFinished(name, s, results) - except: - log.msg("Exception caught notifying %r of buildFinished event" % w) - log.err() - - self.prune() # conserve disk - - - def asDict(self): - result = {} - # Constant - # TODO(maruel): Fix me. We don't want to leak the full path. - result['basedir'] = os.path.basename(self.basedir) - result['category'] = self.category - result['slaves'] = self.slavenames - result['schedulers'] = [ s.name - for s in self.status.master.allSchedulers() - if self.name in s.builderNames ] - #result['url'] = self.parent.getURLForThing(self) - # TODO(maruel): Add cache settings? Do we care? - - # Transient - # Collect build numbers. - # Important: Only grab the *cached* builds numbers to reduce I/O. - current_builds = [b.getNumber() for b in self.currentBuilds] - cached_builds = list(set(self.buildCache.keys() + current_builds)) - cached_builds.sort() - result['cachedBuilds'] = cached_builds - result['currentBuilds'] = current_builds - result['state'] = self.getState()[0] - # lies, but we don't have synchronous access to this info; use - # asDict_async instead - result['pendingBuilds'] = 0 - return result - - def asDict_async(self): - """Just like L{asDict}, but with a nonzero pendingBuilds.""" - result = self.asDict() - d = self.getPendingBuildRequestStatuses() - def combine(statuses): - result['pendingBuilds'] = len(statuses) - return result - d.addCallback(combine) - return d - - def getMetrics(self): - return self.botmaster.parent.metrics - -# vim: set ts=4 sts=4 sw=4 et: diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/buildrequest.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/buildrequest.py deleted file mode 100644 index 1f52902e..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/buildrequest.py +++ /dev/null @@ -1,149 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -from zope.interface import implements -from twisted.python import log -from twisted.internet import defer -from buildbot import interfaces -from buildbot.util.eventual import eventually - -class BuildRequestStatus: - implements(interfaces.IBuildRequestStatus) - - def __init__(self, buildername, brid, status): - self.buildername = buildername - self.brid = brid - self.status = status - self.master = status.master - - self._buildrequest = None - self._buildrequest_lock = defer.DeferredLock() - - @defer.inlineCallbacks - def _getBuildRequest(self): - """ - Get the underlying BuildRequest object for this status. This is a slow - operation! - - @returns: BuildRequest instance or None, via Deferred - """ - # late binding to avoid an import cycle - from buildbot.process import buildrequest - - # this is only set once, so no need to lock if we already have it - if self._buildrequest: - defer.returnValue(self._buildrequest) - return - - yield self._buildrequest_lock.acquire() - - try: - if not self._buildrequest: - brd = yield self.master.db.buildrequests.getBuildRequest( - self.brid) - - br = yield buildrequest.BuildRequest.fromBrdict(self.master, - brd) - self._buildrequest = br - except: # try/finally isn't allowed in generators in older Pythons - self._buildrequest_lock.release() - raise - - self._buildrequest_lock.release() - - defer.returnValue(self._buildrequest) - - def buildStarted(self, build): - self.status._buildrequest_buildStarted(build.status) - self.builds.append(build.status) - - # methods called by our clients - @defer.inlineCallbacks - def getBsid(self): - br = yield self._getBuildRequest() - defer.returnValue(br.bsid) - - @defer.inlineCallbacks - def getBuildProperties(self): - br = yield self._getBuildRequest() - defer.returnValue(br.properties) - - @defer.inlineCallbacks - def getSourceStamp(self): - br = yield self._getBuildRequest() - defer.returnValue(br.source) - - def getBuilderName(self): - return self.buildername - - @defer.inlineCallbacks - def getBuilds(self): - builder = self.status.getBuilder(self.getBuilderName()) - builds = [] - - bdicts = yield self.master.db.builds.getBuildsForRequest(self.brid) - - buildnums = sorted([ bdict['number'] for bdict in bdicts ]) - - for buildnum in buildnums: - bs = builder.getBuild(buildnum) - if bs: - builds.append(bs) - defer.returnValue(builds) - - def subscribe(self, observer): - d = self.getBuilds() - def notify_old(oldbuilds): - for bs in oldbuilds: - eventually(observer, bs) - d.addCallback(notify_old) - d.addCallback(lambda _ : - self.status._buildrequest_subscribe(self.brid, observer)) - d.addErrback(log.err, 'while notifying subscribers') - - def unsubscribe(self, observer): - self.status._buildrequest_unsubscribe(self.brid, observer) - - @defer.inlineCallbacks - def getSubmitTime(self): - br = yield self._getBuildRequest() - defer.returnValue(br.submittedAt) - - def asDict(self): - result = {} - # Constant - result['source'] = None # not available sync, sorry - result['builderName'] = self.buildername - result['submittedAt'] = None # not availably sync, sorry - - # Transient - result['builds'] = [] # not available async, sorry - return result - - @defer.inlineCallbacks - def asDict_async(self): - result = {} - - ss = yield self.getSourceStamp() - result['source'] = ss.asDict() - props = yield self.getBuildProperties() - result['properties'] = props.asList() - result['builderName'] = self.getBuilderName() - result['submittedAt'] = yield self.getSubmitTime() - - builds = yield self.getBuilds() - result['builds'] = [ build.asDict() for build in builds ] - - defer.returnValue(result) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/buildset.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/buildset.py deleted file mode 100644 index 524473a7..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/buildset.py +++ /dev/null @@ -1,68 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -from zope.interface import implements -from buildbot import interfaces -from buildbot.status.buildrequest import BuildRequestStatus - -class BuildSetStatus: - implements(interfaces.IBuildSetStatus) - - def __init__(self, bsdict, status): - self.id = bsdict['bsid'] - self.bsdict = bsdict - self.status = status - self.master = status.master - - # methods for our clients - - def getReason(self): - return self.bsdict['reason'] - - def getResults(self): - return self.bsdict['results'] - - def getID(self): - return self.bsdict['external_idstring'] - - def isFinished(self): - return self.bsdict['complete'] - - def getBuilderNamesAndBuildRequests(self): - # returns a Deferred; undocumented method that may be removed - # without warning - d = self.master.db.buildrequests.getBuildRequests(bsid=self.id) - def get_objects(brdicts): - return dict([ - (brd['buildername'], BuildRequestStatus(brd['buildername'], - brd['brid'], self.status)) - for brd in brdicts ]) - d.addCallback(get_objects) - return d - - def getBuilderNames(self): - d = self.master.db.buildrequests.getBuildRequests(bsid=self.id) - def get_names(brdicts): - return sorted([ brd['buildername'] for brd in brdicts ]) - d.addCallback(get_names) - return d - - def waitUntilFinished(self): - return self.status._buildset_waitUntilFinished(self.id) - - def asDict(self): - d = dict(self.bsdict) - d["submitted_at"] = str(self.bsdict["submitted_at"]) - return d diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/buildstep.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/buildstep.py deleted file mode 100644 index 8e453873..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/buildstep.py +++ /dev/null @@ -1,386 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -import os -from zope.interface import implements -from twisted.persisted import styles -from twisted.python import log -from twisted.internet import reactor, defer -from buildbot import interfaces, util -from buildbot.status.logfile import LogFile, HTMLLogFile - -class BuildStepStatus(styles.Versioned): - """ - I represent a collection of output status for a - L{buildbot.process.step.BuildStep}. - - Statistics contain any information gleaned from a step that is - not in the form of a logfile. As an example, steps that run - tests might gather statistics about the number of passed, failed, - or skipped tests. - - @type progress: L{buildbot.status.progress.StepProgress} - @cvar progress: tracks ETA for the step - @type text: list of strings - @cvar text: list of short texts that describe the command and its status - @type text2: list of strings - @cvar text2: list of short texts added to the overall build description - @type logs: dict of string -> L{buildbot.status.logfile.LogFile} - @ivar logs: logs of steps - @type statistics: dict - @ivar statistics: results from running this step - """ - # note that these are created when the Build is set up, before each - # corresponding BuildStep has started. - implements(interfaces.IBuildStepStatus, interfaces.IStatusEvent) - - persistenceVersion = 4 - persistenceForgets = ( 'wasUpgraded', ) - - started = None - finished = None - progress = None - text = [] - results = None - text2 = [] - watchers = [] - updates = {} - finishedWatchers = [] - statistics = {} - step_number = None - hidden = False - - def __init__(self, parent, master, step_number): - assert interfaces.IBuildStatus(parent) - self.build = parent - self.step_number = step_number - self.hidden = False - self.logs = [] - self.urls = {} - self.watchers = [] - self.updates = {} - self.finishedWatchers = [] - self.statistics = {} - self.skipped = False - - self.master = master - - self.waitingForLocks = False - - def getName(self): - """Returns a short string with the name of this step. This string - may have spaces in it.""" - return self.name - - def getBuild(self): - return self.build - - def getTimes(self): - return (self.started, self.finished) - - def getExpectations(self): - """Returns a list of tuples (name, current, target).""" - if not self.progress: - return [] - ret = [] - metrics = self.progress.progress.keys() - metrics.sort() - for m in metrics: - t = (m, self.progress.progress[m], self.progress.expectations[m]) - ret.append(t) - return ret - - def getLogs(self): - return self.logs - - def getURLs(self): - return self.urls.copy() - - def isStarted(self): - return (self.started is not None) - - def isSkipped(self): - return self.skipped - - def isFinished(self): - return (self.finished is not None) - - def isHidden(self): - return self.hidden - - def waitUntilFinished(self): - if self.finished: - d = defer.succeed(self) - else: - d = defer.Deferred() - self.finishedWatchers.append(d) - return d - - # while the step is running, the following methods make sense. - # Afterwards they return None - - def getETA(self): - if self.started is None: - return None # not started yet - if self.finished is not None: - return None # already finished - if not self.progress: - return None # no way to predict - return self.progress.remaining() - - # Once you know the step has finished, the following methods are legal. - # Before this step has finished, they all return None. - - def getText(self): - """Returns a list of strings which describe the step. These are - intended to be displayed in a narrow column. If more space is - available, the caller should join them together with spaces before - presenting them to the user.""" - return self.text - - def getResults(self): - """Return a tuple describing the results of the step. - 'result' is one of the constants in L{buildbot.status.builder}: - SUCCESS, WARNINGS, FAILURE, or SKIPPED. - 'strings' is an optional list of strings that the step wants to - append to the overall build's results. These strings are usually - more terse than the ones returned by getText(): in particular, - successful Steps do not usually contribute any text to the - overall build. - - @rtype: tuple of int, list of strings - @returns: (result, strings) - """ - return (self.results, self.text2) - - def hasStatistic(self, name): - """Return true if this step has a value for the given statistic. - """ - return self.statistics.has_key(name) - - def getStatistic(self, name, default=None): - """Return the given statistic, if present - """ - return self.statistics.get(name, default) - - def getStatistics(self): - return self.statistics.copy() - - # subscription interface - - def subscribe(self, receiver, updateInterval=10): - # will get logStarted, logFinished, stepETAUpdate - assert receiver not in self.watchers - self.watchers.append(receiver) - self.sendETAUpdate(receiver, updateInterval) - - def sendETAUpdate(self, receiver, updateInterval): - self.updates[receiver] = None - # they might unsubscribe during stepETAUpdate - receiver.stepETAUpdate(self.build, self, - self.getETA(), self.getExpectations()) - if receiver in self.watchers: - self.updates[receiver] = reactor.callLater(updateInterval, - self.sendETAUpdate, - receiver, - updateInterval) - - def unsubscribe(self, receiver): - if receiver in self.watchers: - self.watchers.remove(receiver) - if receiver in self.updates: - if self.updates[receiver] is not None: - self.updates[receiver].cancel() - del self.updates[receiver] - - - # methods to be invoked by the BuildStep - - def setName(self, stepname): - self.name = stepname - - def setColor(self, color): - log.msg("BuildStepStatus.setColor is no longer supported -- ignoring color %s" % (color,)) - - def setProgress(self, stepprogress): - self.progress = stepprogress - - def setHidden(self, hidden): - self.hidden = hidden - - def stepStarted(self): - self.started = util.now() - if self.build: - self.build.stepStarted(self) - - def addLog(self, name): - assert self.started # addLog before stepStarted won't notify watchers - logfilename = self.build.generateLogfileName(self.name, name) - log = LogFile(self, name, logfilename) - self.logs.append(log) - for w in self.watchers: - receiver = w.logStarted(self.build, self, log) - if receiver: - log.subscribe(receiver, True) - d = log.waitUntilFinished() - d.addCallback(lambda log: log.unsubscribe(receiver)) - d = log.waitUntilFinished() - d.addCallback(self.logFinished) - return log - - def addHTMLLog(self, name, html): - assert self.started # addLog before stepStarted won't notify watchers - logfilename = self.build.generateLogfileName(self.name, name) - log = HTMLLogFile(self, name, logfilename, html) - self.logs.append(log) - for w in self.watchers: - w.logStarted(self.build, self, log) - w.logFinished(self.build, self, log) - - def logFinished(self, log): - for w in self.watchers: - w.logFinished(self.build, self, log) - - def addURL(self, name, url): - self.urls[name] = url - - def setText(self, text): - self.text = text - for w in self.watchers: - w.stepTextChanged(self.build, self, text) - def setText2(self, text): - self.text2 = text - for w in self.watchers: - w.stepText2Changed(self.build, self, text) - - def setStatistic(self, name, value): - """Set the given statistic. Usually called by subclasses. - """ - self.statistics[name] = value - - def setSkipped(self, skipped): - self.skipped = skipped - - def stepFinished(self, results): - self.finished = util.now() - self.results = results - cld = [] # deferreds for log compression - logCompressionLimit = self.master.config.logCompressionLimit - for loog in self.logs: - if not loog.isFinished(): - loog.finish() - # if log compression is on, and it's a real LogFile, - # HTMLLogFiles aren't files - if logCompressionLimit is not False and \ - isinstance(loog, LogFile): - if os.path.getsize(loog.getFilename()) > logCompressionLimit: - loog_deferred = loog.compressLog() - if loog_deferred: - cld.append(loog_deferred) - - for r in self.updates.keys(): - if self.updates[r] is not None: - self.updates[r].cancel() - del self.updates[r] - - watchers = self.finishedWatchers - self.finishedWatchers = [] - for w in watchers: - w.callback(self) - if cld: - return defer.DeferredList(cld) - - def checkLogfiles(self): - # filter out logs that have been deleted - self.logs = [ l for l in self.logs if l.hasContents() ] - - def isWaitingForLocks(self): - return self.waitingForLocks - - def setWaitingForLocks(self, waiting): - self.waitingForLocks = waiting - - # persistence - - def __getstate__(self): - d = styles.Versioned.__getstate__(self) - del d['build'] # filled in when loading - if d.has_key('progress'): - del d['progress'] - del d['watchers'] - del d['finishedWatchers'] - del d['updates'] - del d['master'] - return d - - def __setstate__(self, d): - styles.Versioned.__setstate__(self, d) - # self.build must be filled in by our parent - - # point the logs to this object - self.watchers = [] - self.finishedWatchers = [] - self.updates = {} - - def setProcessObjects(self, build, master): - self.build = build - self.master = master - for loog in self.logs: - loog.step = self - loog.master = master - - def upgradeToVersion1(self): - if not hasattr(self, "urls"): - self.urls = {} - self.wasUpgraded = True - - def upgradeToVersion2(self): - if not hasattr(self, "statistics"): - self.statistics = {} - self.wasUpgraded = True - - def upgradeToVersion3(self): - if not hasattr(self, "step_number"): - self.step_number = 0 - self.wasUpgraded = True - - def upgradeToVersion4(self): - if not hasattr(self, "hidden"): - self.hidden = False - self.wasUpgraded = True - - def asDict(self): - result = {} - # Constant - result['name'] = self.getName() - - # Transient - result['text'] = self.getText() - result['results'] = self.getResults() - result['isStarted'] = self.isStarted() - result['isFinished'] = self.isFinished() - result['statistics'] = self.statistics - result['times'] = self.getTimes() - result['expectations'] = self.getExpectations() - result['eta'] = self.getETA() - result['urls'] = self.getURLs() - result['step_number'] = self.step_number - result['hidden'] = self.hidden - result['logs'] = [[l.getName(), - self.build.builder.status.getURLForThing(l)] - for l in self.getLogs()] - return result - - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/client.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/client.py deleted file mode 100644 index 39adcec5..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/client.py +++ /dev/null @@ -1,586 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -from twisted.spread import pb -from twisted.python import components, log as twlog -from twisted.application import strports -from twisted.cred import portal, checkers - -from buildbot import interfaces -from zope.interface import Interface, implements -from buildbot.status import logfile, base -from buildbot.changes import changes -from buildbot.util.eventual import eventually - -class IRemote(Interface): - pass - -def makeRemote(obj): - # we want IRemote(None) to be None, but you can't really do that with - # adapters, so we fake it - if obj is None: - return None - return IRemote(obj) - - -class RemoteBuildSet(pb.Referenceable): - def __init__(self, buildset): - self.b = buildset - - def remote_getSourceStamp(self): - return self.b.getSourceStamp() - - def remote_getReason(self): - return self.b.getReason() - - def remote_getID(self): - return self.b.getID() - - def remote_getBuilderNames(self): - return self.b.getBuilderNames() # note: passes along the Deferred - - def remote_getBuildRequests(self): - """Returns a list of (builderName, BuildRequest) tuples.""" - d = self.b.getBuilderNamesAndBuildRequests() - def add_remote(buildrequests): - for k,v in buildrequests.iteritems(): - buildrequests[k] = IRemote(v) - return buildrequests.items() - d.addCallback(add_remote) - return d - - def remote_isFinished(self): - return self.b.isFinished() - - def remote_waitUntilFinished(self): - d = self.b.waitUntilFinished() - d.addCallback(makeRemote) - return d - - def remote_getResults(self): - return self.b.getResults() - -components.registerAdapter(RemoteBuildSet, - interfaces.IBuildSetStatus, IRemote) - - -class RemoteBuilder(pb.Referenceable): - def __init__(self, builder): - self.b = builder - - def remote_getName(self): - return self.b.getName() - - def remote_getCategory(self): - return self.b.getCategory() - - def remote_getState(self): - state, builds = self.b.getState() - return (state, - None, # TODO: remove leftover ETA - [makeRemote(b) for b in builds]) - - def remote_getSlaves(self): - return [IRemote(s) for s in self.b.getSlaves()] - - def remote_getLastFinishedBuild(self): - return makeRemote(self.b.getLastFinishedBuild()) - - def remote_getCurrentBuilds(self): - return [IRemote(b) for b in self.b.getCurrentBuilds()] - - def remote_getBuild(self, number): - return makeRemote(self.b.getBuild(number)) - - def remote_getEvent(self, number): - return IRemote(self.b.getEvent(number)) - -components.registerAdapter(RemoteBuilder, - interfaces.IBuilderStatus, IRemote) - - -class RemoteBuildRequest(pb.Referenceable): - def __init__(self, buildreq): - self.b = buildreq - # mapping of observers (RemoteReference instances) to local callable - # objects that have been passed to BuildRequestStatus.subscribe - self.observers = [] - - def remote_getSourceStamp(self): - # note that this now returns a Deferred - return self.b.getSourceStamp() - - def remote_getBuilderName(self): - return self.b.getBuilderName() - - def remote_subscribe(self, observer): - """The observer's remote_newbuild method will be called (with two - arguments: the RemoteBuild object, and our builderName) for each new - Build that is created to handle this BuildRequest.""" - def send(bs): - d = observer.callRemote("newbuild", - IRemote(bs), self.b.getBuilderName()) - d.addErrback(twlog.err, - "while calling client-side remote_newbuild") - self.observers.append((observer, send)) - self.b.subscribe(send) - - def remote_unsubscribe(self, observer): - for i, (obs, send) in enumerate(self.observers): - if obs == observer: - del self.observers[i] - self.b.unsubscribe(send) - break - -components.registerAdapter(RemoteBuildRequest, - interfaces.IBuildRequestStatus, IRemote) - -class RemoteBuild(pb.Referenceable): - def __init__(self, build): - self.b = build - self.observers = [] - - def remote_getBuilderName(self): - return self.b.getBuilder().getName() - - def remote_getNumber(self): - return self.b.getNumber() - - def remote_getReason(self): - return self.b.getReason() - - def remote_getChanges(self): - return [IRemote(c) for c in self.b.getChanges()] - - def remote_getRevisions(self): - return self.b.getRevisions() - - def remote_getResponsibleUsers(self): - return self.b.getResponsibleUsers() - - def remote_getSteps(self): - return [IRemote(s) for s in self.b.getSteps()] - - def remote_getTimes(self): - return self.b.getTimes() - - def remote_isFinished(self): - return self.b.isFinished() - - def remote_waitUntilFinished(self): - # the Deferred returned by callRemote() will fire when this build is - # finished - d = self.b.waitUntilFinished() - d.addCallback(lambda res: self) - return d - - def remote_getETA(self): - return self.b.getETA() - - def remote_getCurrentStep(self): - return makeRemote(self.b.getCurrentStep()) - - def remote_getText(self): - return self.b.getText() - - def remote_getResults(self): - return self.b.getResults() - - def remote_getLogs(self): - logs = {} - for name,log in self.b.getLogs().items(): - logs[name] = IRemote(log) - return logs - - def remote_subscribe(self, observer, updateInterval=None): - """The observer will have remote_stepStarted(buildername, build, - stepname, step), remote_stepFinished(buildername, build, stepname, - step, results), and maybe remote_buildETAUpdate(buildername, build, - eta)) messages sent to it.""" - self.observers.append(observer) - s = BuildSubscriber(observer) - self.b.subscribe(s, updateInterval) - - def remote_unsubscribe(self, observer): - # TODO: is the observer automatically unsubscribed when the build - # finishes? Or are they responsible for unsubscribing themselves - # anyway? How do we avoid a race condition here? - for o in self.observers: - if o == observer: - self.observers.remove(o) - - -components.registerAdapter(RemoteBuild, - interfaces.IBuildStatus, IRemote) - -class BuildSubscriber: - def __init__(self, observer): - self.observer = observer - - def buildETAUpdate(self, build, eta): - self.observer.callRemote("buildETAUpdate", - build.getBuilder().getName(), - IRemote(build), - eta) - - def stepStarted(self, build, step): - self.observer.callRemote("stepStarted", - build.getBuilder().getName(), - IRemote(build), - step.getName(), IRemote(step)) - return None - - def stepFinished(self, build, step, results): - self.observer.callRemote("stepFinished", - build.getBuilder().getName(), - IRemote(build), - step.getName(), IRemote(step), - results) - - -class RemoteBuildStep(pb.Referenceable): - def __init__(self, step): - self.s = step - - def remote_getName(self): - return self.s.getName() - - def remote_getBuild(self): - return IRemote(self.s.getBuild()) - - def remote_getTimes(self): - return self.s.getTimes() - - def remote_getExpectations(self): - return self.s.getExpectations() - - def remote_getLogs(self): - logs = {} - for log in self.s.getLogs(): - logs[log.getName()] = IRemote(log) - return logs - - def remote_isFinished(self): - return self.s.isFinished() - - def remote_waitUntilFinished(self): - return self.s.waitUntilFinished() # returns a Deferred - - def remote_getETA(self): - return self.s.getETA() - - def remote_getText(self): - return self.s.getText() - - def remote_getResults(self): - return self.s.getResults() - -components.registerAdapter(RemoteBuildStep, - interfaces.IBuildStepStatus, IRemote) - -class RemoteSlave: - def __init__(self, slave): - self.s = slave - - def remote_getName(self): - return self.s.getName() - def remote_getAdmin(self): - return self.s.getAdmin() - def remote_getHost(self): - return self.s.getHost() - def remote_isConnected(self): - return self.s.isConnected() - -components.registerAdapter(RemoteSlave, - interfaces.ISlaveStatus, IRemote) - -class RemoteEvent: - def __init__(self, event): - self.e = event - - def remote_getTimes(self): - return self.s.getTimes() - def remote_getText(self): - return self.s.getText() - -components.registerAdapter(RemoteEvent, - interfaces.IStatusEvent, IRemote) - -class RemoteLog(pb.Referenceable): - def __init__(self, log): - self.l = log - - def remote_getName(self): - return self.l.getName() - - def remote_isFinished(self): - return self.l.isFinished() - def remote_waitUntilFinished(self): - d = self.l.waitUntilFinished() - d.addCallback(lambda res: self) - return d - - def remote_getText(self): - return self.l.getText() - def remote_getTextWithHeaders(self): - return self.l.getTextWithHeaders() - def remote_getChunks(self): - return self.l.getChunks() - # TODO: subscription interface - -components.registerAdapter(RemoteLog, logfile.LogFile, IRemote) -# TODO: something similar for builder.HTMLLogfile ? - -class RemoteChange: - def __init__(self, change): - self.c = change - - def getWho(self): - return self.c.who - def getFiles(self): - return self.c.files - def getComments(self): - return self.c.comments - -components.registerAdapter(RemoteChange, changes.Change, IRemote) - - -class StatusClientPerspective(base.StatusReceiverPerspective): - - subscribed = None - client = None - - def __init__(self, status): - self.status = status # the IStatus - self.subscribed_to_builders = [] # Builders to which we're subscribed - self.subscribed_to = [] # everything else we're subscribed to - - def __getstate__(self): - d = self.__dict__.copy() - d['client'] = None - return d - - def attached(self, mind): - #twlog.msg("StatusClientPerspective.attached") - return self - - def detached(self, mind): - twlog.msg("PB client detached") - self.client = None - for name in self.subscribed_to_builders: - twlog.msg(" unsubscribing from Builder(%s)" % name) - self.status.getBuilder(name).unsubscribe(self) - for s in self.subscribed_to: - twlog.msg(" unsubscribe from %s" % s) - s.unsubscribe(self) - self.subscribed = None - - def perspective_subscribe(self, mode, interval, target): - """The remote client wishes to subscribe to some set of events. - 'target' will be sent remote messages when these events happen. - 'mode' indicates which events are desired: it is a string with one - of the following values: - - 'builders': builderAdded, builderRemoved - 'builds': those plus builderChangedState, buildStarted, buildFinished - 'steps': all those plus buildETAUpdate, stepStarted, stepFinished - 'logs': all those plus stepETAUpdate, logStarted, logFinished - 'full': all those plus logChunk (with the log contents) - - - Messages are defined by buildbot.interfaces.IStatusReceiver . - 'interval' is used to specify how frequently ETAUpdate messages - should be sent. - - Raising or lowering the subscription level will take effect starting - with the next build or step.""" - - assert mode in ("builders", "builds", "steps", "logs", "full") - assert target - twlog.msg("PB subscribe(%s)" % mode) - - self.client = target - self.subscribed = mode - self.interval = interval - self.subscribed_to.append(self.status) - # wait a moment before subscribing, so the new-builder messages - # won't appear before this remote method finishes - eventually(self.status.subscribe, self) - return None - - def perspective_unsubscribe(self): - twlog.msg("PB unsubscribe") - self.status.unsubscribe(self) - self.subscribed_to.remove(self.status) - self.client = None - - def perspective_getBuildSets(self): - """This returns tuples of (buildset, bsid), because that is much more - convenient for tryclient.""" - d = self.status.getBuildSets() - def make_remotes(buildsets): - return [(IRemote(s), s.id) for s in buildsets] - d.addCallback(make_remotes) - return d - - def perspective_getBuilderNames(self): - return self.status.getBuilderNames() - - def perspective_getBuilder(self, name): - b = self.status.getBuilder(name) - return IRemote(b) - - def perspective_getSlave(self, name): - s = self.status.getSlave(name) - return IRemote(s) - - def perspective_ping(self): - """Ping method to allow pb clients to validate their connections.""" - return "pong" - - # IStatusReceiver methods, invoked if we've subscribed - - # mode >= builder - def builderAdded(self, name, builder): - self.client.callRemote("builderAdded", name, IRemote(builder)) - if self.subscribed in ("builds", "steps", "logs", "full"): - self.subscribed_to_builders.append(name) - return self - return None - - def builderChangedState(self, name, state): - self.client.callRemote("builderChangedState", name, state, None) - # TODO: remove leftover ETA argument - - def builderRemoved(self, name): - if name in self.subscribed_to_builders: - self.subscribed_to_builders.remove(name) - self.client.callRemote("builderRemoved", name) - - def buildsetSubmitted(self, buildset): - # TODO: deliver to client, somehow - pass - - # mode >= builds - def buildStarted(self, name, build): - self.client.callRemote("buildStarted", name, IRemote(build)) - if self.subscribed in ("steps", "logs", "full"): - self.subscribed_to.append(build) - return (self, self.interval) - return None - - def buildFinished(self, name, build, results): - if build in self.subscribed_to: - # we might have joined during the build - self.subscribed_to.remove(build) - self.client.callRemote("buildFinished", - name, IRemote(build), results) - - # mode >= steps - def buildETAUpdate(self, build, eta): - self.client.callRemote("buildETAUpdate", - build.getBuilder().getName(), IRemote(build), - eta) - - def stepStarted(self, build, step): - # we add some information here so the client doesn't have to do an - # extra round-trip - self.client.callRemote("stepStarted", - build.getBuilder().getName(), IRemote(build), - step.getName(), IRemote(step)) - if self.subscribed in ("logs", "full"): - self.subscribed_to.append(step) - return (self, self.interval) - return None - - def stepFinished(self, build, step, results): - self.client.callRemote("stepFinished", - build.getBuilder().getName(), IRemote(build), - step.getName(), IRemote(step), - results) - if step in self.subscribed_to: - # eventually (through some new subscription method) we could - # join in the middle of the step - self.subscribed_to.remove(step) - - # mode >= logs - def stepETAUpdate(self, build, step, ETA, expectations): - self.client.callRemote("stepETAUpdate", - build.getBuilder().getName(), IRemote(build), - step.getName(), IRemote(step), - ETA, expectations) - - def logStarted(self, build, step, log): - # TODO: make the HTMLLog adapter - rlog = IRemote(log, None) - if not rlog: - print "hey, couldn't adapt %s to IRemote" % log - self.client.callRemote("logStarted", - build.getBuilder().getName(), IRemote(build), - step.getName(), IRemote(step), - log.getName(), IRemote(log, None)) - if self.subscribed in ("full",): - self.subscribed_to.append(log) - return self - return None - - def logFinished(self, build, step, log): - self.client.callRemote("logFinished", - build.getBuilder().getName(), IRemote(build), - step.getName(), IRemote(step), - log.getName(), IRemote(log, None)) - if log in self.subscribed_to: - self.subscribed_to.remove(log) - - # mode >= full - def logChunk(self, build, step, log, channel, text): - self.client.callRemote("logChunk", - build.getBuilder().getName(), IRemote(build), - step.getName(), IRemote(step), - log.getName(), IRemote(log), - channel, text) - - -class PBListener(base.StatusReceiverMultiService): - """I am a listener for PB-based status clients.""" - - compare_attrs = ["port", "cred"] - implements(portal.IRealm) - - def __init__(self, port, user="statusClient", passwd="clientpw"): - base.StatusReceiverMultiService.__init__(self) - if type(port) is int: - port = "tcp:%d" % port - self.port = port - self.cred = (user, passwd) - p = portal.Portal(self) - c = checkers.InMemoryUsernamePasswordDatabaseDontUse() - c.addUser(user, passwd) - p.registerChecker(c) - f = pb.PBServerFactory(p) - s = strports.service(port, f) - s.setServiceParent(self) - - def setServiceParent(self, parent): - base.StatusReceiverMultiService.setServiceParent(self, parent) - self.status = parent - - def requestAvatar(self, avatarID, mind, interface): - assert interface == pb.IPerspective - p = StatusClientPerspective(self.status) - p.attached(mind) # perhaps .callLater(0) ? - return (pb.IPerspective, p, - lambda p=p,mind=mind: p.detached(mind)) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/event.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/event.py deleted file mode 100644 index 8e072931..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/event.py +++ /dev/null @@ -1,35 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -from zope.interface import implements -from buildbot import interfaces, util - -class Event: - implements(interfaces.IStatusEvent) - - started = None - finished = None - text = [] - - # IStatusEvent methods - def getTimes(self): - return (self.started, self.finished) - def getText(self): - return self.text - def getLogs(self): - return [] - - def finish(self): - self.finished = util.now() diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/html.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/html.py deleted file mode 100644 index a0fc5755..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/html.py +++ /dev/null @@ -1,21 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -# compatibility wrapper. This is currently the preferred place for master.cfg -# to import from. - -from buildbot.status.web.baseweb import WebStatus -_hush_pyflakes = [WebStatus] diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/logfile.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/logfile.py deleted file mode 100644 index bd1d686d..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/logfile.py +++ /dev/null @@ -1,699 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -import os -from cStringIO import StringIO -from bz2 import BZ2File -from gzip import GzipFile - -from zope.interface import implements -from twisted.python import log, runtime -from twisted.internet import defer, threads, reactor -from buildbot.util import netstrings -from buildbot.util.eventual import eventually -from buildbot import interfaces - -STDOUT = interfaces.LOG_CHANNEL_STDOUT -STDERR = interfaces.LOG_CHANNEL_STDERR -HEADER = interfaces.LOG_CHANNEL_HEADER -ChunkTypes = ["stdout", "stderr", "header"] - -class LogFileScanner(netstrings.NetstringParser): - def __init__(self, chunk_cb, channels=[]): - self.chunk_cb = chunk_cb - self.channels = channels - netstrings.NetstringParser.__init__(self) - - def stringReceived(self, line): - channel = int(line[0]) - if not self.channels or (channel in self.channels): - self.chunk_cb((channel, line[1:])) - -class LogFileProducer: - """What's the plan? - - the LogFile has just one FD, used for both reading and writing. - Each time you add an entry, fd.seek to the end and then write. - - Each reader (i.e. Producer) keeps track of their own offset. The reader - starts by seeking to the start of the logfile, and reading forwards. - Between each hunk of file they yield chunks, so they must remember their - offset before yielding and re-seek back to that offset before reading - more data. When their read() returns EOF, they're finished with the first - phase of the reading (everything that's already been written to disk). - - After EOF, the remaining data is entirely in the current entries list. - These entries are all of the same channel, so we can do one "".join and - obtain a single chunk to be sent to the listener. But since that involves - a yield, and more data might arrive after we give up control, we have to - subscribe them before yielding. We can't subscribe them any earlier, - otherwise they'd get data out of order. - - We're using a generator in the first place so that the listener can - throttle us, which means they're pulling. But the subscription means - we're pushing. Really we're a Producer. In the first phase we can be - either a PullProducer or a PushProducer. In the second phase we're only a - PushProducer. - - So the client gives a LogFileConsumer to File.subscribeConsumer . This - Consumer must have registerProducer(), unregisterProducer(), and - writeChunk(), and is just like a regular twisted.interfaces.IConsumer, - except that writeChunk() takes chunks (tuples of (channel,text)) instead - of the normal write() which takes just text. The LogFileConsumer is - allowed to call stopProducing, pauseProducing, and resumeProducing on the - producer instance it is given. """ - - paused = False - subscribed = False - BUFFERSIZE = 2048 - - def __init__(self, logfile, consumer): - self.logfile = logfile - self.consumer = consumer - self.chunkGenerator = self.getChunks() - consumer.registerProducer(self, True) - - def getChunks(self): - f = self.logfile.getFile() - offset = 0 - chunks = [] - p = LogFileScanner(chunks.append) - f.seek(offset) - data = f.read(self.BUFFERSIZE) - offset = f.tell() - while data: - p.dataReceived(data) - while chunks: - yield chunks.pop(0) - f.seek(offset) - data = f.read(self.BUFFERSIZE) - offset = f.tell() - del f - - # now subscribe them to receive new entries - self.subscribed = True - self.logfile.watchers.append(self) - d = self.logfile.waitUntilFinished() - - # then give them the not-yet-merged data - if self.logfile.runEntries: - channel = self.logfile.runEntries[0][0] - text = "".join([c[1] for c in self.logfile.runEntries]) - yield (channel, text) - - # now we've caught up to the present. Anything further will come from - # the logfile subscription. We add the callback *after* yielding the - # data from runEntries, because the logfile might have finished - # during the yield. - d.addCallback(self.logfileFinished) - - def stopProducing(self): - # TODO: should we still call consumer.finish? probably not. - self.paused = True - self.consumer = None - self.done() - - def done(self): - if self.chunkGenerator: - self.chunkGenerator = None # stop making chunks - if self.subscribed: - self.logfile.watchers.remove(self) - self.subscribed = False - - def pauseProducing(self): - self.paused = True - - def resumeProducing(self): - # Twisted-1.3.0 has a bug which causes hangs when resumeProducing - # calls transport.write (there is a recursive loop, fixed in 2.0 in - # t.i.abstract.FileDescriptor.doWrite by setting the producerPaused - # flag *before* calling resumeProducing). To work around this, we - # just put off the real resumeProducing for a moment. This probably - # has a performance hit, but I'm going to assume that the log files - # are not retrieved frequently enough for it to be an issue. - - eventually(self._resumeProducing) - - def _resumeProducing(self): - self.paused = False - if not self.chunkGenerator: - return - try: - while not self.paused: - chunk = self.chunkGenerator.next() - self.consumer.writeChunk(chunk) - # we exit this when the consumer says to stop, or we run out - # of chunks - except StopIteration: - # if the generator finished, it will have done releaseFile - self.chunkGenerator = None - # now everything goes through the subscription, and they don't get to - # pause anymore - - def logChunk(self, build, step, logfile, channel, chunk): - if self.consumer: - self.consumer.writeChunk((channel, chunk)) - - def logfileFinished(self, logfile): - self.done() - if self.consumer: - self.consumer.unregisterProducer() - self.consumer.finish() - self.consumer = None - -class LogFile: - """ - A LogFile keeps all of its contents on disk, in a non-pickle format to - which new entries can easily be appended. The file on disk has a name like - 12-log-compile-output, under the Builder's directory. The actual filename - is generated (before the LogFile is created) by - L{BuildStatus.generateLogfileName}. - - @ivar length: length of the data in the logfile (sum of chunk sizes; not - the length of the on-disk encoding) - """ - - implements(interfaces.IStatusLog, interfaces.ILogFile) - - finished = False - length = 0 - nonHeaderLength = 0 - tailLength = 0 - chunkSize = 10*1000 - runLength = 0 - # No max size by default - # Don't keep a tail buffer by default - logMaxTailSize = None - maxLengthExceeded = False - runEntries = [] # provided so old pickled builds will getChunks() ok - entries = None - BUFFERSIZE = 2048 - filename = None # relative to the Builder's basedir - openfile = None - - def __init__(self, parent, name, logfilename): - """ - @type parent: L{BuildStepStatus} - @param parent: the Step that this log is a part of - @type name: string - @param name: the name of this log, typically 'output' - @type logfilename: string - @param logfilename: the Builder-relative pathname for the saved entries - """ - self.step = parent - self.master = parent.build.builder.master - self.name = name - self.filename = logfilename - fn = self.getFilename() - if os.path.exists(fn): - # the buildmaster was probably stopped abruptly, before the - # BuilderStatus could be saved, so BuilderStatus.nextBuildNumber - # is out of date, and we're overlapping with earlier builds now. - # Warn about it, but then overwrite the old pickle file - log.msg("Warning: Overwriting old serialized Build at %s" % fn) - dirname = os.path.dirname(fn) - if not os.path.exists(dirname): - os.makedirs(dirname) - self.openfile = open(fn, "w+") - self.runEntries = [] - self.watchers = [] - self.finishedWatchers = [] - self.tailBuffer = [] - - def getFilename(self): - """ - Get the base (uncompressed) filename for this log file. - - @returns: filename - """ - return os.path.join(self.step.build.builder.basedir, self.filename) - - def hasContents(self): - """ - Return true if this logfile's contents are available. For a newly - created logfile, this is always true, but for a L{LogFile} instance - that has been persisted, the logfiles themselves may have been deleted, - in which case this method will return False. - - @returns: boolean - """ - return os.path.exists(self.getFilename() + '.bz2') or \ - os.path.exists(self.getFilename() + '.gz') or \ - os.path.exists(self.getFilename()) - - def getName(self): - """ - Get this logfile's name - - @returns: string - """ - return self.name - - def getStep(self): - """ - Get the L{BuildStepStatus} instance containing this logfile - - @returns: L{BuildStepStatus} instance - """ - return self.step - - def isFinished(self): - """ - Return true if this logfile is finished (that is, if it will not - receive any additional data - - @returns: boolean - """ - - return self.finished - - def waitUntilFinished(self): - """ - Return a Deferred that will fire when this logfile is finished, or will - fire immediately if the logfile is already finished. - """ - if self.finished: - d = defer.succeed(self) - else: - d = defer.Deferred() - self.finishedWatchers.append(d) - return d - - def getFile(self): - """ - Get an open file object for this log. The file may also be in use for - writing, so it should not be closed by the caller, and the caller - should not rely on its file position remaining constant between - asynchronous code segments. - - @returns: file object - """ - if self.openfile: - # this is the filehandle we're using to write to the log, so - # don't close it! - return self.openfile - # otherwise they get their own read-only handle - # try a compressed log first - try: - return BZ2File(self.getFilename() + ".bz2", "r") - except IOError: - pass - try: - return GzipFile(self.getFilename() + ".gz", "r") - except IOError: - pass - return open(self.getFilename(), "r") - - def getText(self): - # this produces one ginormous string - return "".join(self.getChunks([STDOUT, STDERR], onlyText=True)) - - def getTextWithHeaders(self): - return "".join(self.getChunks(onlyText=True)) - - def getChunks(self, channels=[], onlyText=False): - # generate chunks for everything that was logged at the time we were - # first called, so remember how long the file was when we started. - # Don't read beyond that point. The current contents of - # self.runEntries will follow. - - # this returns an iterator, which means arbitrary things could happen - # while we're yielding. This will faithfully deliver the log as it - # existed when it was started, and not return anything after that - # point. To use this in subscribe(catchup=True) without missing any - # data, you must insure that nothing will be added to the log during - # yield() calls. - - f = self.getFile() - if not self.finished: - offset = 0 - f.seek(0, 2) - remaining = f.tell() - else: - offset = 0 - remaining = None - - leftover = None - if self.runEntries and (not channels or - (self.runEntries[0][0] in channels)): - leftover = (self.runEntries[0][0], - "".join([c[1] for c in self.runEntries])) - - # freeze the state of the LogFile by passing a lot of parameters into - # a generator - return self._generateChunks(f, offset, remaining, leftover, - channels, onlyText) - - def _generateChunks(self, f, offset, remaining, leftover, - channels, onlyText): - chunks = [] - p = LogFileScanner(chunks.append, channels) - f.seek(offset) - if remaining is not None: - data = f.read(min(remaining, self.BUFFERSIZE)) - remaining -= len(data) - else: - data = f.read(self.BUFFERSIZE) - - offset = f.tell() - while data: - p.dataReceived(data) - while chunks: - channel, text = chunks.pop(0) - if onlyText: - yield text - else: - yield (channel, text) - f.seek(offset) - if remaining is not None: - data = f.read(min(remaining, self.BUFFERSIZE)) - remaining -= len(data) - else: - data = f.read(self.BUFFERSIZE) - offset = f.tell() - del f - - if leftover: - if onlyText: - yield leftover[1] - else: - yield leftover - - def readlines(self): - """Return an iterator that produces newline-terminated lines, - excluding header chunks.""" - alltext = "".join(self.getChunks([STDOUT], onlyText=True)) - io = StringIO(alltext) - return io.readlines() - - def subscribe(self, receiver, catchup): - if self.finished: - return - self.watchers.append(receiver) - if catchup: - for channel, text in self.getChunks(): - # TODO: add logChunks(), to send over everything at once? - receiver.logChunk(self.step.build, self.step, self, - channel, text) - - def unsubscribe(self, receiver): - if receiver in self.watchers: - self.watchers.remove(receiver) - - def subscribeConsumer(self, consumer): - p = LogFileProducer(self, consumer) - p.resumeProducing() - - # interface used by the build steps to add things to the log - - def _merge(self): - # merge all .runEntries (which are all of the same type) into a - # single chunk for .entries - if not self.runEntries: - return - channel = self.runEntries[0][0] - text = "".join([c[1] for c in self.runEntries]) - assert channel < 10, "channel number must be a single decimal digit" - f = self.openfile - f.seek(0, 2) - offset = 0 - while offset < len(text): - size = min(len(text)-offset, self.chunkSize) - f.write("%d:%d" % (1 + size, channel)) - f.write(text[offset:offset+size]) - f.write(",") - offset += size - self.runEntries = [] - self.runLength = 0 - - def addEntry(self, channel, text, _no_watchers=False): - """ - Add an entry to the logfile. The C{channel} is one of L{STDOUT}, - L{STDERR}, or L{HEADER}. The C{text} is the text to add to the - logfile, which can be a unicode string or a bytestring which is - presumed to be encoded with utf-8. - - This method cannot be called after the logfile is finished. - - @param channel: channel to add a chunk for - @param text: chunk of text - @param _no_watchers: private - """ - - assert not self.finished, "logfile is already finished" - - if isinstance(text, unicode): - text = text.encode('utf-8') - - # notify watchers first, before the chunk gets munged, so that they get - # a complete picture of the actual log output - # TODO: is this right, or should the watchers get a picture of the chunks? - if not _no_watchers: - for w in self.watchers: - w.logChunk(self.step.build, self.step, self, channel, text) - - if channel != HEADER: - # Truncate the log if it's more than logMaxSize bytes - logMaxSize = self.master.config.logMaxSize - logMaxTailSize = self.master.config.logMaxTailSize - if logMaxSize: - self.nonHeaderLength += len(text) - if self.nonHeaderLength > logMaxSize: - # Add a message about what's going on and truncate this - # chunk if necessary - if not self.maxLengthExceeded: - if self.runEntries and channel != self.runEntries[0][0]: - self._merge() - i = -(self.nonHeaderLength - logMaxSize) - trunc, text = text[:i], text[i:] - self.runEntries.append((channel, trunc)) - self._merge() - msg = ("\nOutput exceeded %i bytes, remaining output " - "has been truncated\n" % logMaxSize) - self.runEntries.append((HEADER, msg)) - self.maxLengthExceeded = True - - # and track the tail of the text - if logMaxTailSize and text: - # Update the tail buffer - self.tailBuffer.append((channel, text)) - self.tailLength += len(text) - while self.tailLength > logMaxTailSize: - # Drop some stuff off the beginning of the buffer - c,t = self.tailBuffer.pop(0) - n = len(t) - self.tailLength -= n - assert self.tailLength >= 0 - return - - # we only add to .runEntries here. _merge() is responsible for adding - # merged chunks to .entries - if self.runEntries and channel != self.runEntries[0][0]: - self._merge() - self.runEntries.append((channel, text)) - self.runLength += len(text) - if self.runLength >= self.chunkSize: - self._merge() - - self.length += len(text) - - def addStdout(self, text): - """ - Shortcut to add stdout text to the logfile - - @param text: text to add to the logfile - """ - self.addEntry(STDOUT, text) - - def addStderr(self, text): - """ - Shortcut to add stderr text to the logfile - - @param text: text to add to the logfile - """ - self.addEntry(STDERR, text) - - def addHeader(self, text): - """ - Shortcut to add header text to the logfile - - @param text: text to add to the logfile - """ - self.addEntry(HEADER, text) - - def finish(self): - """ - Finish the logfile, flushing any buffers and preventing any further - writes to the log. - """ - self._merge() - if self.tailBuffer: - msg = "\nFinal %i bytes follow below:\n" % self.tailLength - tmp = self.runEntries - self.runEntries = [(HEADER, msg)] - self._merge() - self.runEntries = self.tailBuffer - self._merge() - self.runEntries = tmp - self._merge() - self.tailBuffer = [] - - if self.openfile: - # we don't do an explicit close, because there might be readers - # shareing the filehandle. As soon as they stop reading, the - # filehandle will be released and automatically closed. - self.openfile.flush() - self.openfile = None - self.finished = True - watchers = self.finishedWatchers - self.finishedWatchers = [] - for w in watchers: - w.callback(self) - self.watchers = [] - - - def compressLog(self): - logCompressionMethod = self.master.config.logCompressionMethod - # bail out if there's no compression support - if logCompressionMethod == "bz2": - compressed = self.getFilename() + ".bz2.tmp" - elif logCompressionMethod == "gz": - compressed = self.getFilename() + ".gz.tmp" - else: - return defer.succeed(None) - - def _compressLog(): - infile = self.getFile() - if logCompressionMethod == "bz2": - cf = BZ2File(compressed, 'w') - elif logCompressionMethod == "gz": - cf = GzipFile(compressed, 'w') - bufsize = 1024*1024 - while True: - buf = infile.read(bufsize) - cf.write(buf) - if len(buf) < bufsize: - break - cf.close() - d = threads.deferToThread(_compressLog) - - def _renameCompressedLog(rv): - if logCompressionMethod == "bz2": - filename = self.getFilename() + '.bz2' - else: - filename = self.getFilename() + '.gz' - if runtime.platformType == 'win32': - # windows cannot rename a file on top of an existing one, so - # fall back to delete-first. There are ways this can fail and - # lose the builder's history, so we avoid using it in the - # general (non-windows) case - if os.path.exists(filename): - os.unlink(filename) - os.rename(compressed, filename) - _tryremove(self.getFilename(), 1, 5) - d.addCallback(_renameCompressedLog) - - def _cleanupFailedCompress(failure): - log.msg("failed to compress %s" % self.getFilename()) - if os.path.exists(compressed): - _tryremove(compressed, 1, 5) - failure.trap() # reraise the failure - d.addErrback(_cleanupFailedCompress) - return d - - - # persistence stuff - def __getstate__(self): - d = self.__dict__.copy() - del d['step'] # filled in upon unpickling - del d['watchers'] - del d['finishedWatchers'] - del d['master'] - d['entries'] = [] # let 0.6.4 tolerate the saved log. TODO: really? - if d.has_key('finished'): - del d['finished'] - if d.has_key('openfile'): - del d['openfile'] - return d - - def __setstate__(self, d): - self.__dict__ = d - self.watchers = [] # probably not necessary - self.finishedWatchers = [] # same - # self.step must be filled in by our parent - self.finished = True - -class HTMLLogFile: - implements(interfaces.IStatusLog) - - filename = None - - def __init__(self, parent, name, logfilename, html): - self.step = parent - self.name = name - self.filename = logfilename - self.html = html - - def getName(self): - return self.name # set in BuildStepStatus.addLog - def getStep(self): - return self.step - - def isFinished(self): - return True - def waitUntilFinished(self): - return defer.succeed(self) - - def hasContents(self): - return True - def getText(self): - return self.html # looks kinda like text - def getTextWithHeaders(self): - return self.html - def getChunks(self): - return [(STDERR, self.html)] - - def subscribe(self, receiver, catchup): - pass - def unsubscribe(self, receiver): - pass - - def finish(self): - pass - - def __getstate__(self): - d = self.__dict__.copy() - del d['step'] - if d.has_key('master'): - del d['master'] - return d - - -def _tryremove(filename, timeout, retries): - """Try to remove a file, and if failed, try again in timeout. - Increases the timeout by a factor of 4, and only keeps trying for - another retries-amount of times. - - """ - try: - os.unlink(filename) - except OSError: - if retries > 0: - reactor.callLater(timeout, _tryremove, filename, timeout * 4, - retries - 1) - else: - log.msg("giving up on removing %s after over %d seconds" % - (filename, timeout)) - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/mail.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/mail.py deleted file mode 100644 index ef48d8e2..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/mail.py +++ /dev/null @@ -1,798 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -import re -import types -from email.Message import Message -from email.Utils import formatdate -from email.MIMEText import MIMEText -from email.MIMENonMultipart import MIMENonMultipart -from email.MIMEMultipart import MIMEMultipart -from StringIO import StringIO -import urllib - -from zope.interface import implements -from twisted.internet import defer, reactor -from twisted.python import log as twlog - -try: - from twisted.mail.smtp import ESMTPSenderFactory - ESMTPSenderFactory = ESMTPSenderFactory # for pyflakes -except ImportError: - ESMTPSenderFactory = None - -have_ssl = True -try: - from twisted.internet import ssl - from OpenSSL.SSL import SSLv3_METHOD -except ImportError: - have_ssl = False - -# this incantation teaches email to output utf-8 using 7- or 8-bit encoding, -# although it has no effect before python-2.7. -from email import Charset -Charset.add_charset('utf-8', Charset.SHORTEST, None, 'utf-8') - -from buildbot import interfaces, util, config -from buildbot.process.users import users -from buildbot.status import base -from buildbot.status.results import FAILURE, SUCCESS, WARNINGS, EXCEPTION, Results - -VALID_EMAIL = re.compile("[a-zA-Z0-9\.\_\%\-\+]+@[a-zA-Z0-9\.\_\%\-]+.[a-zA-Z]{2,6}") - -ENCODING = 'utf8' -LOG_ENCODING = 'utf-8' - -class Domain(util.ComparableMixin): - implements(interfaces.IEmailLookup) - compare_attrs = ["domain"] - - def __init__(self, domain): - assert "@" not in domain - self.domain = domain - - def getAddress(self, name): - """If name is already an email address, pass it through.""" - if '@' in name: - return name - return name + "@" + self.domain - - -def defaultMessage(mode, name, build, results, master_status): - """Generate a buildbot mail message and return a tuple of message text - and type.""" - ss_list = build.getSourceStamps() - - prev = build.getPreviousBuild() - - text = "" - if results == FAILURE: - if "change" in mode and prev and prev.getResults() != results or \ - "problem" in mode and prev and prev.getResults() != FAILURE: - text += "The Buildbot has detected a new failure" - else: - text += "The Buildbot has detected a failed build" - elif results == WARNINGS: - text += "The Buildbot has detected a problem in the build" - elif results == SUCCESS: - if "change" in mode and prev and prev.getResults() != results: - text += "The Buildbot has detected a restored build" - else: - text += "The Buildbot has detected a passing build" - elif results == EXCEPTION: - text += "The Buildbot has detected a build exception" - - projects = [] - if ss_list: - for ss in ss_list: - if ss.project and ss.project not in projects: - projects.append(ss.project) - if not projects: - projects = [master_status.getTitle()] - text += " on builder %s while building %s.\n" % (name, ', '.join(projects)) - - if master_status.getURLForThing(build): - text += "Full details are available at:\n %s\n" % master_status.getURLForThing(build) - text += "\n" - - if master_status.getBuildbotURL(): - text += "Buildbot URL: %s\n\n" % urllib.quote(master_status.getBuildbotURL(), '/:') - - text += "Buildslave for this Build: %s\n\n" % build.getSlavename() - text += "Build Reason: %s\n" % build.getReason() - - for ss in ss_list: - source = "" - if ss and ss.branch: - source += "[branch %s] " % ss.branch - if ss and ss.revision: - source += str(ss.revision) - else: - source += "HEAD" - if ss and ss.patch: - source += " (plus patch)" - - discriminator = "" - if ss.codebase: - discriminator = " '%s'" % ss.codebase - text += "Build Source Stamp%s: %s\n" % (discriminator, source) - - text += "Blamelist: %s\n" % ",".join(build.getResponsibleUsers()) - - text += "\n" - - t = build.getText() - if t: - t = ": " + " ".join(t) - else: - t = "" - - if results == SUCCESS: - text += "Build succeeded!\n" - elif results == WARNINGS: - text += "Build Had Warnings%s\n" % t - else: - text += "BUILD FAILED%s\n" % t - - text += "\n" - text += "sincerely,\n" - text += " -The Buildbot\n" - text += "\n" - return { 'body' : text, 'type' : 'plain' } - -def defaultGetPreviousBuild(current_build): - return current_build.getPreviousBuild() - -class MailNotifier(base.StatusReceiverMultiService): - """This is a status notifier which sends email to a list of recipients - upon the completion of each build. It can be configured to only send out - mail for certain builds, and only send messages when the build fails, or - when it transitions from success to failure. It can also be configured to - include various build logs in each message. - - By default, the message will be sent to the Interested Users list, which - includes all developers who made changes in the build. You can add - additional recipients with the extraRecipients argument. - - To get a simple one-message-per-build (say, for a mailing list), use - sendToInterestedUsers=False, extraRecipients=['listaddr@example.org'] - - Each MailNotifier sends mail to a single set of recipients. To send - different kinds of mail to different recipients, use multiple - MailNotifiers. - """ - - implements(interfaces.IEmailSender) - - compare_attrs = ["extraRecipients", "lookup", "fromaddr", "mode", - "categories", "builders", "addLogs", "relayhost", - "subject", "sendToInterestedUsers", "customMesg", - "messageFormatter", "extraHeaders"] - - possible_modes = ("change", "failing", "passing", "problem", "warnings", "exception") - - def __init__(self, fromaddr, mode=("failing", "passing", "warnings"), - categories=None, builders=None, addLogs=False, - relayhost="localhost", buildSetSummary=False, - subject="buildbot %(result)s in %(title)s on %(builder)s", - lookup=None, extraRecipients=[], - sendToInterestedUsers=True, customMesg=None, - messageFormatter=defaultMessage, extraHeaders=None, - addPatch=True, useTls=False, - smtpUser=None, smtpPassword=None, smtpPort=25, - previousBuildGetter=defaultGetPreviousBuild): - """ - @type fromaddr: string - @param fromaddr: the email address to be used in the 'From' header. - @type sendToInterestedUsers: boolean - @param sendToInterestedUsers: if True (the default), send mail to all - of the Interested Users. If False, only - send mail to the extraRecipients list. - - @type extraRecipients: tuple of strings - @param extraRecipients: a list of email addresses to which messages - should be sent (in addition to the - InterestedUsers list, which includes any - developers who made Changes that went into this - build). It is a good idea to create a small - mailing list and deliver to that, then let - subscribers come and go as they please. The - addresses in this list are used literally (they - are not processed by lookup). - - @type subject: string - @param subject: a string to be used as the subject line of the message. - %(builder)s will be replaced with the name of the - builder which provoked the message. - - @type mode: list of strings - @param mode: a list of MailNotifer.possible_modes: - - "change": send mail about builds which change status - - "failing": send mail about builds which fail - - "passing": send mail about builds which succeed - - "problem": send mail about a build which failed - when the previous build passed - - "warnings": send mail if a build contain warnings - - "exception": send mail if a build fails due to an exception - - "all": always send mail - Defaults to ("failing", "passing", "warnings"). - - @type builders: list of strings - @param builders: a list of builder names for which mail should be - sent. Defaults to None (send mail for all builds). - Use either builders or categories, but not both. - - @type categories: list of strings - @param categories: a list of category names to serve status - information for. Defaults to None (all - categories). Use either builders or categories, - but not both. - - @type addLogs: boolean - @param addLogs: if True, include all build logs as attachments to the - messages. These can be quite large. This can also be - set to a list of log names, to send a subset of the - logs. Defaults to False. - - @type addPatch: boolean - @param addPatch: if True, include the patch when the source stamp - includes one. - - @type relayhost: string - @param relayhost: the host to which the outbound SMTP connection - should be made. Defaults to 'localhost' - - @type buildSetSummary: boolean - @param buildSetSummary: if True, this notifier will only send a summary - email when a buildset containing any of its - watched builds completes - - @type lookup: implementor of {IEmailLookup} - @param lookup: object which provides IEmailLookup, which is - responsible for mapping User names for Interested - Users (which come from the VC system) into valid - email addresses. If not provided, the notifier will - only be able to send mail to the addresses in the - extraRecipients list. Most of the time you can use a - simple Domain instance. As a shortcut, you can pass - as string: this will be treated as if you had provided - Domain(str). For example, lookup='twistedmatrix.com' - will allow mail to be sent to all developers whose SVN - usernames match their twistedmatrix.com account names. - - @type customMesg: func - @param customMesg: (this function is deprecated) - - @type messageFormatter: func - @param messageFormatter: function taking (mode, name, build, result, - master_status) and returning a dictionary - containing two required keys "body" and "type", - with a third optional key, "subject". The - "body" key gives a string that contains the - complete text of the message. The "type" key - is the message type ('plain' or 'html'). The - 'html' type should be used when generating an - HTML message. The optional "subject" key - gives the subject for the email. - - @type extraHeaders: dict - @param extraHeaders: A dict of extra headers to add to the mail. It's - best to avoid putting 'To', 'From', 'Date', - 'Subject', or 'CC' in here. Both the names and - values may be WithProperties instances. - - @type useTls: boolean - @param useTls: Send emails using TLS and authenticate with the - smtp host. Defaults to False. - - @type smtpUser: string - @param smtpUser: The user that will attempt to authenticate with the - relayhost when useTls is True. - - @type smtpPassword: string - @param smtpPassword: The password that smtpUser will use when - authenticating with relayhost. - - @type smtpPort: int - @param smtpPort: The port that will be used when connecting to the - relayhost. Defaults to 25. - - @type previousBuildGetter: func - @param previousBuildGetter: function taking a BuildStatus instance - returning a BuildStatus of the build - previous to the one passed in. This allows - to implement a relative ordering between - builds other than the default one, which is - chronological. - """ - base.StatusReceiverMultiService.__init__(self) - - if not isinstance(extraRecipients, (list, tuple)): - config.error("extraRecipients must be a list or tuple") - else: - for r in extraRecipients: - if not isinstance(r, str) or not VALID_EMAIL.search(r): - config.error( - "extra recipient %r is not a valid email" % (r,)) - self.extraRecipients = extraRecipients - self.sendToInterestedUsers = sendToInterestedUsers - self.fromaddr = fromaddr - if isinstance(mode, basestring): - if mode == "all": - mode = ("failing", "passing", "warnings", "exception") - elif mode == "warnings": - mode = ("failing", "warnings") - else: - mode = (mode,) - for m in mode: - if m not in self.possible_modes: - config.error( - "mode %s is not a valid mode" % (m,)) - self.mode = mode - self.categories = categories - self.builders = builders - self.addLogs = addLogs - self.relayhost = relayhost - if '\n' in subject: - config.error( - 'Newlines are not allowed in email subjects') - self.subject = subject - if lookup is not None: - if type(lookup) is str: - lookup = Domain(lookup) - assert interfaces.IEmailLookup.providedBy(lookup) - self.lookup = lookup - self.customMesg = customMesg - self.messageFormatter = messageFormatter - if extraHeaders: - if not isinstance(extraHeaders, dict): - config.error("extraHeaders must be a dictionary") - self.extraHeaders = extraHeaders - self.addPatch = addPatch - self.useTls = useTls - self.smtpUser = smtpUser - self.smtpPassword = smtpPassword - self.smtpPort = smtpPort - self.buildSetSummary = buildSetSummary - self.buildSetSubscription = None - self.getPreviousBuild = previousBuildGetter - self.watched = [] - self.master_status = None - - # you should either limit on builders or categories, not both - if self.builders != None and self.categories != None: - config.error( - "Please specify only builders or categories to include - " + - "not both.") - - if customMesg: - config.error( - "customMesg is deprecated; use messageFormatter instead") - - def setServiceParent(self, parent): - """ - @type parent: L{buildbot.master.BuildMaster} - """ - base.StatusReceiverMultiService.setServiceParent(self, parent) - self.master_status = self.parent - self.master_status.subscribe(self) - self.master = self.master_status.master - - def startService(self): - if self.buildSetSummary: - self.buildSetSubscription = \ - self.master.subscribeToBuildsetCompletions(self.buildsetFinished) - - base.StatusReceiverMultiService.startService(self) - - def stopService(self): - if self.buildSetSubscription is not None: - self.buildSetSubscription.unsubscribe() - self.buildSetSubscription = None - - return base.StatusReceiverMultiService.stopService(self) - - def disownServiceParent(self): - self.master_status.unsubscribe(self) - self.master_status = None - for w in self.watched: - w.unsubscribe(self) - return base.StatusReceiverMultiService.disownServiceParent(self) - - def builderAdded(self, name, builder): - # only subscribe to builders we are interested in - if self.categories != None and builder.category not in self.categories: - return None - - self.watched.append(builder) - return self # subscribe to this builder - - def builderRemoved(self, name): - pass - - def builderChangedState(self, name, state): - pass - - def buildStarted(self, name, build): - pass - - def isMailNeeded(self, build, results): - # here is where we actually do something. - builder = build.getBuilder() - if self.builders is not None and builder.name not in self.builders: - return False # ignore this build - if self.categories is not None and \ - builder.category not in self.categories: - return False # ignore this build - - prev = self.getPreviousBuild(build) - if "change" in self.mode: - if prev and prev.getResults() != results: - return True - if "failing" in self.mode and results == FAILURE: - return True - if "passing" in self.mode and results == SUCCESS: - return True - if "problem" in self.mode and results == FAILURE: - if prev and prev.getResults() != FAILURE: - return True - if "warnings" in self.mode and results == WARNINGS: - return True - if "exception" in self.mode and results == EXCEPTION: - return True - - return False - - def buildFinished(self, name, build, results): - if ( not self.buildSetSummary and - self.isMailNeeded(build, results) ): - # for testing purposes, buildMessage returns a Deferred that fires - # when the mail has been sent. To help unit tests, we return that - # Deferred here even though the normal IStatusReceiver.buildFinished - # signature doesn't do anything with it. If that changes (if - # .buildFinished's return value becomes significant), we need to - # rearrange this. - return self.buildMessage(name, [build], results) - return None - - def _gotBuilds(self, res, buildset): - builds = [] - for (builddictlist, builder) in res: - for builddict in builddictlist: - build = builder.getBuild(builddict['number']) - if build is not None and self.isMailNeeded(build, build.results): - builds.append(build) - - if builds: - self.buildMessage("Buildset Complete: " + buildset['reason'], builds, - buildset['results']) - - def _gotBuildRequests(self, breqs, buildset): - dl = [] - for breq in breqs: - buildername = breq['buildername'] - builder = self.master_status.getBuilder(buildername) - d = self.master.db.builds.getBuildsForRequest(breq['brid']) - d.addCallback(lambda builddictlist, builder=builder: - (builddictlist, builder)) - dl.append(d) - d = defer.gatherResults(dl) - d.addCallback(self._gotBuilds, buildset) - - def _gotBuildSet(self, buildset, bsid): - d = self.master.db.buildrequests.getBuildRequests(bsid=bsid) - d.addCallback(self._gotBuildRequests, buildset) - - def buildsetFinished(self, bsid, result): - d = self.master.db.buildsets.getBuildset(bsid=bsid) - d.addCallback(self._gotBuildSet, bsid) - - return d - - def getCustomMesgData(self, mode, name, build, results, master_status): - # - # logs is a list of tuples that contain the log - # name, log url, and the log contents as a list of strings. - # - logs = list() - for logf in build.getLogs(): - logStep = logf.getStep() - stepName = logStep.getName() - logStatus, dummy = logStep.getResults() - logName = logf.getName() - logs.append(('%s.%s' % (stepName, logName), - '%s/steps/%s/logs/%s' % ( - master_status.getURLForThing(build), - stepName, logName), - logf.getText().splitlines(), - logStatus)) - - attrs = {'builderName': name, - 'title': master_status.getTitle(), - 'mode': mode, - 'result': Results[results], - 'buildURL': master_status.getURLForThing(build), - 'buildbotURL': master_status.getBuildbotURL(), - 'buildText': build.getText(), - 'buildProperties': build.getProperties(), - 'slavename': build.getSlavename(), - 'reason': build.getReason().replace('\n', ''), - 'responsibleUsers': build.getResponsibleUsers(), - 'branch': "", - 'revision': "", - 'patch': "", - 'patch_info': "", - 'changes': [], - 'logs': logs} - - ss = None - ss_list = build.getSourceStamps() - - if ss_list: - if len(ss_list) == 1: - ss = ss_list[0] - if ss: - attrs['branch'] = ss.branch - attrs['revision'] = ss.revision - attrs['patch'] = ss.patch - attrs['patch_info'] = ss.patch_info - attrs['changes'] = ss.changes[:] - else: - for key in ['branch', 'revision', 'patch', 'patch_info', 'changes']: - attrs[key] = {} - for ss in ss_list: - attrs['branch'][ss.codebase] = ss.branch - attrs['revision'][ss.codebase] = ss.revision - attrs['patch'][ss.codebase] = ss.patch - attrs['patch_info'][ss.codebase] = ss.patch_info - attrs['changes'][ss.codebase] = ss.changes[:] - - return attrs - - def patch_to_attachment(self, patch, index): - # patches don't come with an encoding. If the patch is valid utf-8, - # we'll attach it as MIMEText; otherwise, it gets attached as a binary - # file. This will suit the vast majority of cases, since utf8 is by - # far the most common encoding. - if type(patch[1]) != types.UnicodeType: - try: - unicode = patch[1].decode('utf8') - except UnicodeDecodeError: - unicode = None - else: - unicode = patch[1] - - if unicode: - a = MIMEText(unicode.encode(ENCODING), _charset=ENCODING) - else: - # MIMEApplication is not present in Python-2.4 :( - a = MIMENonMultipart('application', 'octet-stream') - a.set_payload(patch[1]) - a.add_header('Content-Disposition', "attachment", - filename="source patch " + str(index) ) - return a - - def createEmail(self, msgdict, builderName, title, results, builds=None, - patches=None, logs=None): - text = msgdict['body'].encode(ENCODING) - type = msgdict['type'] - if 'subject' in msgdict: - subject = msgdict['subject'].encode(ENCODING) - else: - subject = self.subject % { 'result': Results[results], - 'projectName': title, - 'title': title, - 'builder': builderName, - } - - assert '\n' not in subject, \ - "Subject cannot contain newlines" - - assert type in ('plain', 'html'), \ - "'%s' message type must be 'plain' or 'html'." % type - - if patches or logs: - m = MIMEMultipart() - m.attach(MIMEText(text, type, ENCODING)) - else: - m = Message() - m.set_payload(text, ENCODING) - m.set_type("text/%s" % type) - - m['Date'] = formatdate(localtime=True) - m['Subject'] = subject - m['From'] = self.fromaddr - # m['To'] is added later - - if patches: - for (i, patch) in enumerate(patches): - a = self.patch_to_attachment(patch, i) - m.attach(a) - if logs: - for log in logs: - name = "%s.%s" % (log.getStep().getName(), - log.getName()) - if ( self._shouldAttachLog(log.getName()) or - self._shouldAttachLog(name) ): - text = log.getText() - if not isinstance(text, unicode): - # guess at the encoding, and use replacement symbols - # for anything that's not in that encoding - text = text.decode(LOG_ENCODING, 'replace') - a = MIMEText(text.encode(ENCODING), - _charset=ENCODING) - a.add_header('Content-Disposition', "attachment", - filename=name) - m.attach(a) - - #@todo: is there a better way to do this? - # Add any extra headers that were requested, doing WithProperties - # interpolation if only one build was given - if self.extraHeaders: - if len(builds) == 1: - d = builds[0].render(self.extraHeaders) - else: - d = defer.succeed(self.extraHeaders) - @d.addCallback - def addExtraHeaders(extraHeaders): - for k,v in extraHeaders.items(): - if k in m: - twlog.msg("Warning: Got header " + k + - " in self.extraHeaders " - "but it already exists in the Message - " - "not adding it.") - m[k] = v - d.addCallback(lambda _: m) - return d - - return defer.succeed(m) - - def buildMessageDict(self, name, build, results): - if self.customMesg: - # the customMesg stuff can be *huge*, so we prefer not to load it - attrs = self.getCustomMesgData(self.mode, name, build, results, - self.master_status) - text, type = self.customMesg(attrs) - msgdict = { 'body' : text, 'type' : type } - else: - msgdict = self.messageFormatter(self.mode, name, build, results, - self.master_status) - - return msgdict - - - def buildMessage(self, name, builds, results): - patches = [] - logs = [] - msgdict = {"body":""} - - for build in builds: - ss_list = build.getSourceStamps() - if self.addPatch: - for ss in ss_list: - if ss.patch: - patches.append(ss.patch) - if self.addLogs: - logs.extend(build.getLogs()) - - tmp = self.buildMessageDict(name=build.getBuilder().name, - build=build, results=build.results) - msgdict['body'] += tmp['body'] - msgdict['body'] += '\n\n' - msgdict['type'] = tmp['type'] - if "subject" in tmp: - msgdict['subject'] = tmp['subject'] - - d = self.createEmail(msgdict, name, self.master_status.getTitle(), - results, builds, patches, logs) - - @d.addCallback - def getRecipients(m): - # now, who is this message going to? - if self.sendToInterestedUsers: - dl = [] - for build in builds: - if self.lookup: - d = self.useLookup(build) - else: - d = self.useUsers(build) - dl.append(d) - d = defer.gatherResults(dl) - else: - d = defer.succeed([]) - d.addCallback(self._gotRecipients, m) - return d - - def useLookup(self, build): - dl = [] - for u in build.getResponsibleUsers() + build.getInterestedUsers(): - d = defer.maybeDeferred(self.lookup.getAddress, u) - dl.append(d) - return defer.gatherResults(dl) - - def useUsers(self, build): - return users.getBuildContacts(self.master, build, ['email']) - - def _shouldAttachLog(self, logname): - if type(self.addLogs) is bool: - return self.addLogs - return logname in self.addLogs - - def _gotRecipients(self, rlist, m): - to_recipients = set() - cc_recipients = set() - - for r in reduce(list.__add__, rlist, []): - if r is None: # getAddress didn't like this address - continue - - # Git can give emails like 'User' <user@foo.com>@foo.com so check - # for two @ and chop the last - if r.count('@') > 1: - r = r[:r.rindex('@')] - - if VALID_EMAIL.search(r): - to_recipients.add(r) - else: - twlog.msg("INVALID EMAIL: %r" + r) - - # If we're sending to interested users put the extras in the - # CC list so they can tell if they are also interested in the - # change: - if self.sendToInterestedUsers and to_recipients: - cc_recipients.update(self.extraRecipients) - else: - to_recipients.update(self.extraRecipients) - - m['To'] = ", ".join(sorted(to_recipients)) - if cc_recipients: - m['CC'] = ", ".join(sorted(cc_recipients)) - - return self.sendMessage(m, list(to_recipients | cc_recipients)) - - def sendmail(self, s, recipients): - result = defer.Deferred() - - if have_ssl and self.useTls: - client_factory = ssl.ClientContextFactory() - client_factory.method = SSLv3_METHOD - else: - client_factory = None - - if self.smtpUser and self.smtpPassword: - useAuth = True - else: - useAuth = False - - if not ESMTPSenderFactory: - raise RuntimeError("twisted-mail is not installed - cannot " - "send mail") - sender_factory = ESMTPSenderFactory( - self.smtpUser, self.smtpPassword, - self.fromaddr, recipients, StringIO(s), - result, contextFactory=client_factory, - requireTransportSecurity=self.useTls, - requireAuthentication=useAuth) - - reactor.connectTCP(self.relayhost, self.smtpPort, sender_factory) - - return result - - def sendMessage(self, m, recipients): - s = m.as_string() - twlog.msg("sending mail (%d bytes) to" % len(s), recipients) - return self.sendmail(s, recipients) - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/master.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/master.py deleted file mode 100644 index 8d5b7e7d..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/master.py +++ /dev/null @@ -1,475 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -from __future__ import with_statement - -import os, urllib -from cPickle import load -from twisted.python import log -from twisted.persisted import styles -from twisted.internet import defer -from twisted.application import service -from zope.interface import implements -from buildbot import config, interfaces, util -from buildbot.util import bbcollections -from buildbot.util.eventual import eventually -from buildbot.changes import changes -from buildbot.status import buildset, builder, buildrequest - -class Status(config.ReconfigurableServiceMixin, service.MultiService): - implements(interfaces.IStatus) - - def __init__(self, master): - service.MultiService.__init__(self) - self.master = master - self.botmaster = master.botmaster - self.basedir = master.basedir - self.watchers = [] - # No default limit to the log size - self.logMaxSize = None - - self._builder_observers = bbcollections.KeyedSets() - self._buildreq_observers = bbcollections.KeyedSets() - self._buildset_finished_waiters = bbcollections.KeyedSets() - self._buildset_completion_sub = None - self._buildset_sub = None - self._build_request_sub = None - self._change_sub = None - - # service management - - def startService(self): - # subscribe to the things we need to know about - self._buildset_completion_sub = \ - self.master.subscribeToBuildsetCompletions( - self._buildsetCompletionCallback) - self._buildset_sub = \ - self.master.subscribeToBuildsets( - self._buildsetCallback) - self._build_request_sub = \ - self.master.subscribeToBuildRequests( - self._buildRequestCallback) - self._change_sub = \ - self.master.subscribeToChanges( - self.changeAdded) - - return service.MultiService.startService(self) - - @defer.inlineCallbacks - def reconfigService(self, new_config): - # remove the old listeners, then add the new - for sr in list(self): - yield defer.maybeDeferred(lambda : - sr.disownServiceParent()) - - # WebStatus instances tend to "hang around" longer than we'd like - - # if there's an ongoing HTTP request, or even a connection held - # open by keepalive, then users may still be talking to an old - # WebStatus. So WebStatus objects get to keep their `master` - # attribute, but all other status objects lose theirs. And we want - # to test this without importing WebStatus, so we use name - if not sr.__class__.__name__.endswith('WebStatus'): - sr.master = None - - for sr in new_config.status: - sr.master = self.master - sr.setServiceParent(self) - - # reconfig any newly-added change sources, as well as existing - yield config.ReconfigurableServiceMixin.reconfigService(self, - new_config) - - def stopService(self): - if self._buildset_completion_sub: - self._buildset_completion_sub.unsubscribe() - self._buildset_completion_sub = None - if self._buildset_sub: - self._buildset_sub.unsubscribe() - self._buildset_sub = None - if self._build_request_sub: - self._build_request_sub.unsubscribe() - self._build_request_sub = None - if self._change_sub: - self._change_sub.unsubscribe() - self._change_sub = None - - return service.MultiService.stopService(self) - - # clean shutdown - - @property - def shuttingDown(self): - return self.botmaster.shuttingDown - - def cleanShutdown(self): - return self.botmaster.cleanShutdown() - - def cancelCleanShutdown(self): - return self.botmaster.cancelCleanShutdown() - - # methods called by our clients - - def getTitle(self): - return self.master.config.title - def getTitleURL(self): - return self.master.config.titleURL - def getBuildbotURL(self): - return self.master.config.buildbotURL - - def getStatus(self): - # some listeners expect their .parent to be a BuildMaster object, and - # use this method to get the Status object. This is documented, so for - # now keep it working. - return self - - def getMetrics(self): - return self.master.metrics - - def getURLForBuild(self, builder_name, build_number): - prefix = self.getBuildbotURL() - import urlparse - url_path = "builders/%s/builds/%d" % ( - urllib.quote(builder_name, safe=''), - build_number) - return(urlparse.urljoin(prefix,url_path)) - - def getURLForThing(self, thing): - prefix = self.getBuildbotURL() - if not prefix: - return None - if interfaces.IStatus.providedBy(thing): - return prefix - if interfaces.ISchedulerStatus.providedBy(thing): - pass - if interfaces.IBuilderStatus.providedBy(thing): - bldr = thing - return prefix + "builders/%s" % ( - urllib.quote(bldr.getName(), safe=''), - ) - if interfaces.IBuildStatus.providedBy(thing): - build = thing - bldr = build.getBuilder() - return self.getURLForBuild(bldr.getName(), build.getNumber()) - - if interfaces.IBuildStepStatus.providedBy(thing): - step = thing - build = step.getBuild() - bldr = build.getBuilder() - return prefix + "builders/%s/builds/%d/steps/%s" % ( - urllib.quote(bldr.getName(), safe=''), - build.getNumber(), - urllib.quote(step.getName(), safe='')) - # IBuildSetStatus - # IBuildRequestStatus - # ISlaveStatus - if interfaces.ISlaveStatus.providedBy(thing): - slave = thing - return prefix + "buildslaves/%s" % ( - urllib.quote(slave.getName(), safe=''), - ) - - # IStatusEvent - if interfaces.IStatusEvent.providedBy(thing): - # TODO: this is goofy, create IChange or something - if isinstance(thing, changes.Change): - change = thing - return "%schanges/%d" % (prefix, change.number) - - if interfaces.IStatusLog.providedBy(thing): - loog = thing - step = loog.getStep() - build = step.getBuild() - bldr = build.getBuilder() - - logs = step.getLogs() - for i in range(len(logs)): - if loog is logs[i]: - break - else: - return None - return prefix + "builders/%s/builds/%d/steps/%s/logs/%s" % ( - urllib.quote(bldr.getName(), safe=''), - build.getNumber(), - urllib.quote(step.getName(), safe=''), - urllib.quote(loog.getName(), safe='')) - - def getChangeSources(self): - return list(self.master.change_svc) - - def getChange(self, number): - """Get a Change object; returns a deferred""" - d = self.master.db.changes.getChange(number) - def chdict2change(chdict): - if not chdict: - return None - return changes.Change.fromChdict(self.master, chdict) - d.addCallback(chdict2change) - return d - - def getSchedulers(self): - return self.master.allSchedulers() - - def getBuilderNames(self, categories=None): - if categories == None: - # assume this is already sorted... - return self.botmaster.builderNames - - l = [] - # respect addition order - for name in self.botmaster.builderNames: - bldr = self.botmaster.builders[name] - if bldr.config.category in categories: - l.append(name) - return util.naturalSort(l) - - def getBuilder(self, name): - """ - @rtype: L{BuilderStatus} - """ - return self.botmaster.builders[name].builder_status - - def getSlaveNames(self): - return self.botmaster.slaves.keys() - - def getSlave(self, slavename): - return self.botmaster.slaves[slavename].slave_status - - def getBuildSets(self): - d = self.master.db.buildsets.getBuildsets(complete=False) - def make_status_objects(bsdicts): - return [ buildset.BuildSetStatus(bsdict, self) - for bsdict in bsdicts ] - d.addCallback(make_status_objects) - return d - - def generateFinishedBuilds(self, builders=[], branches=[], - num_builds=None, finished_before=None, - max_search=200): - - def want_builder(bn): - if builders: - return bn in builders - return True - builder_names = [bn - for bn in self.getBuilderNames() - if want_builder(bn)] - - # 'sources' is a list of generators, one for each Builder we're - # using. When the generator is exhausted, it is replaced in this list - # with None. - sources = [] - for bn in builder_names: - b = self.getBuilder(bn) - g = b.generateFinishedBuilds(branches, - finished_before=finished_before, - max_search=max_search) - sources.append(g) - - # next_build the next build from each source - next_build = [None] * len(sources) - - def refill(): - for i,g in enumerate(sources): - if next_build[i]: - # already filled - continue - if not g: - # already exhausted - continue - try: - next_build[i] = g.next() - except StopIteration: - next_build[i] = None - sources[i] = None - - got = 0 - while True: - refill() - # find the latest build among all the candidates - candidates = [(i, b, b.getTimes()[1]) - for i,b in enumerate(next_build) - if b is not None] - candidates.sort(lambda x,y: cmp(x[2], y[2])) - if not candidates: - return - - # and remove it from the list - i, build, finshed_time = candidates[-1] - next_build[i] = None - got += 1 - yield build - if num_builds is not None: - if got >= num_builds: - return - - def subscribe(self, target): - self.watchers.append(target) - for name in self.botmaster.builderNames: - self.announceNewBuilder(target, name, self.getBuilder(name)) - def unsubscribe(self, target): - self.watchers.remove(target) - - - # methods called by upstream objects - - def announceNewBuilder(self, target, name, builder_status): - t = target.builderAdded(name, builder_status) - if t: - builder_status.subscribe(t) - - def builderAdded(self, name, basedir, category=None, description=None): - """ - @rtype: L{BuilderStatus} - """ - filename = os.path.join(self.basedir, basedir, "builder") - log.msg("trying to load status pickle from %s" % filename) - builder_status = None - try: - with open(filename, "rb") as f: - builder_status = load(f) - builder_status.master = self.master - - # (bug #1068) if we need to upgrade, we probably need to rewrite - # this pickle, too. We determine this by looking at the list of - # Versioned objects that have been unpickled, and (after doUpgrade) - # checking to see if any of them set wasUpgraded. The Versioneds' - # upgradeToVersionNN methods all set this. - versioneds = styles.versionedsToUpgrade - styles.doUpgrade() - if True in [ hasattr(o, 'wasUpgraded') for o in versioneds.values() ]: - log.msg("re-writing upgraded builder pickle") - builder_status.saveYourself() - - except IOError: - log.msg("no saved status pickle, creating a new one") - except: - log.msg("error while loading status pickle, creating a new one") - log.msg("error follows:") - log.err() - if not builder_status: - builder_status = builder.BuilderStatus(name, category, self.master, - description) - builder_status.addPointEvent(["builder", "created"]) - log.msg("added builder %s in category %s" % (name, category)) - # an unpickled object might not have category set from before, - # so set it here to make sure - builder_status.category = category - builder_status.description = description - builder_status.master = self.master - builder_status.basedir = os.path.join(self.basedir, basedir) - builder_status.name = name # it might have been updated - builder_status.status = self - - if not os.path.isdir(builder_status.basedir): - os.makedirs(builder_status.basedir) - builder_status.determineNextBuildNumber() - - builder_status.setBigState("offline") - - for t in self.watchers: - self.announceNewBuilder(t, name, builder_status) - - return builder_status - - def builderRemoved(self, name): - for t in self.watchers: - if hasattr(t, 'builderRemoved'): - t.builderRemoved(name) - - def slaveConnected(self, name): - for t in self.watchers: - if hasattr(t, 'slaveConnected'): - t.slaveConnected(name) - - def slaveDisconnected(self, name): - for t in self.watchers: - if hasattr(t, 'slaveDisconnected'): - t.slaveDisconnected(name) - - def changeAdded(self, change): - for t in self.watchers: - if hasattr(t, 'changeAdded'): - t.changeAdded(change) - - def asDict(self): - result = {} - # Constant - result['title'] = self.getTitle() - result['titleURL'] = self.getTitleURL() - result['buildbotURL'] = self.getBuildbotURL() - # TODO: self.getSchedulers() - # self.getChangeSources() - return result - - def build_started(self, brid, buildername, build_status): - if brid in self._buildreq_observers: - for o in self._buildreq_observers[brid]: - eventually(o, build_status) - - def _buildrequest_subscribe(self, brid, observer): - self._buildreq_observers.add(brid, observer) - - def _buildrequest_unsubscribe(self, brid, observer): - self._buildreq_observers.discard(brid, observer) - - def _buildset_waitUntilFinished(self, bsid): - d = defer.Deferred() - self._buildset_finished_waiters.add(bsid, d) - self._maybeBuildsetFinished(bsid) - return d - - def _maybeBuildsetFinished(self, bsid): - # check bsid to see if it's successful or finished, and notify anyone - # who cares - if bsid not in self._buildset_finished_waiters: - return - d = self.master.db.buildsets.getBuildset(bsid) - def do_notifies(bsdict): - bss = buildset.BuildSetStatus(bsdict, self) - if bss.isFinished(): - for d in self._buildset_finished_waiters.pop(bsid): - eventually(d.callback, bss) - d.addCallback(do_notifies) - d.addErrback(log.err, 'while notifying for buildset finishes') - - def _builder_subscribe(self, buildername, watcher): - # should get requestSubmitted and requestCancelled - self._builder_observers.add(buildername, watcher) - - def _builder_unsubscribe(self, buildername, watcher): - self._builder_observers.discard(buildername, watcher) - - def _buildsetCallback(self, **kwargs): - bsid = kwargs['bsid'] - d = self.master.db.buildsets.getBuildset(bsid) - def do_notifies(bsdict): - bss = buildset.BuildSetStatus(bsdict, self) - for t in self.watchers: - if hasattr(t, 'buildsetSubmitted'): - t.buildsetSubmitted(bss) - d.addCallback(do_notifies) - d.addErrback(log.err, 'while notifying buildsetSubmitted') - - def _buildsetCompletionCallback(self, bsid, result): - self._maybeBuildsetFinished(bsid) - - def _buildRequestCallback(self, notif): - buildername = notif['buildername'] - if buildername in self._builder_observers: - brs = buildrequest.BuildRequestStatus(buildername, - notif['brid'], self) - for observer in self._builder_observers[buildername]: - if hasattr(observer, 'requestSubmitted'): - eventually(observer.requestSubmitted, brs) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/persistent_queue.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/persistent_queue.py deleted file mode 100644 index 0106a210..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/persistent_queue.py +++ /dev/null @@ -1,382 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -from __future__ import with_statement - - -from collections import deque -import os -import cPickle as pickle - -from zope.interface import implements, Interface - - -def ReadFile(path): - with open(path, 'rb') as f: - return f.read() - - -def WriteFile(path, buf): - with open(path, 'wb') as f: - f.write(buf) - - -class IQueue(Interface): - """Abstraction of a queue.""" - def pushItem(item): - """Adds an individual item to the end of the queue. - - Returns an item if it was overflowed.""" - - def insertBackChunk(items): - """Adds a list of items as the oldest entries. - - Normally called in case of failure to process the data, queue the data - back so it can be retrieved at a later time. - - Returns a list of items if it was overflowed.""" - - def popChunk(nbItems=None): - """Pop many items at once. Defaults to self.maxItems().""" - - def save(): - """Save the queue to storage if implemented.""" - - def items(): - """Returns items in the queue. - - Warning: Can be extremely slow for queue on disk.""" - - def nbItems(): - """Returns the number of items in the queue.""" - - def maxItems(): - """Returns the maximum number of items this queue can hold.""" - - -class MemoryQueue(object): - """Simple length bounded queue using deque. - - list.pop(0) operation is O(n) so for a 10000 items list, it can start to - be real slow. On the contrary, deque.popleft() is O(1) most of the time. - See http://docs.python.org/library/collections.html for more - information. - """ - implements(IQueue) - - def __init__(self, maxItems=None): - self._maxItems = maxItems - if self._maxItems is None: - self._maxItems = 10000 - self._items = deque() - - def pushItem(self, item): - ret = None - if len(self._items) == self._maxItems: - ret = self._items.popleft() - self._items.append(item) - return ret - - def insertBackChunk(self, chunk): - ret = None - excess = len(self._items) + len(chunk) - self._maxItems - if excess > 0: - ret = chunk[0:excess] - chunk = chunk[excess:] - self._items.extendleft(reversed(chunk)) - return ret - - def popChunk(self, nbItems=None): - if nbItems is None: - nbItems = self._maxItems - if nbItems > len(self._items): - items = list(self._items) - self._items = deque() - else: - items = [] - for i in range(nbItems): - items.append(self._items.popleft()) - return items - - def save(self): - pass - - def items(self): - return list(self._items) - - def nbItems(self): - return len(self._items) - - def maxItems(self): - return self._maxItems - - -class DiskQueue(object): - """Keeps a list of abstract items and serializes it to the disk. - - Use pickle for serialization.""" - implements(IQueue) - - def __init__(self, path, maxItems=None, pickleFn=pickle.dumps, - unpickleFn=pickle.loads): - """ - @path: directory to save the items. - @maxItems: maximum number of items to keep on disk, flush the - older ones. - @pickleFn: function used to pack the items to disk. - @unpickleFn: function used to unpack items from disk. - """ - self.path = path - self._maxItems = maxItems - if self._maxItems is None: - self._maxItems = 100000 - if not os.path.isdir(self.path): - os.mkdir(self.path) - self.pickleFn = pickleFn - self.unpickleFn = unpickleFn - - # Total number of items. - self._nbItems = 0 - # The actual items id start at one. - self.firstItemId = 0 - self.lastItemId = 0 - self._loadFromDisk() - - def pushItem(self, item): - ret = None - if self._nbItems == self._maxItems: - id = self._findNext(self.firstItemId) - path = os.path.join(self.path, str(id)) - ret = self.unpickleFn(ReadFile(path)) - os.remove(path) - self.firstItemId = id + 1 - else: - self._nbItems += 1 - self.lastItemId += 1 - path = os.path.join(self.path, str(self.lastItemId)) - if os.path.exists(path): - raise IOError('%s already exists.' % path) - WriteFile(path, self.pickleFn(item)) - return ret - - def insertBackChunk(self, chunk): - ret = None - excess = self._nbItems + len(chunk) - self._maxItems - if excess > 0: - ret = chunk[0:excess] - chunk = chunk[excess:] - for i in reversed(chunk): - self.firstItemId -= 1 - path = os.path.join(self.path, str(self.firstItemId)) - if os.path.exists(path): - raise IOError('%s already exists.' % path) - WriteFile(path, self.pickleFn(i)) - self._nbItems += 1 - return ret - - def popChunk(self, nbItems=None): - if nbItems is None: - nbItems = self._maxItems - ret = [] - for i in range(nbItems): - if self._nbItems == 0: - break - id = self._findNext(self.firstItemId) - path = os.path.join(self.path, str(id)) - ret.append(self.unpickleFn(ReadFile(path))) - os.remove(path) - self._nbItems -= 1 - self.firstItemId = id + 1 - return ret - - def save(self): - pass - - def items(self): - """Warning, very slow.""" - ret = [] - for id in range(self.firstItemId, self.lastItemId + 1): - path = os.path.join(self.path, str(id)) - if os.path.exists(path): - ret.append(self.unpickleFn(ReadFile(path))) - return ret - - def nbItems(self): - return self._nbItems - - def maxItems(self): - return self._maxItems - - #### Protected functions - - def _findNext(self, id): - while True: - path = os.path.join(self.path, str(id)) - if os.path.isfile(path): - return id - id += 1 - return None - - def _loadFromDisk(self): - """Loads the queue from disk upto self.maxMemoryItems items into - self.items. - """ - def SafeInt(item): - try: - return int(item) - except ValueError: - return None - - files = filter(None, [SafeInt(x) for x in os.listdir(self.path)]) - files.sort() - self._nbItems = len(files) - if self._nbItems: - self.firstItemId = files[0] - self.lastItemId = files[-1] - - -class PersistentQueue(object): - """Keeps a list of abstract items and serializes it to the disk. - - It has 2 layers of queue, normally an in-memory queue and an on-disk queue. - When the number of items in the primary queue gets too large, the new items - are automatically saved to the secondary queue. The older items are kept in - the primary queue. - """ - implements(IQueue) - - def __init__(self, primaryQueue=None, secondaryQueue=None, path=None): - """ - @primaryQueue: memory queue to use before buffering to disk. - @secondaryQueue: disk queue to use as permanent buffer. - @path: path is a shortcut when using default DiskQueue settings. - """ - self.primaryQueue = primaryQueue - if self.primaryQueue is None: - self.primaryQueue = MemoryQueue() - self.secondaryQueue = secondaryQueue - if self.secondaryQueue is None: - self.secondaryQueue = DiskQueue(path) - # Preload data from the secondary queue only if we know we won't start - # using the secondary queue right away. - if self.secondaryQueue.nbItems() < self.primaryQueue.maxItems(): - self.primaryQueue.insertBackChunk( - self.secondaryQueue.popChunk(self.primaryQueue.maxItems())) - - def pushItem(self, item): - # If there is already items in secondaryQueue, we'd need to pop them - # all to start inserting them into primaryQueue so don't bother and - # just push it in secondaryQueue. - if (self.secondaryQueue.nbItems() or - self.primaryQueue.nbItems() == self.primaryQueue.maxItems()): - item = self.secondaryQueue.pushItem(item) - if item is None: - return item - # If item is not None, secondaryQueue overflowed. We need to push it - # back to primaryQueue so the oldest item is dumped. - # Or everything fit in the primaryQueue. - return self.primaryQueue.pushItem(item) - - def insertBackChunk(self, chunk): - ret = None - # Overall excess - excess = self.nbItems() + len(chunk) - self.maxItems() - if excess > 0: - ret = chunk[0:excess] - chunk = chunk[excess:] - # Memory excess - excess = (self.primaryQueue.nbItems() + len(chunk) - - self.primaryQueue.maxItems()) - if excess > 0: - chunk2 = [] - for i in range(excess): - chunk2.append(self.primaryQueue.items().pop()) - chunk2.reverse() - x = self.primaryQueue.insertBackChunk(chunk) - assert x is None, "primaryQueue.insertBackChunk did not return None" - if excess > 0: - x = self.secondaryQueue.insertBackChunk(chunk2) - assert x is None, ("secondaryQueue.insertBackChunk did not return " - " None") - return ret - - def popChunk(self, nbItems=None): - if nbItems is None: - nbItems = self.primaryQueue.maxItems() - ret = self.primaryQueue.popChunk(nbItems) - nbItems -= len(ret) - if nbItems and self.secondaryQueue.nbItems(): - ret.extend(self.secondaryQueue.popChunk(nbItems)) - return ret - - def save(self): - self.secondaryQueue.insertBackChunk(self.primaryQueue.popChunk()) - - def items(self): - return self.primaryQueue.items() + self.secondaryQueue.items() - - def nbItems(self): - return self.primaryQueue.nbItems() + self.secondaryQueue.nbItems() - - def maxItems(self): - return self.primaryQueue.maxItems() + self.secondaryQueue.maxItems() - - -class IndexedQueue(object): - """Adds functionality to a IQueue object to track its usage. - - Adds a new member function getIndex() and modify popChunk() and - insertBackChunk() to keep a virtual pointer to the queue's first entry - index.""" - implements(IQueue) - - def __init__(self, queue): - # Copy all the member functions from the other object that this class - # doesn't already define. - assert IQueue.providedBy(queue) - def Filter(m): - return (m[0] != '_' and callable(getattr(queue, m)) - and not hasattr(self, m)) - for member in filter(Filter, dir(queue)): - setattr(self, member, getattr(queue, member)) - self.queue = queue - self._index = 0 - - def getIndex(self): - return self._index - - def popChunk(self, *args, **kwargs): - items = self.queue.popChunk(*args, **kwargs) - if items: - self._index += len(items) - return items - - def insertBackChunk(self, items): - self._index -= len(items) - ret = self.queue.insertBackChunk(items) - if ret: - self._index += len(ret) - return ret - - -def ToIndexedQueue(queue): - """If the IQueue wasn't already a IndexedQueue, makes it an IndexedQueue.""" - if not IQueue.providedBy(queue): - raise TypeError("queue doesn't implement IQueue", queue) - if isinstance(queue, IndexedQueue): - return queue - return IndexedQueue(queue) - -# vim: set ts=4 sts=4 sw=4 et: diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/progress.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/progress.py deleted file mode 100644 index 62aae317..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/progress.py +++ /dev/null @@ -1,324 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -from twisted.internet import reactor -from twisted.spread import pb -from twisted.python import log -from buildbot import util -from collections import defaultdict - -class StepProgress: - """I keep track of how much progress a single BuildStep has made. - - Progress is measured along various axes. Time consumed is one that is - available for all steps. Amount of command output is another, and may be - better quantified by scanning the output for markers to derive number of - files compiled, directories walked, tests run, etc. - - I am created when the build begins, and given to a BuildProgress object - so it can track the overall progress of the whole build. - - """ - - startTime = None - stopTime = None - expectedTime = None - buildProgress = None - debug = False - - def __init__(self, name, metricNames): - self.name = name - self.progress = {} - self.expectations = {} - for m in metricNames: - self.progress[m] = None - self.expectations[m] = None - - def setBuildProgress(self, bp): - self.buildProgress = bp - - def setExpectations(self, metrics): - """The step can call this to explicitly set a target value for one - of its metrics. E.g., ShellCommands knows how many commands it will - execute, so it could set the 'commands' expectation.""" - for metric, value in metrics.items(): - self.expectations[metric] = value - self.buildProgress.newExpectations() - - def setExpectedTime(self, seconds): - self.expectedTime = seconds - self.buildProgress.newExpectations() - - def start(self): - if self.debug: print "StepProgress.start[%s]" % self.name - self.startTime = util.now() - - def setProgress(self, metric, value): - """The step calls this as progress is made along various axes.""" - if self.debug: - print "setProgress[%s][%s] = %s" % (self.name, metric, value) - self.progress[metric] = value - if self.debug: - r = self.remaining() - print " step remaining:", r - self.buildProgress.newProgress() - - def finish(self): - """This stops the 'time' metric and marks the step as finished - overall. It should be called after the last .setProgress has been - done for each axis.""" - if self.debug: print "StepProgress.finish[%s]" % self.name - self.stopTime = util.now() - self.buildProgress.stepFinished(self.name) - - def totalTime(self): - if self.startTime != None and self.stopTime != None: - return self.stopTime - self.startTime - - def remaining(self): - if self.startTime == None: - return self.expectedTime - if self.stopTime != None: - return 0 # already finished - # TODO: replace this with cleverness that graphs each metric vs. - # time, then finds the inverse function. Will probably need to save - # a timestamp with each setProgress update, when finished, go back - # and find the 2% transition points, then save those 50 values in a - # list. On the next build, do linear interpolation between the two - # closest samples to come up with a percentage represented by that - # metric. - - # TODO: If no other metrics are available, just go with elapsed - # time. Given the non-time-uniformity of text output from most - # steps, this would probably be better than the text-percentage - # scheme currently implemented. - - percentages = [] - for metric, value in self.progress.items(): - expectation = self.expectations[metric] - if value != None and expectation != None: - p = 1.0 * value / expectation - percentages.append(p) - if percentages: - avg = reduce(lambda x,y: x+y, percentages) / len(percentages) - if avg > 1.0: - # overdue - avg = 1.0 - if avg < 0.0: - avg = 0.0 - if percentages and self.expectedTime != None: - return self.expectedTime - (avg * self.expectedTime) - if self.expectedTime is not None: - # fall back to pure time - return self.expectedTime - (util.now() - self.startTime) - return None # no idea - - -class WatcherState: - def __init__(self, interval): - self.interval = interval - self.timer = None - self.needUpdate = 0 - -class BuildProgress(pb.Referenceable): - """I keep track of overall build progress. I hold a list of StepProgress - objects. - """ - - def __init__(self, stepProgresses): - self.steps = {} - for s in stepProgresses: - self.steps[s.name] = s - s.setBuildProgress(self) - self.finishedSteps = [] - self.watchers = {} - self.debug = 0 - - def setExpectationsFrom(self, exp): - """Set our expectations from the builder's Expectations object.""" - for name, metrics in exp.steps.items(): - s = self.steps.get(name) - if s: - s.setExpectedTime(exp.times[name]) - s.setExpectations(exp.steps[name]) - - def newExpectations(self): - """Call this when one of the steps has changed its expectations. - This should trigger us to update our ETA value and notify any - subscribers.""" - pass # subscribers are not implemented: they just poll - - def stepFinished(self, stepname): - assert(stepname not in self.finishedSteps) - self.finishedSteps.append(stepname) - if len(self.finishedSteps) == len(self.steps.keys()): - self.sendLastUpdates() - - def newProgress(self): - r = self.remaining() - if self.debug: - print " remaining:", r - if r != None: - self.sendAllUpdates() - - def remaining(self): - # sum eta of all steps - sum = 0 - for name, step in self.steps.items(): - rem = step.remaining() - if rem == None: - return None # not sure - sum += rem - return sum - def eta(self): - left = self.remaining() - if left == None: - return None # not sure - done = util.now() + left - return done - - - def remote_subscribe(self, remote, interval=5): - # [interval, timer, needUpdate] - # don't send an update more than once per interval - self.watchers[remote] = WatcherState(interval) - remote.notifyOnDisconnect(self.removeWatcher) - self.updateWatcher(remote) - self.startTimer(remote) - log.msg("BuildProgress.remote_subscribe(%s)" % remote) - def remote_unsubscribe(self, remote): - # TODO: this doesn't work. I think 'remote' will always be different - # than the object that appeared in _subscribe. - log.msg("BuildProgress.remote_unsubscribe(%s)" % remote) - self.removeWatcher(remote) - #remote.dontNotifyOnDisconnect(self.removeWatcher) - def removeWatcher(self, remote): - #log.msg("removeWatcher(%s)" % remote) - try: - timer = self.watchers[remote].timer - if timer: - timer.cancel() - del self.watchers[remote] - except KeyError: - log.msg("Weird, removeWatcher on non-existent subscriber:", - remote) - def sendAllUpdates(self): - for r in self.watchers.keys(): - self.updateWatcher(r) - def updateWatcher(self, remote): - # an update wants to go to this watcher. Send it if we can, otherwise - # queue it for later - w = self.watchers[remote] - if not w.timer: - # no timer, so send update now and start the timer - self.sendUpdate(remote) - self.startTimer(remote) - else: - # timer is running, just mark as needing an update - w.needUpdate = 1 - def startTimer(self, remote): - w = self.watchers[remote] - timer = reactor.callLater(w.interval, self.watcherTimeout, remote) - w.timer = timer - def sendUpdate(self, remote, last=0): - self.watchers[remote].needUpdate = 0 - #text = self.asText() # TODO: not text, duh - try: - remote.callRemote("progress", self.remaining()) - if last: - remote.callRemote("finished", self) - except: - log.deferr() - self.removeWatcher(remote) - - def watcherTimeout(self, remote): - w = self.watchers.get(remote, None) - if not w: - return # went away - w.timer = None - if w.needUpdate: - self.sendUpdate(remote) - self.startTimer(remote) - def sendLastUpdates(self): - for remote in self.watchers.keys(): - self.sendUpdate(remote, 1) - self.removeWatcher(remote) - - -class Expectations: - debug = False - # decay=1.0 ignores all but the last build - # 0.9 is short time constant. 0.1 is very long time constant - # TODO: let decay be specified per-metric - decay = 0.5 - - def __init__(self, buildprogress): - """Create us from a successful build. We will expect each step to - take as long as it did in that build.""" - - # .steps maps stepname to dict2 - # dict2 maps metricname to final end-of-step value - self.steps = defaultdict(dict) - - # .times maps stepname to per-step elapsed time - self.times = {} - - for name, step in buildprogress.steps.items(): - self.steps[name] = {} - for metric, value in step.progress.items(): - self.steps[name][metric] = value - self.times[name] = None - if step.startTime is not None and step.stopTime is not None: - self.times[name] = step.stopTime - step.startTime - - def wavg(self, old, current): - if old is None: - return current - if current is None: - return old - else: - return (current * self.decay) + (old * (1 - self.decay)) - - def update(self, buildprogress): - for name, stepprogress in buildprogress.steps.items(): - old = self.times.get(name) - current = stepprogress.totalTime() - if current == None: - log.msg("Expectations.update: current[%s] was None!" % name) - continue - new = self.wavg(old, current) - self.times[name] = new - if self.debug: - print "new expected time[%s] = %s, old %s, cur %s" % \ - (name, new, old, current) - - for metric, current in stepprogress.progress.items(): - old = self.steps[name].get(metric) - new = self.wavg(old, current) - if self.debug: - print "new expectation[%s][%s] = %s, old %s, cur %s" % \ - (name, metric, new, old, current) - self.steps[name][metric] = new - - def expectedBuildTime(self): - if None in self.times.values(): - return None - #return sum(self.times.values()) - # python-2.2 doesn't have 'sum'. TODO: drop python-2.2 support - s = 0 - for v in self.times.values(): - s += v - return s diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/results.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/results.py deleted file mode 100644 index 2b012f0e..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/results.py +++ /dev/null @@ -1,25 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -SUCCESS, WARNINGS, FAILURE, SKIPPED, EXCEPTION, RETRY = range(6) -Results = ["success", "warnings", "failure", "skipped", "exception", "retry"] - -def worst_status(a, b): - # SUCCESS > WARNINGS > FAILURE > EXCEPTION > RETRY - # Retry needs to be considered the worst so that conusmers don't have to - # worry about other failures undermining the RETRY. - for s in (RETRY, EXCEPTION, FAILURE, WARNINGS, SKIPPED, SUCCESS): - if s in (a, b): - return s diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/slave.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/slave.py deleted file mode 100644 index a3c5b4ec..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/slave.py +++ /dev/null @@ -1,118 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -import time -from zope.interface import implements -from buildbot import interfaces -from buildbot.util.eventual import eventually - -class SlaveStatus: - implements(interfaces.ISlaveStatus) - - admin = None - host = None - access_uri = None - version = None - connected = False - graceful_shutdown = False - paused = False - - def __init__(self, name): - self.name = name - self._lastMessageReceived = 0 - self.runningBuilds = [] - self.graceful_callbacks = [] - self.connect_times = [] - - def getName(self): - return self.name - def getAdmin(self): - return self.admin - def getHost(self): - return self.host - def getAccessURI(self): - return self.access_uri - def getVersion(self): - return self.version - def isConnected(self): - return self.connected - def isPaused(self): - return self.paused - def lastMessageReceived(self): - return self._lastMessageReceived - def getRunningBuilds(self): - return self.runningBuilds - def getConnectCount(self): - then = time.time() - 3600 - return len([ t for t in self.connect_times if t > then ]) - - def setAdmin(self, admin): - self.admin = admin - def setHost(self, host): - self.host = host - def setAccessURI(self, access_uri): - self.access_uri = access_uri - def setVersion(self, version): - self.version = version - def setConnected(self, isConnected): - self.connected = isConnected - def setLastMessageReceived(self, when): - self._lastMessageReceived = when - def setPaused(self, isPaused): - self.paused = isPaused - - def recordConnectTime(self): - # record this connnect, and keep data for the last hour - now = time.time() - self.connect_times = [ t for t in self.connect_times if t > now - 3600 ] + [ now ] - - def buildStarted(self, build): - self.runningBuilds.append(build) - def buildFinished(self, build): - self.runningBuilds.remove(build) - - def getGraceful(self): - """Return the graceful shutdown flag""" - return self.graceful_shutdown - def setGraceful(self, graceful): - """Set the graceful shutdown flag, and notify all the watchers""" - self.graceful_shutdown = graceful - for cb in self.graceful_callbacks: - eventually(cb, graceful) - def addGracefulWatcher(self, watcher): - """Add watcher to the list of watchers to be notified when the - graceful shutdown flag is changed.""" - if not watcher in self.graceful_callbacks: - self.graceful_callbacks.append(watcher) - def removeGracefulWatcher(self, watcher): - """Remove watcher from the list of watchers to be notified when the - graceful shutdown flag is changed.""" - if watcher in self.graceful_callbacks: - self.graceful_callbacks.remove(watcher) - - def asDict(self): - result = {} - # Constant - result['name'] = self.getName() - result['access_uri'] = self.getAccessURI() - - # Transient (since it changes when the slave reconnects) - result['host'] = self.getHost() - result['admin'] = self.getAdmin() - result['version'] = self.getVersion() - result['connected'] = self.isConnected() - result['runningBuilds'] = [b.asDict() for b in self.getRunningBuilds()] - return result - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/status_gerrit.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/status_gerrit.py deleted file mode 100644 index b4925ccf..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/status_gerrit.py +++ /dev/null @@ -1,149 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -"""Push events to gerrit - -.""" - -from buildbot.status.base import StatusReceiverMultiService -from buildbot.status.builder import Results, SUCCESS, RETRY -from twisted.internet import reactor -from twisted.internet.protocol import ProcessProtocol - -def defaultReviewCB(builderName, build, result, status, arg): - if result == RETRY: - return None, 0, 0 - - message = "Buildbot finished compiling your patchset\n" - message += "on configuration: %s\n" % builderName - message += "The result is: %s\n" % Results[result].upper() - - # message, verified, reviewed - return message, (result == SUCCESS or -1), 0 - -class GerritStatusPush(StatusReceiverMultiService): - """Event streamer to a gerrit ssh server.""" - - def __init__(self, server, username, reviewCB=defaultReviewCB, - startCB=None, port=29418, reviewArg=None, - startArg=None, **kwargs): - """ - @param server: Gerrit SSH server's address to use for push event notifications. - @param username: Gerrit SSH server's username. - @param reviewCB: Callback that is called each time a build is finished, and that is used - to define the message and review approvals depending on the build result. - @param startCB: Callback that is called each time a build is started. - Used to define the message sent to Gerrit. - @param port: Gerrit SSH server's port. - @param reviewArg: Optional argument passed to the review callback. - @param startArg: Optional argument passed to the start callback. - """ - StatusReceiverMultiService.__init__(self) - # Parameters. - self.gerrit_server = server - self.gerrit_username = username - self.gerrit_port = port - self.reviewCB = reviewCB - self.reviewArg = reviewArg - self.startCB = startCB - self.startArg = startArg - - class LocalPP(ProcessProtocol): - def __init__(self, status): - self.status = status - - def outReceived(self, data): - print "gerritout:", data - - def errReceived(self, data): - print "gerriterr:", data - - def processEnded(self, status_object): - if status_object.value.exitCode: - print "gerrit status: ERROR:", status_object - else: - print "gerrit status: OK" - - def startService(self): - print """Starting up.""" - StatusReceiverMultiService.startService(self) - self.status = self.parent.getStatus() - self.status.subscribe(self) - - def builderAdded(self, name, builder): - return self # subscribe to this builder - - def buildStarted(self, builderName, build): - if self.startCB is not None: - message = self.startCB(builderName, build, self.startArg) - self.sendCodeReviews(build, message) - - def buildFinished(self, builderName, build, result): - """Do the SSH gerrit verify command to the server.""" - message, verified, reviewed = self.reviewCB(builderName, build, result, self.status, self.reviewArg) - self.sendCodeReviews(build, message, verified, reviewed) - - def sendCodeReviews(self, build, message, verified=0, reviewed=0): - if message is None: - return - - # Gerrit + Repo - downloads = build.getProperty("repo_downloads") - downloaded = build.getProperty("repo_downloaded") - if downloads is not None and downloaded is not None: - downloaded = downloaded.split(" ") - if downloads and 2 * len(downloads) == len(downloaded): - for i in range(0, len(downloads)): - try: - project, change1 = downloads[i].split(" ") - except ValueError: - return # something is wrong, abort - change2 = downloaded[2 * i] - revision = downloaded[2 * i + 1] - if change1 == change2: - self.sendCodeReview(project, revision, message, verified, reviewed) - else: - return # something is wrong, abort - return - - # Gerrit + Git - if build.getProperty("gerrit_branch") is not None: # used only to verify Gerrit source - project = build.getProperty("project") - revision = build.getProperty("got_revision") - - # review doesn't really work with multiple revisions, so let's - # just assume it's None there - if isinstance(revision, dict): - revision = None - - if project is not None and revision is not None: - self.sendCodeReview(project, revision, message, verified, reviewed) - return - - def sendCodeReview(self, project, revision, message=None, verified=0, reviewed=0): - command = ["ssh", self.gerrit_username + "@" + self.gerrit_server, "-p %d" % self.gerrit_port, - "gerrit", "review", "--project %s" % str(project)] - if message: - command.append("--message '%s'" % message.replace("'","\"")) - if verified: - command.extend(["--verified %d" % int(verified)]) - if reviewed: - command.extend(["--code-review %d" % int(reviewed)]) - command.append(str(revision)) - print command - reactor.spawnProcess(self.LocalPP(self), "ssh", command) - -# vim: set ts=4 sts=4 sw=4 et: diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/status_push.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/status_push.py deleted file mode 100644 index a3ecf5c0..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/status_push.py +++ /dev/null @@ -1,442 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -from __future__ import with_statement - - -"""Push events to an abstract receiver. - -Implements the HTTP receiver.""" - -import datetime -import os -import urllib -import urlparse - -try: - import simplejson as json - assert json -except ImportError: - import json - -from buildbot import config -from buildbot.status.base import StatusReceiverMultiService -from buildbot.status.persistent_queue import DiskQueue, IndexedQueue, \ - MemoryQueue, PersistentQueue -from buildbot.status.web.status_json import FilterOut -from twisted.internet import defer, reactor -from twisted.python import log -from twisted.web import client - - - -class StatusPush(StatusReceiverMultiService): - """Event streamer to a abstract channel. - - It uses IQueue to batch push requests and queue the data when - the receiver is down. - When a PersistentQueue object is used, the items are saved to disk on master - shutdown so they can be pushed back when the master is restarted. - """ - - def __init__(self, serverPushCb, queue=None, path=None, filter=True, - bufferDelay=1, retryDelay=5, blackList=None): - """ - @serverPushCb: callback to be used. It receives 'self' as parameter. It - should call self.queueNextServerPush() when it's done to queue the next - push. It is guaranteed that the queue is not empty when this function is - called. - @queue: a item queue that implements IQueue. - @path: path to save config. - @filter: when True (default), removes all "", None, False, [] or {} - entries. - @bufferDelay: amount of time events are queued before sending, to - reduce the number of push requests rate. This is the delay between the - end of a request to initializing a new one. - @retryDelay: amount of time between retries when no items were pushed on - last serverPushCb call. - @blackList: events that shouldn't be sent. - """ - StatusReceiverMultiService.__init__(self) - - # Parameters. - self.queue = queue - if self.queue is None: - self.queue = MemoryQueue() - self.queue = IndexedQueue(self.queue) - self.path = path - self.filter = filter - self.bufferDelay = bufferDelay - self.retryDelay = retryDelay - if not callable(serverPushCb): - raise NotImplementedError('Please pass serverPushCb parameter.') - def hookPushCb(): - # Update the index so we know if the next push succeed or not, don't - # update the value when the queue is empty. - if not self.queue.nbItems(): - return - self.lastIndex = self.queue.getIndex() - return serverPushCb(self) - self.serverPushCb = hookPushCb - self.blackList = blackList - - # Other defaults. - # IDelayedCall object that represents the next queued push. - self.task = None - self.stopped = False - self.lastIndex = -1 - self.state = {} - self.state['started'] = str(datetime.datetime.utcnow()) - self.state['next_id'] = 1 - self.state['last_id_pushed'] = 0 - # Try to load back the state. - if self.path and os.path.isdir(self.path): - state_path = os.path.join(self.path, 'state') - if os.path.isfile(state_path): - with open(state_path, 'r') as f: - self.state.update(json.load(f)) - - if self.queue.nbItems(): - # Last shutdown was not clean, don't wait to send events. - self.queueNextServerPush() - - def startService(self): - """Starting up.""" - StatusReceiverMultiService.startService(self) - self.status = self.parent.getStatus() - self.status.subscribe(self) - self.initialPush() - - def wasLastPushSuccessful(self): - """Returns if the "virtual pointer" in the queue advanced.""" - return self.lastIndex <= self.queue.getIndex() - - def queueNextServerPush(self): - """Queue the next push or call it immediately. - - Called to signal new items are available to be sent or on shutdown. - A timer should be queued to trigger a network request or the callback - should be called immediately. If a status push is already queued, ignore - the current call.""" - # Determine the delay. - if self.wasLastPushSuccessful(): - if self.stopped: - # Shutting down. - delay = 0 - else: - # Normal case. - delay = self.bufferDelay - else: - if self.stopped: - # Too bad, we can't do anything now, we're shutting down and the - # receiver is also down. We'll just save the objects to disk. - return - else: - # The server is inaccessible, retry less often. - delay = self.retryDelay - - # Cleanup a previously queued task if necessary. - if self.task: - # Warning: we could be running inside the task. - if self.task.active(): - # There was already a task queue, don't requeue it, just let it - # go. - return - else: - if self.task.active(): - # There was a task queued but it is requested to call it - # *right now* so cancel it. - self.task.cancel() - # Otherwise, it was just a stray object. - self.task = None - - # Do the queue/direct call. - if delay: - # Call in delay seconds. - self.task = reactor.callLater(delay, self.serverPushCb) - elif self.stopped: - if not self.queue.nbItems(): - return - # Call right now, we're shutting down. - @defer.inlineCallbacks - def BlockForEverythingBeingSent(): - yield self.serverPushCb() - return BlockForEverythingBeingSent() - else: - # delay should never be 0. That can cause Buildbot to spin tightly - # trying to push events that may not be received well by a status - # listener. - log.err('Did not expect delay to be 0, but it is.') - return - - def stopService(self): - """Shutting down.""" - self.finalPush() - self.stopped = True - if (self.task and self.task.active()): - # We don't have time to wait, force an immediate call. - self.task.cancel() - self.task = None - d = self.queueNextServerPush() - elif self.wasLastPushSuccessful(): - d = self.queueNextServerPush() - else: - d = defer.succeed(None) - - # We're dying, make sure we save the results. - self.queue.save() - if self.path and os.path.isdir(self.path): - state_path = os.path.join(self.path, 'state') - with open(state_path, 'w') as f: - json.dump(self.state, f, sort_keys=True, - indent=2) - # Make sure all Deferreds are called on time and in a sane order. - defers = filter(None, [d, StatusReceiverMultiService.stopService(self)]) - return defer.DeferredList(defers) - - def push(self, event, **objs): - """Push a new event. - - The new event will be either: - - Queued in memory to reduce network usage - - Queued to disk when the sink server is down - - Pushed (along the other queued items) to the server - """ - if self.blackList and event in self.blackList: - return - # First, generate the packet. - packet = {} - packet['id'] = self.state['next_id'] - self.state['next_id'] += 1 - packet['timestamp'] = str(datetime.datetime.utcnow()) - packet['project'] = self.status.getTitle() - packet['started'] = self.state['started'] - packet['event'] = event - packet['payload'] = {} - for obj_name, obj in objs.items(): - if hasattr(obj, 'asDict'): - obj = obj.asDict() - if self.filter: - obj = FilterOut(obj) - packet['payload'][obj_name] = obj - self.queue.pushItem(packet) - if self.task is None or not self.task.active(): - # No task queued since it was probably idle, let's queue a task. - return self.queueNextServerPush() - - #### Events - - def initialPush(self): - # Push everything we want to push from the initial configuration. - self.push('start', status=self.status) - - def finalPush(self): - self.push('shutdown', status=self.status) - - def requestSubmitted(self, request): - self.push('requestSubmitted', request=request) - - def requestCancelled(self, builder, request): - self.push('requestCancelled', builder=builder, request=request) - - def buildsetSubmitted(self, buildset): - self.push('buildsetSubmitted', buildset=buildset) - - def builderAdded(self, builderName, builder): - self.push('builderAdded', builderName=builderName, builder=builder) - return self - - def builderChangedState(self, builderName, state): - self.push('builderChangedState', builderName=builderName, state=state) - - def buildStarted(self, builderName, build): - self.push('buildStarted', build=build) - return self - - def buildETAUpdate(self, build, ETA): - self.push('buildETAUpdate', build=build, ETA=ETA) - - def stepStarted(self, build, step): - self.push('stepStarted', - properties=build.getProperties().asList(), - step=step) - - def stepTextChanged(self, build, step, text): - self.push('stepTextChanged', - properties=build.getProperties().asList(), - step=step, - text=text) - - def stepText2Changed(self, build, step, text2): - self.push('stepText2Changed', - properties=build.getProperties().asList(), - step=step, - text2=text2) - - def stepETAUpdate(self, build, step, ETA, expectations): - self.push('stepETAUpdate', - properties=build.getProperties().asList(), - step=step, - ETA=ETA, - expectations=expectations) - - def logStarted(self, build, step, log): - self.push('logStarted', - properties=build.getProperties().asList(), - step=step) - - def logFinished(self, build, step, log): - self.push('logFinished', - properties=build.getProperties().asList(), - step=step) - - def stepFinished(self, build, step, results): - self.push('stepFinished', - properties=build.getProperties().asList(), - step=step) - - def buildFinished(self, builderName, build, results): - self.push('buildFinished', build=build) - - def builderRemoved(self, builderName): - self.push('buildedRemoved', builderName=builderName) - - def changeAdded(self, change): - self.push('changeAdded', change=change) - - def slaveConnected(self, slavename): - self.push('slaveConnected', slave=self.status.getSlave(slavename)) - - def slaveDisconnected(self, slavename): - self.push('slaveDisconnected', slavename=slavename) - - -class HttpStatusPush(StatusPush): - """Event streamer to a HTTP server.""" - - def __init__(self, serverUrl, debug=None, maxMemoryItems=None, - maxDiskItems=None, chunkSize=200, maxHttpRequestSize=2**20, - extra_post_params=None, **kwargs): - """ - @serverUrl: Base URL to be used to push events notifications. - @maxMemoryItems: Maximum number of items to keep queued in memory. - @maxDiskItems: Maximum number of items to buffer to disk, if 0, doesn't - use disk at all. - @debug: Save the json with nice formatting. - @chunkSize: maximum number of items to send in each at each HTTP POST. - @maxHttpRequestSize: limits the size of encoded data for AE, the default - is 1MB. - """ - if not serverUrl: - raise config.ConfigErrors(['HttpStatusPush requires a serverUrl']) - - # Parameters. - self.serverUrl = serverUrl - self.extra_post_params = extra_post_params or {} - self.debug = debug - self.chunkSize = chunkSize - self.lastPushWasSuccessful = True - self.maxHttpRequestSize = maxHttpRequestSize - if maxDiskItems != 0: - # The queue directory is determined by the server url. - path = ('events_' + - urlparse.urlparse(self.serverUrl)[1].split(':')[0]) - queue = PersistentQueue( - primaryQueue=MemoryQueue(maxItems=maxMemoryItems), - secondaryQueue=DiskQueue(path, maxItems=maxDiskItems)) - else: - path = None - queue = MemoryQueue(maxItems=maxMemoryItems) - - # Use the unbounded method. - StatusPush.__init__(self, serverPushCb=HttpStatusPush.pushHttp, - queue=queue, path=path, **kwargs) - - def wasLastPushSuccessful(self): - return self.lastPushWasSuccessful - - def popChunk(self): - """Pops items from the pending list. - - They must be queued back on failure.""" - if self.wasLastPushSuccessful(): - chunkSize = self.chunkSize - else: - chunkSize = 1 - - while True: - items = self.queue.popChunk(chunkSize) - newitems = [] - for item in items: - if hasattr(item, 'asDict'): - newitems.append(item.asDict()) - else: - newitems.append(item) - if self.debug: - packets = json.dumps(newitems, indent=2, sort_keys=True) - else: - packets = json.dumps(newitems, separators=(',',':')) - params = {'packets': packets} - params.update(self.extra_post_params) - data = urllib.urlencode(params) - if (not self.maxHttpRequestSize or - len(data) < self.maxHttpRequestSize): - return (data, items) - - if chunkSize == 1: - # This packet is just too large. Drop this packet. - log.msg("ERROR: packet %s was dropped, too large: %d > %d" % - (items[0]['id'], len(data), self.maxHttpRequestSize)) - chunkSize = self.chunkSize - else: - # Try with half the packets. - chunkSize /= 2 - self.queue.insertBackChunk(items) - - def pushHttp(self): - """Do the HTTP POST to the server.""" - (encoded_packets, items) = self.popChunk() - - def Success(result): - """Queue up next push.""" - log.msg('Sent %d events to %s' % (len(items), self.serverUrl)) - self.lastPushWasSuccessful = True - return self.queueNextServerPush() - - def Failure(result): - """Insert back items not sent and queue up next push.""" - # Server is now down. - log.msg('Failed to push %d events to %s: %s' % - (len(items), self.serverUrl, str(result))) - self.queue.insertBackChunk(items) - if self.stopped: - # Bad timing, was being called on shutdown and the server died - # on us. Make sure the queue is saved since we just queued back - # items. - self.queue.save() - self.lastPushWasSuccessful = False - return self.queueNextServerPush() - - # Trigger the HTTP POST request. - headers = {'Content-Type': 'application/x-www-form-urlencoded'} - connection = client.getPage(self.serverUrl, - method='POST', - postdata=encoded_packets, - headers=headers, - agent='buildbot') - connection.addCallbacks(Success, Failure) - return connection - -# vim: set ts=4 sts=4 sw=4 et: diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/testresult.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/testresult.py deleted file mode 100644 index 20002405..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/testresult.py +++ /dev/null @@ -1,39 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -from zope.interface import implements -from buildbot import interfaces - -class TestResult: - implements(interfaces.ITestResult) - - def __init__(self, name, results, text, logs): - assert isinstance(name, tuple) - self.name = name - self.results = results - self.text = text - self.logs = logs - - def getName(self): - return self.name - - def getResults(self): - return self.results - - def getText(self): - return self.text - - def getLogs(self): - return self.logs diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/tinderbox.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/tinderbox.py deleted file mode 100644 index 1694c243..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/tinderbox.py +++ /dev/null @@ -1,276 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -from email.Message import Message -from email.Utils import formatdate - -from zope.interface import implements -from twisted.internet import defer - -from buildbot import interfaces -from buildbot.status import mail -from buildbot.status.results import SUCCESS, WARNINGS, EXCEPTION, RETRY -from buildbot.steps.shell import WithProperties - -import gzip, bz2, base64, re, cStringIO - -# TODO: docs, maybe a test of some sort just to make sure it actually imports -# and can format email without raising an exception. - -class TinderboxMailNotifier(mail.MailNotifier): - """This is a Tinderbox status notifier. It can send e-mail to a number of - different tinderboxes or people. E-mails are sent at the beginning and - upon completion of each build. It can be configured to send out e-mails - for only certain builds. - - The most basic usage is as follows:: - TinderboxMailNotifier(fromaddr="buildbot@localhost", - tree="MyTinderboxTree", - extraRecipients=["tinderboxdaemon@host.org"]) - - The builder name (as specified in master.cfg) is used as the "build" - tinderbox option. - - """ - implements(interfaces.IEmailSender) - - compare_attrs = ["extraRecipients", "fromaddr", "categories", "builders", - "addLogs", "relayhost", "subject", "binaryURL", "tree", - "logCompression", "errorparser", "columnName", - "useChangeTime"] - - def __init__(self, fromaddr, tree, extraRecipients, - categories=None, builders=None, relayhost="localhost", - subject="buildbot %(result)s in %(builder)s", binaryURL="", - logCompression="", errorparser="unix", columnName=None, - useChangeTime=False): - """ - @type fromaddr: string - @param fromaddr: the email address to be used in the 'From' header. - - @type tree: string - @param tree: The Tinderbox tree to post to. - When tree is a WithProperties instance it will be - interpolated as such. See WithProperties for more detail - - @type extraRecipients: tuple of string - @param extraRecipients: E-mail addresses of recipients. This should at - least include the tinderbox daemon. - - @type categories: list of strings - @param categories: a list of category names to serve status - information for. Defaults to None (all - categories). Use either builders or categories, - but not both. - - @type builders: list of strings - @param builders: a list of builder names for which mail should be - sent. Defaults to None (send mail for all builds). - Use either builders or categories, but not both. - - @type relayhost: string - @param relayhost: the host to which the outbound SMTP connection - should be made. Defaults to 'localhost' - - @type subject: string - @param subject: a string to be used as the subject line of the message. - %(builder)s will be replaced with the name of the - %builder which provoked the message. - This parameter is not significant for the tinderbox - daemon. - - @type binaryURL: string - @param binaryURL: If specified, this should be the location where final - binary for a build is located. - (ie. http://www.myproject.org/nightly/08-08-2006.tgz) - It will be posted to the Tinderbox. - - @type logCompression: string - @param logCompression: The type of compression to use on the log. - Valid options are"bzip2" and "gzip". gzip is - only known to work on Python 2.4 and above. - - @type errorparser: string - @param errorparser: The error parser that the Tinderbox server - should use when scanning the log file. - Default is "unix". - - @type columnName: string - @param columnName: When columnName is None, use the buildername as - the Tinderbox column name. When columnName is a - string this exact string will be used for all - builders that this TinderboxMailNotifier cares - about (not recommended). When columnName is a - WithProperties instance it will be interpolated - as such. See WithProperties for more detail. - @type useChangeTime: bool - @param useChangeTime: When True, the time of the first Change for a - build is used as the builddate. When False, - the current time is used as the builddate. - """ - - mail.MailNotifier.__init__(self, fromaddr, categories=categories, - builders=builders, relayhost=relayhost, - subject=subject, - extraRecipients=extraRecipients, - sendToInterestedUsers=False) - assert isinstance(tree, basestring) \ - or isinstance(tree, WithProperties), \ - "tree must be a string or a WithProperties instance" - self.tree = tree - self.binaryURL = binaryURL - self.logCompression = logCompression - self.errorparser = errorparser - self.useChangeTime = useChangeTime - assert columnName is None or type(columnName) is str \ - or isinstance(columnName, WithProperties), \ - "columnName must be None, a string, or a WithProperties instance" - self.columnName = columnName - - def buildStarted(self, name, build): - builder = build.getBuilder() - if self.builders is not None and name not in self.builders: - return # ignore this Build - if self.categories is not None and \ - builder.category not in self.categories: - return # ignore this build - self.buildMessage(name, build, "building") - - @defer.inlineCallbacks - def buildMessage(self, name, build, results): - text = "" - res = "" - # shortform - t = "tinderbox:" - - tree = yield build.render(self.tree) - text += "%s tree: %s\n" % (t, tree) - - # the start time - # getTimes() returns a fractioned time that tinderbox doesn't understand - builddate = int(build.getTimes()[0]) - # attempt to pull a Change time from this Build's Changes. - # if that doesn't work, fall back on the current time - if self.useChangeTime: - try: - builddate = build.getChanges()[-1].when - except: - pass - text += "%s builddate: %s\n" % (t, builddate) - text += "%s status: " % t - - if results == "building": - res = "building" - text += res - elif results == SUCCESS: - res = "success" - text += res - elif results == WARNINGS: - res = "testfailed" - text += res - elif results in (EXCEPTION, RETRY): - res = "exception" - text += res - else: - res += "busted" - text += res - - text += "\n"; - - if self.columnName is None: - # use the builder name - text += "%s build: %s\n" % (t, name) - else: - columnName = yield build.render(self.columnName) - text += "%s build: %s\n" % (t, columnName) - text += "%s errorparser: %s\n" % (t, self.errorparser) - - # if the build just started... - if results == "building": - text += "%s END\n" % t - # if the build finished... - else: - text += "%s binaryurl: %s\n" % (t, self.binaryURL) - text += "%s logcompression: %s\n" % (t, self.logCompression) - - # logs will always be appended - logEncoding = "" - tinderboxLogs = "" - for bs in build.getSteps(): - # Make sure that shortText is a regular string, so that bad - # data in the logs don't generate UnicodeDecodeErrors - shortText = "%s\n" % ' '.join(bs.getText()).encode('ascii', 'replace') - - # ignore steps that haven't happened - if not re.match(".*[^\s].*", shortText): - continue - # we ignore TinderboxPrint's here so we can do things like: - # ShellCommand(command=['echo', 'TinderboxPrint:', ...]) - if re.match(".+TinderboxPrint.*", shortText): - shortText = shortText.replace("TinderboxPrint", - "Tinderbox Print") - logs = bs.getLogs() - - tinderboxLogs += "======== BuildStep started ========\n" - tinderboxLogs += shortText - tinderboxLogs += "=== Output ===\n" - for log in logs: - logText = log.getTextWithHeaders() - # Because we pull in the log headers here we have to ignore - # some of them. Basically, if we're TinderboxPrint'ing in - # a ShellCommand, the only valid one(s) are at the start - # of a line. The others are prendeded by whitespace, quotes, - # or brackets/parentheses - for line in logText.splitlines(): - if re.match(".+TinderboxPrint.*", line): - line = line.replace("TinderboxPrint", - "Tinderbox Print") - tinderboxLogs += line + "\n" - - tinderboxLogs += "=== Output ended ===\n" - tinderboxLogs += "======== BuildStep ended ========\n" - - if self.logCompression == "bzip2": - cLog = bz2.compress(tinderboxLogs) - tinderboxLogs = base64.encodestring(cLog) - logEncoding = "base64" - elif self.logCompression == "gzip": - cLog = cStringIO.StringIO() - gz = gzip.GzipFile(mode="w", fileobj=cLog) - gz.write(tinderboxLogs) - gz.close() - cLog = cLog.getvalue() - tinderboxLogs = base64.encodestring(cLog) - logEncoding = "base64" - - text += "%s logencoding: %s\n" % (t, logEncoding) - text += "%s END\n\n" % t - text += tinderboxLogs - text += "\n" - - m = Message() - m.set_payload(text) - - m['Date'] = formatdate(localtime=True) - m['Subject'] = self.subject % { 'result': res, - 'builder': name, - } - m['From'] = self.fromaddr - # m['To'] is added later - - d = defer.DeferredList([]) - d.addCallback(self._gotRecipients, self.extraRecipients, m) - defer.returnValue((yield d)) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/__init__.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/__init__.py +++ /dev/null diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/about.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/about.py deleted file mode 100644 index 0a5e068b..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/about.py +++ /dev/null @@ -1,35 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -from buildbot.status.web.base import HtmlResource -import buildbot -import twisted -import sys -import jinja2 - -class AboutBuildbot(HtmlResource): - pageTitle = "About this Buildbot" - - def content(self, request, cxt): - cxt.update(dict(buildbot=buildbot.version, - twisted=twisted.__version__, - jinja=jinja2.__version__, - python=sys.version, - platform=sys.platform)) - - template = request.site.buildbot_service.templates.get_template("about.html") - template.autoescape = True - return template.render(**cxt) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/auth.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/auth.py deleted file mode 100644 index e9b168c9..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/auth.py +++ /dev/null @@ -1,220 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -import os -from zope.interface import Interface, Attribute, implements -from buildbot.status.web.base import HtmlResource, ActionResource -from buildbot.status.web.base import path_to_authfail - -from buildbot.process.users import users - -class IAuth(Interface): - """ - Represent an authentication method. - - Note that each IAuth instance contains a link to the BuildMaster that - will be set once the IAuth instance is initialized. - """ - - master = Attribute('master', "Link to BuildMaster, set when initialized") - - def authenticate(self, user, passwd): - """Check whether C{user} / C{passwd} are valid.""" - - def getUserInfo(self, user): - """return dict with user info. - dict( fullName="", email="", groups=[]) - """ - - def errmsg(self): - """Get the reason authentication failed.""" - -class AuthBase: - master = None # set in status.web.baseweb - err = "" - - def errmsg(self): - return self.err - - def getUserInfo(self, user): - """default dummy impl""" - return dict(userName=user, fullName=user, email=user+"@localhost", groups=[ user ]) - -class BasicAuth(AuthBase): - implements(IAuth) - """Implement basic authentication against a list of user/passwd.""" - - userpass = [] - """List of user/pass tuples.""" - - def __init__(self, userpass): - """C{userpass} is a list of (user, passwd).""" - for item in userpass: - assert isinstance(item, tuple) or isinstance(item, list) - u, p = item - assert isinstance(u, str) - assert isinstance(p, str) - self.userpass = userpass - - def authenticate(self, user, passwd): - """Check that C{user}/C{passwd} is a valid user/pass tuple.""" - if not self.userpass: - self.err = "Bad self.userpass data" - return False - for u, p in self.userpass: - if user == u and passwd == p: - self.err = "" - return True - self.err = "Invalid username or password" - return False - -class HTPasswdAuth(AuthBase): - implements(IAuth) - """Implement authentication against an .htpasswd file.""" - - file = "" - """Path to the .htpasswd file to use.""" - - def __init__(self, file): - """C{file} is a path to an .htpasswd file.""" - assert os.path.exists(file) - self.file = file - - def authenticate(self, user, passwd): - """Authenticate C{user} and C{passwd} against an .htpasswd file""" - if not os.path.exists(self.file): - self.err = "No such file: " + self.file - return False - # Fetch each line from the .htpasswd file and split it into a - # [user, passwd] array. - lines = [l.rstrip().split(':', 1) - for l in file(self.file).readlines()] - # Keep only the line for this login - lines = [l for l in lines if l[0] == user] - if not lines: - self.err = "Invalid user/passwd" - return False - hash = lines[0][1] - res = self.validatePassword(passwd, hash) - if res: - self.err = "" - else: - self.err = "Invalid user/passwd" - return res - - def validatePassword(self, passwd, hash): - # This is the DES-hash of the password. The first two characters are - # the salt used to introduce disorder in the DES algorithm. - from crypt import crypt #@UnresolvedImport - return hash == crypt(passwd, hash[0:2]) - - -class HTPasswdAprAuth(HTPasswdAuth): - implements(IAuth) - """Implement authentication against an .htpasswd file based on libaprutil""" - - file = "" - """Path to the .htpasswd file to use.""" - - def __init__(self, file): - HTPasswdAuth.__init__(self, file) - - # Try to load libaprutil throug ctypes - self.apr = None - try: - from ctypes import CDLL - from ctypes.util import find_library - lib = find_library("aprutil-1") - if lib: - self.apr = CDLL(lib) - except: - self.apr = None - - def validatePassword(self, passwd, hash): - # Use apr_password_validate from libaprutil if libaprutil is available. - # Fallback to DES only checking from HTPasswdAuth - if self.apr: - return self.apr.apr_password_validate(passwd, hash) == 0 - else: - return HTPasswdAuth.validatePassword(self, passwd, hash) - -class UsersAuth(AuthBase): - """Implement authentication against users in database""" - implements(IAuth) - - def authenticate(self, user, passwd): - """ - It checks for a matching uid in the database for the credentials - and return True if a match is found, False otherwise. - - @param user: username portion of user credentials - @type user: string - - @param passwd: password portion of user credentials - @type passwd: string - - @returns: boolean via deferred. - """ - d = self.master.db.users.getUserByUsername(user) - def check_creds(user): - if user: - if users.check_passwd(passwd, user['bb_password']): - return True - self.err = "no user found with those credentials" - return False - d.addCallback(check_creds) - return d - -class AuthFailResource(HtmlResource): - pageTitle = "Authentication Failed" - - def content(self, request, cxt): - templates =request.site.buildbot_service.templates - template = templates.get_template("authfail.html") - return template.render(**cxt) - -class AuthzFailResource(HtmlResource): - pageTitle = "Authorization Failed" - - def content(self, request, cxt): - templates =request.site.buildbot_service.templates - template = templates.get_template("authzfail.html") - return template.render(**cxt) - -class LoginResource(ActionResource): - - def performAction(self, request): - authz = self.getAuthz(request) - d = authz.login(request) - def on_login(res): - if res: - status = request.site.buildbot_service.master.status - root = status.getBuildbotURL() - return request.requestHeaders.getRawHeaders('referer', - [root])[0] - else: - return path_to_authfail(request) - d.addBoth(on_login) - return d - -class LogoutResource(ActionResource): - - def performAction(self, request): - authz = self.getAuthz(request) - authz.logout(request) - status = request.site.buildbot_service.master.status - root = status.getBuildbotURL() - return request.requestHeaders.getRawHeaders('referer',[root])[0] diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/authz.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/authz.py deleted file mode 100644 index dace7f23..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/authz.py +++ /dev/null @@ -1,188 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -from twisted.internet import defer -from buildbot.status.web.auth import IAuth -from buildbot.status.web.session import SessionManager - -COOKIE_KEY="BuildBotSession" -class Authz(object): - """Decide who can do what.""" - - knownActions = [ - # If you add a new action here, be sure to also update the documentation - # at docs/manual/cfg-statustargets.rst. - 'view', - 'gracefulShutdown', - 'forceBuild', - 'forceAllBuilds', - 'pingBuilder', - 'stopBuild', - 'stopAllBuilds', - 'cancelPendingBuild', - 'cancelAllPendingBuilds', - 'stopChange', - 'cleanShutdown', - 'showUsersPage', - 'pauseSlave', - ] - - def __init__(self, - default_action=False, - auth=None, - useHttpHeader=False, - httpLoginUrl=False, - view=True, - **kwargs): - self.auth = auth - if auth: - assert IAuth.providedBy(auth) - - self.useHttpHeader = useHttpHeader - self.httpLoginUrl = httpLoginUrl - - self.config = dict((a, default_action) for a in self.knownActions) - self.config['view'] = view - for act in self.knownActions: - if act in kwargs: - self.config[act] = kwargs[act] - del kwargs[act] - - self.sessions = SessionManager() - if kwargs: - raise ValueError("unknown authorization action(s) " + ", ".join(kwargs.keys())) - - def session(self, request): - if COOKIE_KEY in request.received_cookies: - cookie = request.received_cookies[COOKIE_KEY] - return self.sessions.get(cookie) - return None - - def authenticated(self, request): - if self.useHttpHeader: - return request.getUser() != '' - return self.session(request) is not None - - def getUserInfo(self, user): - if self.useHttpHeader: - return dict(userName=user, fullName=user, email=user, groups=[user]) - s = self.sessions.getUser(user) - if s: - return s.infos - - def getUsername(self, request): - """Get the userid of the user""" - if self.useHttpHeader: - return request.getUser() - s = self.session(request) - if s: - return s.user - return request.args.get("username", ["<unknown>"])[0] - - def getUsernameHTML(self, request): - """Get the user formatated in html (with possible link to email)""" - if self.useHttpHeader: - return request.getUser() - s = self.session(request) - if s: - return s.userInfosHTML() - return "not authenticated?!" - - def getUsernameFull(self, request): - """Get the full username as fullname <email>""" - if self.useHttpHeader: - return request.getUser() - s = self.session(request) - if s: - return "%(fullName)s <%(email)s>" % (s.infos) - else: - return request.args.get("username", ["<unknown>"])[0] - - - def getPassword(self, request): - if self.useHttpHeader: - return request.getPassword() - return request.args.get("passwd", ["<no-password>"])[0] - - def advertiseAction(self, action, request): - """Should the web interface even show the form for ACTION?""" - if action not in self.knownActions: - raise KeyError("unknown action") - cfg = self.config.get(action, False) - if cfg: - if cfg == 'auth' or callable(cfg): - return self.authenticated(request) - return cfg - - def actionAllowed(self, action, request, *args): - """Is this ACTION allowed, given this http REQUEST?""" - if action not in self.knownActions: - raise KeyError("unknown action") - cfg = self.config.get(action, False) - if cfg: - if cfg == 'auth' or callable(cfg): - if not self.auth: - return defer.succeed(False) - def check_authenticate(res): - if callable(cfg) and not cfg(self.getUsername(request), *args): - return False - return True - # retain old behaviour, if people have scripts - # without cookie support - passwd = self.getPassword(request) - if self.authenticated(request): - return defer.succeed(check_authenticate(None)) - elif passwd != "<no-password>": - def check_login(cookie): - ret = False - if isinstance(cookie, str): - ret = check_authenticate(None) - self.sessions.remove(cookie) - return ret - d = self.login(request) - d.addBoth(check_login) - return d - else: - return defer.succeed(False) - return defer.succeed(cfg) - - def login(self, request): - """Login one user, and return session cookie""" - if self.authenticated(request): - return defer.succeed(False) - - user = request.args.get("username", ["<unknown>"])[0] - passwd = request.args.get("passwd", ["<no-password>"])[0] - if user == "<unknown>" or passwd == "<no-password>": - return defer.succeed(False) - if not self.auth: - return defer.succeed(False) - d = defer.maybeDeferred(self.auth.authenticate, user, passwd) - - def check_authenticate(res): - if res: - cookie, s = self.sessions.new(user, self.auth.getUserInfo(user)) - request.addCookie(COOKIE_KEY, cookie, expires=s.getExpiration(), path="/") - request.received_cookies = {COOKIE_KEY: cookie} - return cookie - else: - return False - d.addBoth(check_authenticate) - return d - - def logout(self, request): - if COOKIE_KEY in request.received_cookies: - cookie = request.received_cookies[COOKIE_KEY] - self.sessions.remove(cookie) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/base.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/base.py deleted file mode 100644 index 47a3f344..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/base.py +++ /dev/null @@ -1,806 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -import urlparse, urllib, time, re -import os, cgi, sys, locale -import jinja2 -from zope.interface import Interface -from twisted.internet import defer -from twisted.web import resource, static, server -from twisted.python import log -from buildbot.status import builder, buildstep, build -from buildbot.status.results import SUCCESS, WARNINGS, FAILURE, SKIPPED -from buildbot.status.results import EXCEPTION, RETRY -from buildbot import version, util -from buildbot.process.properties import Properties - -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.""" - 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.""" - 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.""" - def getBox(self, request): - """Return a Box instance, which wraps an Event and can produce a <td> - cell. - """ - -class IHTMLLog(Interface): - pass - -css_classes = {SUCCESS: "success", - WARNINGS: "warnings", - FAILURE: "failure", - SKIPPED: "skipped", - EXCEPTION: "exception", - RETRY: "retry", - None: "", - } - - -def getAndCheckProperties(req): - """ - Fetch custom build properties from the HTTP request of a "Force build" or - "Resubmit build" HTML form. - Check the names for valid strings, and return None if a problem is found. - Return a new Properties object containing each property found in req. - """ - master = req.site.buildbot_service.master - pname_validate = master.config.validation['property_name'] - pval_validate = master.config.validation['property_value'] - properties = Properties() - i = 1 - while True: - pname = req.args.get("property%dname" % i, [""])[0] - pvalue = req.args.get("property%dvalue" % i, [""])[0] - if not pname: - break - if not pname_validate.match(pname) \ - or not pval_validate.match(pvalue): - log.msg("bad property name='%s', value='%s'" % (pname, pvalue)) - return None - properties.setProperty(pname, pvalue, "Force Build Form") - i = i + 1 - - return properties - -def build_get_class(b): - """ - Return the class to use for a finished build or buildstep, - based on the result. - """ - # FIXME: this getResults duplicity might need to be fixed - result = b.getResults() - if isinstance(b, build.BuildStatus): - result = b.getResults() - elif isinstance(b, buildstep.BuildStepStatus): - result = b.getResults()[0] - # after forcing a build, b.getResults() returns ((None, []), []), ugh - if isinstance(result, tuple): - result = result[0] - else: - raise TypeError, "%r is not a BuildStatus or BuildStepStatus" % b - - if result == None: - # FIXME: this happens when a buildstep is running ? - 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 if segs else './' - return root - -def path_to_authfail(request): - return path_to_root(request) + "authfail" - -def path_to_authzfail(request): - return path_to_root(request) + "authzfail" - -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='')) - -def path_to_slave(request, slave): - return (path_to_root(request) + - "buildslaves/" + - urllib.quote(slave.getName(), safe='')) - -def path_to_change(request, change): - return (path_to_root(request) + - "changes/%s" % change.number) - -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. - # Events don't know about HTML. - spacer = False - def __init__(self, text=[], class_=None, urlbase=None, - **parms): - self.text = text - self.class_ = class_ - self.urlbase = urlbase - self.show_idle = 0 - if parms.has_key('show_idle'): - del parms['show_idle'] - self.show_idle = 1 - - self.parms = parms - # parms is a dict of HTML parameters for the <td> element that will - # represent this Event in the waterfall display. - - def td(self, **props): - props.update(self.parms) - text = self.text - if not text and self.show_idle: - text = ["[idle]"] - props['class'] = self.class_ - props['text'] = text; - return props - - -class AccessorMixin(object): - def getStatus(self, request): - return request.site.buildbot_service.getStatus() - - def getPageTitle(self, request): - return self.pageTitle - - def getAuthz(self, request): - return request.site.buildbot_service.authz - - def getBuildmaster(self, request): - return request.site.buildbot_service.master - - -class ContextMixin(AccessorMixin): - def getContext(self, request): - status = self.getStatus(request) - rootpath = path_to_root(request) - locale_enc = locale.getdefaultlocale()[1] - if locale_enc is not None: - locale_tz = unicode(time.tzname[time.localtime()[-1]], locale_enc) - else: - locale_tz = unicode(time.tzname[time.localtime()[-1]]) - return dict(title_url = status.getTitleURL(), - title = status.getTitle(), - stylesheet = rootpath + 'default.css', - path_to_root = rootpath, - version = version, - time = time.strftime("%a %d %b %Y %H:%M:%S", - time.localtime(util.now())), - tz = locale_tz, - metatags = [], - pageTitle = self.getPageTitle(request), - welcomeurl = rootpath, - authz = self.getAuthz(request), - request = request, - alert_msg = request.args.get("alert_msg", [""])[0], - ) - - -class ActionResource(resource.Resource, AccessorMixin): - """A resource that performs some action, then redirects to a new URL.""" - - isLeaf = 1 - - def getChild(self, name, request): - return self - - def performAction(self, request): - """ - Perform the action, and return the URL to redirect to - - @param request: the web request - @returns: URL via Deferred - can also return (URL, alert_msg) to display simple - feedback to user in case of failure - """ - - def render(self, request): - d = defer.maybeDeferred(lambda : self.performAction(request)) - def redirect(url): - if isinstance(url, tuple): - url, alert_msg = url - if alert_msg: - url += "?alert_msg="+urllib.quote(alert_msg, safe='') - request.redirect(url) - request.write("see <a href='%s'>%s</a>" % (url,url)) - try: - request.finish() - except RuntimeError: - # this occurs when the client has already disconnected; ignore - # it (see #2027) - log.msg("http client disconnected before results were sent") - d.addCallback(redirect) - - def fail(f): - request.processingFailed(f) - return None # processingFailed will log this for us - d.addErrback(fail) - return server.NOT_DONE_YET - -class HtmlResource(resource.Resource, ContextMixin): - # this is a cheap sort of template thingy - contentType = "text/html; charset=utf-8" - pageTitle = "Buildbot" - addSlash = False # adapted from Nevow - - def getChild(self, path, request): - if self.addSlash and path == "" and len(request.postpath) == 0: - return self - return resource.Resource.getChild(self, path, request) - - - def content(self, req, context): - """ - Generate content using the standard layout and the result of the C{body} - method. - - This is suitable for the case where a resource just wants to generate - the body of a page. It depends on another method, C{body}, being - defined to accept the request object and return a C{str}. C{render} - will call this method and to generate the response body. - """ - body = self.body(req) - context['content'] = body - template = req.site.buildbot_service.templates.get_template( - "empty.html") - return template.render(**context) - - - def render(self, request): - # tell the WebStatus about the HTTPChannel that got opened, so they - # can close it if we get reconfigured and the WebStatus goes away. - # They keep a weakref to this, since chances are good that it will be - # closed by the browser or by us before we get reconfigured. See - # ticket #102 for details. - if hasattr(request, "channel"): - # web.distrib.Request has no .channel - request.site.buildbot_service.registerChannel(request.channel) - - # 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] + "/" - if query: - new_url += "?" + query - request.redirect(new_url) - return '' - - ctx = self.getContext(request) - - d = defer.maybeDeferred(lambda : self.content(request, ctx)) - def handle(data): - request.setHeader("X-Clacks-Overhead", 'GNU N%C3%B3ir%C3%ADn Plunkett') - if isinstance(data, unicode): - data = data.encode("utf-8") - request.setHeader("content-type", self.contentType) - if request.method == "HEAD": - request.setHeader("content-length", len(data)) - return '' - return data - d.addCallback(handle) - def ok(data): - request.write(data) - try: - request.finish() - except RuntimeError: - # this occurs when the client has already disconnected; ignore - # it (see #2027) - log.msg("http client disconnected before results were sent") - def fail(f): - request.processingFailed(f) - return None # processingFailed will log this for us - d.addCallbacks(ok, fail) - return server.NOT_DONE_YET - -class StaticHTML(HtmlResource): - def __init__(self, body, pageTitle): - HtmlResource.__init__(self) - self.bodyHTML = body - self.pageTitle = pageTitle - def content(self, request, cxt): - cxt['content'] = self.bodyHTML - cxt['pageTitle'] = self.pageTitle - template = request.site.buildbot_service.templates.get_template("empty.html") - return template.render(**cxt) - -class DirectoryLister(static.DirectoryLister, ContextMixin): - """This variant of the static.DirectoryLister uses a template - for rendering.""" - - pageTitle = 'BuildBot' - - def render(self, request): - cxt = self.getContext(request) - - if self.dirs is None: - directory = os.listdir(self.path) - directory.sort() - else: - directory = self.dirs - - dirs, files = self._getFilesAndDirectories(directory) - - cxt['path'] = cgi.escape(urllib.unquote(request.uri)) - cxt['directories'] = dirs - cxt['files'] = files - template = request.site.buildbot_service.templates.get_template("directory.html") - data = template.render(**cxt) - if isinstance(data, unicode): - data = data.encode("utf-8") - return data - -class StaticFile(static.File): - """This class adds support for templated directory - views.""" - - def directoryListing(self): - return DirectoryLister(self.path, - self.listNames(), - self.contentTypes, - self.contentEncodings, - self.defaultType) - - -MINUTE = 60 -HOUR = 60*MINUTE -DAY = 24*HOUR -WEEK = 7*DAY -MONTH = 30*DAY - -def plural(word, words, num): - if int(num) == 1: - return "%d %s" % (num, word) - else: - return "%d %s" % (num, words) - -def abbreviate_age(age): - if age <= 90: - return "%s ago" % plural("second", "seconds", age) - if age < 90*MINUTE: - return "about %s ago" % plural("minute", "minutes", age / MINUTE) - if age < DAY: - return "about %s ago" % plural("hour", "hours", age / HOUR) - if age < 2*WEEK: - return "about %s ago" % plural("day", "days", age / DAY) - if age < 2*MONTH: - return "about %s ago" % plural("week", "weeks", age / WEEK) - return "a long time ago" - - -class BuildLineMixin: - LINE_TIME_FORMAT = "%b %d %H:%M" - - def get_line_values(self, req, build, include_builder=True): - ''' - Collect the data needed for each line display - ''' - builder_name = build.getBuilder().getName() - results = build.getResults() - text = build.getText() - all_got_revision = build.getAllGotRevisions() - css_class = css_classes.get(results, "") - ss_list = build.getSourceStamps() - if ss_list: - repo = ss_list[0].repository - if all_got_revision: - if len(ss_list) == 1: - rev = all_got_revision.get(ss_list[0].codebase, "??") - else: - rev = "multiple rev." - else: - rev = "??" - else: - repo = 'unknown, no information in build' - rev = 'unknown' - - if type(text) == list: - text = " ".join(text) - - values = {'class': css_class, - 'builder_name': builder_name, - 'buildnum': build.getNumber(), - 'results': css_class, - 'text': " ".join(build.getText()), - 'buildurl': path_to_build(req, build), - 'builderurl': path_to_builder(req, build.getBuilder()), - 'rev': rev, - 'rev_repo' : repo, - 'time': time.strftime(self.LINE_TIME_FORMAT, - time.localtime(build.getTimes()[0])), - 'text': text, - 'include_builder': include_builder - } - return values - -def map_branches(branches): - # when the query args say "trunk", present that to things like - # IBuilderStatus.generateFinishedBuilds as None, since that's the - # convention in use. But also include 'trunk', because some VC systems - # refer to it that way. In the long run we should clean this up better, - # maybe with Branch objects or something. - if "trunk" in branches: - return branches + [None] - return branches - - -# jinja utilities - -def createJinjaEnv(revlink=None, changecommentlink=None, - repositories=None, projects=None, jinja_loaders=None): - ''' Create a jinja environment changecommentlink is used to - render HTML in the WebStatus and for mail changes - - @type changecommentlink: C{None}, tuple (2 or 3 strings), dict (string -> 2- or 3-tuple) or callable - @param changecommentlink: see changelinkfilter() - - @type revlink: C{None}, format-string, dict (repository -> format string) or callable - @param revlink: see revlinkfilter() - - @type repositories: C{None} or dict (string -> url) - @param repositories: an (optinal) mapping from repository identifiers - (as given by Change sources) to URLs. Is used to create a link - on every place where a repository is listed in the WebStatus. - - @type projects: C{None} or dict (string -> url) - @param projects: similar to repositories, but for projects. - ''' - - # See http://buildbot.net/trac/ticket/658 - assert not hasattr(sys, "frozen"), 'Frozen config not supported with jinja (yet)' - - all_loaders = [jinja2.FileSystemLoader(os.path.join(os.getcwd(), 'templates'))] - if jinja_loaders: - all_loaders.extend(jinja_loaders) - all_loaders.append(jinja2.PackageLoader('buildbot.status.web', 'templates')) - loader = jinja2.ChoiceLoader(all_loaders) - - env = jinja2.Environment(loader=loader, - extensions=['jinja2.ext.i18n'], - trim_blocks=True, - undefined=AlmostStrictUndefined) - - env.install_null_translations() # needed until we have a proper i18n backend - - env.tests['mapping'] = lambda obj : isinstance(obj, dict) - - env.filters.update(dict( - urlencode = urllib.quote, - email = emailfilter, - user = userfilter, - shortrev = shortrevfilter(revlink, env), - revlink = revlinkfilter(revlink, env), - changecomment = changelinkfilter(changecommentlink), - repolink = dictlinkfilter(repositories), - projectlink = dictlinkfilter(projects) - )) - - return env - -def emailfilter(value): - ''' Escape & obfuscate e-mail addresses - - replacing @ with <span style="display:none> reportedly works well against web-spiders - and the next level is to use rot-13 (or something) and decode in javascript ''' - - user = jinja2.escape(value) - obfuscator = jinja2.Markup('<span style="display:none">ohnoyoudont</span>@') - output = user.replace('@', obfuscator) - return output - - -def userfilter(value): - ''' Hide e-mail address from user name when viewing changes - - We still include the (obfuscated) e-mail so that we can show - it on mouse-over or similar etc - ''' - r = re.compile('(.*) +<(.*)>') - m = r.search(value) - if m: - user = jinja2.escape(m.group(1)) - email = emailfilter(m.group(2)) - return jinja2.Markup('<div class="user">%s<div class="email">%s</div></div>' % (user, email)) - else: - return emailfilter(value) # filter for emails here for safety - -def _revlinkcfg(replace, templates): - '''Helper function that returns suitable macros and functions - for building revision links depending on replacement mechanism -''' - - assert not replace or callable(replace) or isinstance(replace, dict) or \ - isinstance(replace, str) or isinstance(replace, unicode) - - if not replace: - return lambda rev, repo: None - else: - if callable(replace): - return lambda rev, repo: replace(rev, repo) - elif isinstance(replace, dict): # TODO: test for [] instead - def filter(rev, repo): - url = replace.get(repo) - if url: - return url % urllib.quote(rev) - else: - return None - - return filter - else: - return lambda rev, repo: replace % urllib.quote(rev) - - assert False, '_replace has a bad type, but we should never get here' - - -def _revlinkmacros(replace, templates): - '''return macros for use with revision links, depending - on whether revlinks are configured or not''' - - macros = templates.get_template("revmacros.html").module - - if not replace: - id = macros.id - short = macros.shorten - else: - id = macros.id_replace - short = macros.shorten_replace - - return (id, short) - - -def shortrevfilter(replace, templates): - ''' Returns a function which shortens the revisison string - to 12-chars (chosen as this is the Mercurial short-id length) - and add link if replacement string is set. - - (The full id is still visible in HTML, for mouse-over events etc.) - - @param replace: see revlinkfilter() - @param templates: a jinja2 environment - ''' - - url_f = _revlinkcfg(replace, templates) - - def filter(rev, repo): - if not rev: - return u'' - - id_html, short_html = _revlinkmacros(replace, templates) - rev = unicode(rev) - url = url_f(rev, repo) - rev = jinja2.escape(rev) - shortrev = rev[:12] # TODO: customize this depending on vc type - - if shortrev == rev: - if url: - return id_html(rev=rev, url=url) - else: - return rev - else: - if url: - return short_html(short=shortrev, rev=rev, url=url) - else: - return shortrev + '...' - - return filter - - -def revlinkfilter(replace, templates): - ''' Returns a function which adds an url link to a - revision identifiers. - - Takes same params as shortrevfilter() - - @param replace: either a python format string with an %s, - or a dict mapping repositories to format strings, - or a callable taking (revision, repository) arguments - and return an URL (or None, if no URL is available), - or None, in which case revisions do not get decorated - with links - - @param templates: a jinja2 environment - ''' - - url_f = _revlinkcfg(replace, templates) - - def filter(rev, repo): - if not rev: - return u'' - - rev = unicode(rev) - url = url_f(rev, repo) - if url: - id_html, _ = _revlinkmacros(replace, templates) - return id_html(rev=rev, url=url) - else: - return jinja2.escape(rev) - - return filter - - -def changelinkfilter(changelink): - ''' Returns function that does regex search/replace in - comments to add links to bug ids and similar. - - @param changelink: - Either C{None} - or: a tuple (2 or 3 elements) - 1. a regex to match what we look for - 2. an url with regex refs (\g<0>, \1, \2, etc) that becomes the 'href' attribute - 3. (optional) an title string with regex ref regex - or: a dict mapping projects to above tuples - (no links will be added if the project isn't found) - or: a callable taking (changehtml, project) args - (where the changetext is HTML escaped in the - form of a jinja2.Markup instance) and - returning another jinja2.Markup instance with - the same change text plus any HTML tags added to it. - ''' - - assert not changelink or isinstance(changelink, dict) or \ - isinstance(changelink, tuple) or callable(changelink) - - def replace_from_tuple(t): - search, url_replace = t[:2] - if len(t) == 3: - title_replace = t[2] - else: - title_replace = '' - - search_re = re.compile(search) - - def replacement_unmatched(text): - return jinja2.escape(text) - def replacement_matched(mo): - # expand things *after* application of the regular expressions - url = jinja2.escape(mo.expand(url_replace)) - title = jinja2.escape(mo.expand(title_replace)) - body = jinja2.escape(mo.group()) - if title: - return '<a href="%s" title="%s">%s</a>' % (url, title, body) - else: - return '<a href="%s">%s</a>' % (url, body) - - def filter(text, project): - # now, we need to split the string into matched and unmatched portions, - # quoting the unmatched portions directly and quoting the components of - # the 'a' element for the matched portions. We can't use re.split here, - # because the user-supplied patterns may have multiple groups. - html = [] - last_idx = 0 - for mo in search_re.finditer(text): - html.append(replacement_unmatched(text[last_idx:mo.start()])) - html.append(replacement_matched(mo)) - last_idx = mo.end() - html.append(replacement_unmatched(text[last_idx:])) - return jinja2.Markup(''.join(html)) - - return filter - - if not changelink: - return lambda text, project: jinja2.escape(text) - - elif isinstance(changelink, dict): - def dict_filter(text, project): - # TODO: Optimize and cache return value from replace_from_tuple so - # we only compile regex once per project, not per view - - t = changelink.get(project) - if t: - return replace_from_tuple(t)(text, project) - else: - return cgi.escape(text) - - return dict_filter - - elif isinstance(changelink, tuple): - return replace_from_tuple(changelink) - - elif callable(changelink): - def callable_filter(text, project): - text = jinja2.escape(text) - return changelink(text, project) - - return callable_filter - - assert False, 'changelink has unsupported type, but that is checked before' - - -def dictlinkfilter(links): - '''A filter that encloses the given value in a link tag - given that the value exists in the dictionary''' - - assert not links or callable(links) or isinstance(links, dict) - - if not links: - return jinja2.escape - - def filter(key): - if callable(links): - url = links(key) - else: - url = links.get(key) - - safe_key = jinja2.escape(key) - - if url: - return jinja2.Markup(r'<a href="%s">%s</a>' % (url, safe_key)) - else: - return safe_key - - return filter - -class AlmostStrictUndefined(jinja2.StrictUndefined): - ''' An undefined that allows boolean testing but - fails properly on every other use. - - Much better than the default Undefined, but not - fully as strict as StrictUndefined ''' - def __nonzero__(self): - return False - -_charsetRe = re.compile('charset=([^;]*)', re.I) -def getRequestCharset(req): - """Get the charset for an x-www-form-urlencoded request""" - # per http://stackoverflow.com/questions/708915/detecting-the-character-encoding-of-an-http-post-request - hdr = req.getHeader('Content-Type') - if hdr: - mo = _charsetRe.search(hdr) - if mo: - return mo.group(1).strip() - return 'utf-8' # reasonable guess, works for ascii diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/baseweb.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/baseweb.py deleted file mode 100644 index 9fe5e7fc..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/baseweb.py +++ /dev/null @@ -1,607 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -import os, weakref - -from zope.interface import implements -from twisted.python import log -from twisted.application import strports, service -from twisted.internet import defer -from twisted.web import server, distrib, static -from twisted.spread import pb -from twisted.web.util import Redirect -from buildbot import config -from buildbot.interfaces import IStatusReceiver -from buildbot.status.web.base import StaticFile, createJinjaEnv -from buildbot.status.web.feeds import Rss20StatusResource, \ - Atom10StatusResource -from buildbot.status.web.waterfall import WaterfallStatusResource -from buildbot.status.web.console import ConsoleStatusResource -from buildbot.status.web.olpb import OneLinePerBuild -from buildbot.status.web.grid import GridStatusResource -from buildbot.status.web.grid import TransposedGridStatusResource -from buildbot.status.web.changes import ChangesResource -from buildbot.status.web.builder import BuildersResource -from buildbot.status.web.buildstatus import BuildStatusStatusResource -from buildbot.status.web.slaves import BuildSlavesResource -from buildbot.status.web.status_json import JsonStatusResource -from buildbot.status.web.about import AboutBuildbot -from buildbot.status.web.authz import Authz -from buildbot.status.web.auth import AuthFailResource,AuthzFailResource, LoginResource, LogoutResource -from buildbot.status.web.root import RootPage -from buildbot.status.web.users import UsersResource -from buildbot.status.web.change_hook import ChangeHookResource -from twisted.cred.portal import IRealm, Portal -from twisted.cred import strcred -from twisted.cred.checkers import ICredentialsChecker -from twisted.cred.credentials import IUsernamePassword -from twisted.web import resource, guard - -# this class contains the WebStatus class. Basic utilities are in base.py, -# and specific pages are each in their own module. - -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. - /tgrid : similar to the grid display, but the commits are down the - left side, and the build hosts are across the top. - /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/_all/{force,stop}: force a build/stop building on all builders. - /buildstatus?builder=...&number=...: an embedded iframe for the console - /changes : summarize all ChangeSources - /changes/CHANGENUM: a page describing a single Change - /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 - /about : describe this buildmaster (Buildbot and support library versions) - /change_hook[/DIALECT] : accepts changes from external sources, optionally - choosing the dialect that will be permitted - (i.e. github format, etc..) - - and more! see the manual. - - - All URLs for pages which are not defined here are used to look - for files in PUBLIC_HTML, which defaults to BASEDIR/public_html. - This means that /robots.txt or /favicon.ico can be placed in - that directory - - This webserver uses the jinja2 template system to generate the web pages - (see http://jinja.pocoo.org/2/) and by default loads pages from the - buildbot.status.web.templates package. Any file here can be overridden by placing - a corresponding file in the master's 'templates' directory. - - The main customization points are layout.html which loads style sheet - (css) and provides header and footer content, and root.html, which - generates the root page. - - All of the resources provided by this service use relative URLs to reach - each other. The only absolute links are the c['titleURL'] links at the - top and bottom of the page, and the buildbot home-page link at the - bottom. - - Buildbot uses some generic classes to identify the type of object, and - some more specific classes for the various kinds of those types. It does - this by specifying both in the class attributes where applicable, - separated by a space. It is important that in your CSS you declare the - more generic class styles above the more specific ones. For example, - first define a style for .Event, and below that for .SUCCESS - - The following CSS class names are used: - - Activity, Event, BuildStep, LastBuild: general classes - - waiting, interlocked, building, offline, idle: Activity states - - start, running, success, failure, warnings, skipped, exception: - LastBuild and BuildStep states - - Change: box with change - - Builder: box for builder name (at top) - - Project - - Time - - """ - - # we are not a ComparableMixin, and therefore the webserver will be - # rebuilt every time we reconfig. This is because WebStatus.putChild() - # makes it too difficult to tell whether two instances are the same or - # not (we'd have to do a recursive traversal of all children to discover - # all the changes). - - def __init__(self, http_port=None, distrib_port=None, allowForce=None, - public_html="public_html", site=None, numbuilds=20, - num_events=200, num_events_max=None, auth=None, - order_console_by_time=False, changecommentlink=None, - revlink=None, projects=None, repositories=None, - authz=None, logRotateLength=None, maxRotatedFiles=None, - change_hook_dialects = {}, provide_feeds=None, jinja_loaders=None, - change_hook_auth=None): - """Run a web server that provides Buildbot status. - - @type http_port: int or L{twisted.application.strports} string - @param http_port: a strports specification describing which port the - buildbot should use for its web server, with the - Waterfall display as the root page. For backwards - compatibility this can also be an int. Use - 'tcp:8000' to listen on that port, or - 'tcp:12345:interface=127.0.0.1' if you only want - local processes to connect to it (perhaps because - you are using an HTTP reverse proxy to make the - buildbot available to the outside world, and do not - want to make the raw port visible). - - @type distrib_port: int or L{twisted.application.strports} string - @param distrib_port: Use this if you want to publish the Waterfall - page using web.distrib instead. The most common - case is to provide a string that is an absolute - pathname to the unix socket on which the - publisher should listen - (C{os.path.expanduser(~/.twistd-web-pb)} will - match the default settings of a standard - twisted.web 'personal web server'). Another - possibility is to pass an integer, which means - the publisher should listen on a TCP socket, - allowing the web server to be on a different - machine entirely. Both forms are provided for - backwards compatibility; the preferred form is a - strports specification like - 'unix:/home/buildbot/.twistd-web-pb'. Providing - a non-absolute pathname will probably confuse - the strports parser. - - @param allowForce: deprecated; use authz instead - @param auth: deprecated; use with authz - - @param authz: a buildbot.status.web.authz.Authz instance giving the authorization - parameters for this view - - @param public_html: the path to the public_html directory for this display, - either absolute or relative to the basedir. The default - is 'public_html', which selects BASEDIR/public_html. - - @type site: None or L{twisted.web.server.Site} - @param site: Use this if you want to define your own object instead of - using the default.` - - @type numbuilds: int - @param numbuilds: Default number of entries in lists at the /one_line_per_build - and /builders/FOO URLs. This default can be overriden both programatically --- - by passing the equally named argument to constructors of OneLinePerBuildOneBuilder - and OneLinePerBuild --- and via the UI, by tacking ?numbuilds=xy onto the URL. - - @type num_events: int - @param num_events: Default number of events to show in the waterfall. - - @type num_events_max: int - @param num_events_max: The maximum number of events that are allowed to be - shown in the waterfall. The default value of C{None} will disable this - check - - @type auth: a L{status.web.auth.IAuth} or C{None} - @param auth: an object that performs authentication to restrict access - to the C{allowForce} features. Ignored if C{allowForce} - is not C{True}. If C{auth} is C{None}, people can force or - stop builds without auth. - - @type order_console_by_time: bool - @param order_console_by_time: Whether to order changes (commits) in the console - view according to the time they were created (for VCS like Git) or - according to their integer revision numbers (for VCS like SVN). - - @type changecommentlink: callable, dict, tuple (2 or 3 strings) or C{None} - @param changecommentlink: adds links to ticket/bug ids in change comments, - see buildbot.status.web.base.changecommentlink for details - - @type revlink: callable, dict, string or C{None} - @param revlink: decorations revision ids with links to a web-view, - see buildbot.status.web.base.revlink for details - - @type projects: callable, dict or c{None} - @param projects: maps project identifiers to URLs, so that any project listed - is automatically decorated with a link to it's front page. - see buildbot.status.web.base.dictlink for details - - @type repositories: callable, dict or c{None} - @param repositories: maps repository identifiers to URLs, so that any project listed - is automatically decorated with a link to it's web view. - see buildbot.status.web.base.dictlink for details - - @type logRotateLength: None or int - @param logRotateLength: file size at which the http.log is rotated/reset. - If not set, the value set in the buildbot.tac will be used, - falling back to the BuildMaster's default value (1 Mb). - - @type maxRotatedFiles: None or int - @param maxRotatedFiles: number of old http.log files to keep during log rotation. - If not set, the value set in the buildbot.tac will be used, - falling back to the BuildMaster's default value (10 files). - - @type change_hook_dialects: None or dict - @param change_hook_dialects: If empty, disables change_hook support, otherwise - whitelists valid dialects. In the format of - {"dialect1": "Option1", "dialect2", None} - Where the values are options that will be passed - to the dialect - - To enable the DEFAULT handler, use a key of DEFAULT - - - - - @type provide_feeds: None or list - @param provide_feeds: If empty, provides atom, json, and rss feeds. - Otherwise, a dictionary of strings of - the type of feeds provided. Current - possibilities are "atom", "json", and "rss" - - @type jinja_loaders: None or list - @param jinja_loaders: If not empty, a list of additional Jinja2 loader - objects to search for templates. - """ - - service.MultiService.__init__(self) - if type(http_port) is int: - http_port = "tcp:%d" % http_port - self.http_port = http_port - if distrib_port is not None: - if type(distrib_port) is int: - distrib_port = "tcp:%d" % distrib_port - if distrib_port[0] in "/~.": # pathnames - distrib_port = "unix:%s" % distrib_port - self.distrib_port = distrib_port - self.num_events = num_events - if num_events_max: - if num_events_max < num_events: - config.error( - "num_events_max must be greater than num_events") - self.num_events_max = num_events_max - self.public_html = public_html - - # make up an authz if allowForce was given - if authz: - if allowForce is not None: - config.error( - "cannot use both allowForce and authz parameters") - if auth: - config.error( - "cannot use both auth and authz parameters (pass " + - "auth as an Authz parameter)") - else: - # invent an authz - if allowForce and auth: - authz = Authz(auth=auth, default_action="auth") - elif allowForce: - authz = Authz(default_action=True) - else: - if auth: - log.msg("Warning: Ignoring authentication. Search for 'authorization'" - " in the manual") - authz = Authz() # no authorization for anything - - self.authz = authz - - # check for correctness of HTTP auth parameters - if change_hook_auth is not None: - self.change_hook_auth = [] - for checker in change_hook_auth: - if isinstance(checker, str): - try: - checker = strcred.makeChecker(checker) - except Exception, error: - config.error("Invalid change_hook checker description: %s" % (error,)) - continue - elif not ICredentialsChecker.providedBy(checker): - config.error("change_hook checker doesn't provide ICredentialChecker: %r" % (checker,)) - continue - - if IUsernamePassword not in checker.credentialInterfaces: - config.error("change_hook checker doesn't support IUsernamePassword: %r" % (checker,)) - continue - - self.change_hook_auth.append(checker) - else: - self.change_hook_auth = None - - self.orderConsoleByTime = order_console_by_time - - # If we were given a site object, go ahead and use it. (if not, we add one later) - self.site = site - - # keep track of our child services - self.http_svc = None - self.distrib_svc = None - - # store the log settings until we create the site object - self.logRotateLength = logRotateLength - self.maxRotatedFiles = maxRotatedFiles - - # create the web site page structure - self.childrenToBeAdded = {} - self.setupUsualPages(numbuilds=numbuilds, num_events=num_events, - num_events_max=num_events_max) - - self.revlink = revlink - self.changecommentlink = changecommentlink - self.repositories = repositories - self.projects = projects - - # keep track of cached connections so we can break them when we shut - # down. See ticket #102 for more details. - self.channels = weakref.WeakKeyDictionary() - - # do we want to allow change_hook - self.change_hook_dialects = {} - if change_hook_dialects: - self.change_hook_dialects = change_hook_dialects - resource_obj = ChangeHookResource(dialects=self.change_hook_dialects) - if self.change_hook_auth is not None: - resource_obj = self.setupProtectedResource( - resource_obj, self.change_hook_auth) - self.putChild("change_hook", resource_obj) - - # Set default feeds - if provide_feeds is None: - self.provide_feeds = ["atom", "json", "rss"] - else: - self.provide_feeds = provide_feeds - - self.jinja_loaders = jinja_loaders - - def setupProtectedResource(self, resource_obj, checkers): - class SimpleRealm(object): - """ - A realm which gives out L{ChangeHookResource} instances for authenticated - users. - """ - implements(IRealm) - - def requestAvatar(self, avatarId, mind, *interfaces): - if resource.IResource in interfaces: - return (resource.IResource, resource_obj, lambda: None) - raise NotImplementedError() - - portal = Portal(SimpleRealm(), checkers) - credentialFactory = guard.BasicCredentialFactory('Protected area') - wrapper = guard.HTTPAuthSessionWrapper(portal, [credentialFactory]) - return wrapper - - def setupUsualPages(self, numbuilds, num_events, num_events_max): - #self.putChild("", IndexOrWaterfallRedirection()) - self.putChild("waterfall", WaterfallStatusResource(num_events=num_events, - num_events_max=num_events_max)) - self.putChild("grid", GridStatusResource()) - self.putChild("console", ConsoleStatusResource( - orderByTime=self.orderConsoleByTime)) - self.putChild("tgrid", TransposedGridStatusResource()) - self.putChild("builders", BuildersResource(numbuilds=numbuilds)) # has builds/steps/logs - self.putChild("one_box_per_builder", Redirect("builders")) - self.putChild("changes", ChangesResource()) - self.putChild("buildslaves", BuildSlavesResource()) - self.putChild("buildstatus", BuildStatusStatusResource()) - self.putChild("one_line_per_build", - OneLinePerBuild(numbuilds=numbuilds)) - self.putChild("about", AboutBuildbot()) - self.putChild("authfail", AuthFailResource()) - self.putChild("authzfail", AuthzFailResource()) - self.putChild("users", UsersResource()) - self.putChild("login", LoginResource()) - self.putChild("logout", LogoutResource()) - - def __repr__(self): - if self.http_port is None: - return "<WebStatus on path %s at %s>" % (self.distrib_port, - hex(id(self))) - if self.distrib_port is None: - return "<WebStatus on port %s at %s>" % (self.http_port, - hex(id(self))) - return ("<WebStatus on port %s and path %s at %s>" % - (self.http_port, self.distrib_port, hex(id(self)))) - - def setServiceParent(self, parent): - # this class keeps a *separate* link to the buildmaster, rather than - # just using self.parent, so that when we are "disowned" (and thus - # parent=None), any remaining HTTP clients of this WebStatus will still - # be able to get reasonable results. - self.master = parent.master - - # set master in IAuth instance - if self.authz.auth: - self.authz.auth.master = self.master - - def either(a,b): # a if a else b for py2.4 - if a: - return a - else: - return b - - rotateLength = either(self.logRotateLength, self.master.log_rotation.rotateLength) - maxRotatedFiles = either(self.maxRotatedFiles, self.master.log_rotation.maxRotatedFiles) - - # Set up the jinja templating engine. - if self.revlink: - revlink = self.revlink - else: - revlink = self.master.config.revlink - self.templates = createJinjaEnv(revlink, self.changecommentlink, - self.repositories, self.projects, self.jinja_loaders) - - if not self.site: - - class RotateLogSite(server.Site): - def _openLogFile(self, path): - try: - from twisted.python.logfile import LogFile - log.msg("Setting up http.log rotating %s files of %s bytes each" % - (maxRotatedFiles, rotateLength)) - if hasattr(LogFile, "fromFullPath"): # not present in Twisted-2.5.0 - return LogFile.fromFullPath(path, rotateLength=rotateLength, maxRotatedFiles=maxRotatedFiles) - else: - log.msg("WebStatus: rotated http logs are not supported on this version of Twisted") - except ImportError, e: - log.msg("WebStatus: Unable to set up rotating http.log: %s" % e) - - # if all else fails, just call the parent method - return server.Site._openLogFile(self, path) - - # this will be replaced once we've been attached to a parent (and - # thus have a basedir and can reference BASEDIR) - root = static.Data("placeholder", "text/plain") - httplog = os.path.abspath(os.path.join(self.master.basedir, "http.log")) - self.site = RotateLogSite(root, logPath=httplog) - - # the following items are accessed by HtmlResource when it renders - # each page. - self.site.buildbot_service = self - - if self.http_port is not None: - self.http_svc = s = strports.service(self.http_port, self.site) - s.setServiceParent(self) - if self.distrib_port is not None: - f = pb.PBServerFactory(distrib.ResourcePublisher(self.site)) - self.distrib_svc = s = strports.service(self.distrib_port, f) - s.setServiceParent(self) - - self.setupSite() - - service.MultiService.setServiceParent(self, parent) - - def setupSite(self): - # this is responsible for creating the root resource. It isn't done - # at __init__ time because we need to reference the parent's basedir. - htmldir = os.path.abspath(os.path.join(self.master.basedir, self.public_html)) - if os.path.isdir(htmldir): - log.msg("WebStatus using (%s)" % htmldir) - else: - log.msg("WebStatus: warning: %s is missing. Do you need to run" - " 'buildbot upgrade-master' on this buildmaster?" % htmldir) - # all static pages will get a 404 until upgrade-master is used to - # populate this directory. Create the directory, though, since - # otherwise we get internal server errors instead of 404s. - os.mkdir(htmldir) - - root = StaticFile(htmldir) - root_page = RootPage() - root.putChild("", root_page) - root.putChild("shutdown", root_page) - root.putChild("cancel_shutdown", root_page) - - for name, child_resource in self.childrenToBeAdded.iteritems(): - root.putChild(name, child_resource) - - status = self.getStatus() - if "rss" in self.provide_feeds: - root.putChild("rss", Rss20StatusResource(status)) - if "atom" in self.provide_feeds: - root.putChild("atom", Atom10StatusResource(status)) - if "json" in self.provide_feeds: - root.putChild("json", JsonStatusResource(status)) - - self.site.resource = root - - def putChild(self, name, child_resource): - """This behaves a lot like root.putChild() . """ - self.childrenToBeAdded[name] = child_resource - - def registerChannel(self, channel): - self.channels[channel] = 1 # weakrefs - - @defer.inlineCallbacks - def stopService(self): - for channel in self.channels: - try: - channel.transport.loseConnection() - except: - log.msg("WebStatus.stopService: error while disconnecting" - " leftover clients") - log.err() - yield service.MultiService.stopService(self) - - # having shut them down, now remove our child services so they don't - # start up again if we're re-started - if self.http_svc: - yield self.http_svc.disownServiceParent() - self.http_svc = None - if self.distrib_svc: - yield self.distrib_svc.disownServiceParent() - self.distrib_svc = None - - def getStatus(self): - return self.master.getStatus() - - def getChangeSvc(self): - return self.master.change_svc - - def getPortnum(self): - # this is for the benefit of unit tests - s = list(self)[0] - return s._port.getHost().port - - # What happened to getControl?! - # - # instead of passing control objects all over the place in the web - # code, at the few places where a control instance is required we - # find the requisite object manually, starting at the buildmaster. - # This is in preparation for removal of the IControl hierarchy - # entirely. - - def checkConfig(self, otherStatusReceivers): - duplicate_webstatus=0 - for osr in otherStatusReceivers: - if isinstance(osr,WebStatus): - if osr is self: - continue - # compare against myself and complain if the settings conflict - if self.http_port == osr.http_port: - if duplicate_webstatus == 0: - duplicate_webstatus = 2 - else: - duplicate_webstatus += 1 - - if duplicate_webstatus: - config.error( - "%d Webstatus objects have same port: %s" - % (duplicate_webstatus, self.http_port), - ) - -# resources can get access to the IStatus by calling -# request.site.buildbot_service.getStatus() diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/build.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/build.py deleted file mode 100644 index 1cf7f92f..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/build.py +++ /dev/null @@ -1,332 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -from twisted.web import html -from twisted.internet import defer, reactor -from twisted.web.util import Redirect, DeferredResource - -import urllib, time -from twisted.python import log -from buildbot.status.web.base import HtmlResource, \ - css_classes, path_to_build, path_to_builder, path_to_slave, \ - getAndCheckProperties, ActionResource, path_to_authzfail, \ - getRequestCharset -from buildbot.schedulers.forcesched import ForceScheduler, TextParameter -from buildbot.status.web.step import StepsResource -from buildbot.status.web.tests import TestsResource -from buildbot import util, interfaces - -class ForceBuildActionResource(ActionResource): - - def __init__(self, build_status, builder): - self.build_status = build_status - self.builder = builder - self.action = "forceBuild" - - @defer.inlineCallbacks - def performAction(self, req): - url = None - authz = self.getAuthz(req) - res = yield authz.actionAllowed(self.action, req, self.builder) - - if not res: - url = path_to_authzfail(req) - else: - # get a control object - c = interfaces.IControl(self.getBuildmaster(req)) - bc = c.getBuilder(self.builder.getName()) - - b = self.build_status - builder_name = self.builder.getName() - log.msg("web rebuild of build %s:%s" % (builder_name, b.getNumber())) - name =authz.getUsernameFull(req) - comments = req.args.get("comments", ["<no reason specified>"])[0] - comments.decode(getRequestCharset(req)) - reason = ("The web-page 'rebuild' button was pressed by " - "'%s': %s\n" % (name, comments)) - msg = "" - extraProperties = getAndCheckProperties(req) - if not bc or not b.isFinished() or extraProperties is None: - msg = "could not rebuild: " - if b.isFinished(): - msg += "build still not finished " - if bc: - msg += "could not get builder control" - else: - tup = yield bc.rebuildBuild(b, reason, extraProperties) - # rebuildBuild returns None on error (?!) - if not tup: - msg = "rebuilding a build failed "+ str(tup) - # we're at - # http://localhost:8080/builders/NAME/builds/5/rebuild?[args] - # Where should we send them? - # - # Ideally it would be to the per-build page that they just started, - # but we don't know the build number for it yet (besides, it might - # 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 should be the Builder page. - - url = path_to_builder(req, self.builder), msg - defer.returnValue(url) - - -class StopBuildActionResource(ActionResource): - - def __init__(self, build_status): - self.build_status = build_status - self.action = "stopBuild" - - @defer.inlineCallbacks - def performAction(self, req): - authz = self.getAuthz(req) - res = yield authz.actionAllowed(self.action, req, self.build_status) - - if not res: - defer.returnValue(path_to_authzfail(req)) - return - - b = self.build_status - log.msg("web stopBuild of build %s:%s" % \ - (b.getBuilder().getName(), b.getNumber())) - name = authz.getUsernameFull(req) - comments = req.args.get("comments", ["<no reason specified>"])[0] - comments.decode(getRequestCharset(req)) - # html-quote both the username and comments, just to be safe - reason = ("The web-page 'stop build' button was pressed by " - "'%s': %s\n" % (html.escape(name), html.escape(comments))) - - c = interfaces.IControl(self.getBuildmaster(req)) - bldrc = c.getBuilder(self.build_status.getBuilder().getName()) - if bldrc: - bldc = bldrc.getBuild(self.build_status.getNumber()) - if bldc: - bldc.stopBuild(reason) - - defer.returnValue(path_to_builder(req, self.build_status.getBuilder())) - -# /builders/$builder/builds/$buildnum -class StatusResourceBuild(HtmlResource): - addSlash = True - - def __init__(self, build_status): - HtmlResource.__init__(self) - self.build_status = build_status - - def getPageTitle(self, request): - return ("Buildbot: %s Build #%d" % - (self.build_status.getBuilder().getName(), - self.build_status.getNumber())) - - def content(self, req, cxt): - b = self.build_status - status = self.getStatus(req) - req.setHeader('Cache-Control', 'no-cache') - - cxt['b'] = b - cxt['path_to_builder'] = path_to_builder(req, b.getBuilder()) - - if not b.isFinished(): - step = b.getCurrentStep() - if not step: - cxt['current_step'] = "[waiting for Lock]" - else: - if step.isWaitingForLocks(): - cxt['current_step'] = "%s [waiting for Lock]" % step.getName() - else: - cxt['current_step'] = step.getName() - when = b.getETA() - if when is not None: - cxt['when'] = util.formatInterval(when) - cxt['when_time'] = time.strftime("%H:%M:%S", - time.localtime(time.time() + when)) - - else: - cxt['result_css'] = css_classes[b.getResults()] - if b.getTestResults(): - cxt['tests_link'] = req.childLink("tests") - - ssList = b.getSourceStamps() - sourcestamps = cxt['sourcestamps'] = ssList - - all_got_revisions = b.getAllGotRevisions() - cxt['got_revisions'] = all_got_revisions - - try: - cxt['slave_url'] = path_to_slave(req, status.getSlave(b.getSlavename())) - except KeyError: - pass - - cxt['steps'] = [] - - for s in b.getSteps(): - step = {'name': s.getName() } - - if s.isFinished(): - if s.isHidden(): - continue - - step['css_class'] = css_classes[s.getResults()[0]] - (start, end) = s.getTimes() - step['time_to_run'] = util.formatInterval(end - start) - elif s.isStarted(): - if s.isWaitingForLocks(): - step['css_class'] = "waiting" - step['time_to_run'] = "waiting for locks" - else: - start = s.getTimes()[0] - step['css_class'] = "running" - step['time_to_run'] = "running %s" % (util.formatInterval(util.now() - start)) - else: - step['css_class'] = "not_started" - step['time_to_run'] = "" - - cxt['steps'].append(step) - - step['link'] = req.childLink("steps/%s" % - urllib.quote(s.getName(), safe='')) - step['text'] = " ".join(s.getText()) - step['urls'] = map(lambda x:dict(url=x[1],logname=x[0]), s.getURLs().items()) - - step['logs']= [] - for l in s.getLogs(): - logname = l.getName() - step['logs'].append({ 'link': req.childLink("steps/%s/logs/%s" % - (urllib.quote(s.getName(), safe=''), - urllib.quote(logname, safe=''))), - 'name': logname }) - - scheduler = b.getProperty("scheduler", None) - parameters = {} - master = self.getBuildmaster(req) - for sch in master.allSchedulers(): - if isinstance(sch, ForceScheduler) and scheduler == sch.name: - for p in sch.all_fields: - parameters[p.name] = p - - ps = cxt['properties'] = [] - for name, value, source in b.getProperties().asList(): - if not isinstance(value, dict): - cxt_value = unicode(value) - else: - cxt_value = value - p = { 'name': name, 'value': cxt_value, 'source': source} - if len(cxt_value) > 500: - p['short_value'] = cxt_value[:500] - if name in parameters: - param = parameters[name] - if isinstance(param, TextParameter): - p['text'] = param.value_to_text(value) - p['cols'] = param.cols - p['rows'] = param.rows - p['label'] = param.label - ps.append(p) - - - cxt['responsible_users'] = list(b.getResponsibleUsers()) - - (start, end) = b.getTimes() - cxt['start'] = time.ctime(start) - if end: - cxt['end'] = time.ctime(end) - cxt['elapsed'] = util.formatInterval(end - start) - else: - now = util.now() - cxt['elapsed'] = util.formatInterval(now - start) - - exactly = True - has_changes = False - for ss in sourcestamps: - exactly = exactly and (ss.revision is not None) - has_changes = has_changes or ss.changes - cxt['exactly'] = (exactly) or b.getChanges() - cxt['has_changes'] = has_changes - cxt['build_url'] = path_to_build(req, b) - cxt['authz'] = self.getAuthz(req) - - template = req.site.buildbot_service.templates.get_template("build.html") - return template.render(**cxt) - - def stop(self, req, auth_ok=False): - # check if this is allowed - if not auth_ok: - return StopBuildActionResource(self.build_status) - - b = self.build_status - log.msg("web stopBuild of build %s:%s" % \ - (b.getBuilder().getName(), b.getNumber())) - - name = self.getAuthz(req).getUsernameFull(req) - comments = req.args.get("comments", ["<no reason specified>"])[0] - comments.decode(getRequestCharset(req)) - # html-quote both the username and comments, just to be safe - reason = ("The web-page 'stop build' button was pressed by " - "'%s': %s\n" % (html.escape(name), html.escape(comments))) - - c = interfaces.IControl(self.getBuildmaster(req)) - bldrc = c.getBuilder(self.build_status.getBuilder().getName()) - if bldrc: - bldc = bldrc.getBuild(self.build_status.getNumber()) - if bldc: - bldc.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 - r = Redirect(path_to_builder(req, self.build_status.getBuilder())) - d = defer.Deferred() - reactor.callLater(1, d.callback, r) - return DeferredResource(d) - - def rebuild(self, req): - return ForceBuildActionResource(self.build_status, - self.build_status.getBuilder()) - - def getChild(self, path, req): - if path == "stop": - return self.stop(req) - if path == "rebuild": - return self.rebuild(req) - if path == "steps": - return StepsResource(self.build_status) - if path == "tests": - return TestsResource(self.build_status) - - return HtmlResource.getChild(self, path, req) - -# /builders/$builder/builds -class BuildsResource(HtmlResource): - addSlash = True - - def __init__(self, builder_status): - HtmlResource.__init__(self) - self.builder_status = builder_status - - def content(self, req, cxt): - return "subpages shows data for each build" - - def getChild(self, path, req): - try: - num = int(path) - except ValueError: - num = None - if num is not None: - build_status = self.builder_status.getBuild(num) - if build_status: - return StatusResourceBuild(build_status) - - return HtmlResource.getChild(self, path, req) - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/builder.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/builder.py deleted file mode 100644 index b4d15233..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/builder.py +++ /dev/null @@ -1,632 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -from twisted.web import html -import urllib, time -from twisted.python import log -from twisted.internet import defer -from buildbot import interfaces -from buildbot.status.web.base import HtmlResource, BuildLineMixin, \ - path_to_build, path_to_slave, path_to_builder, path_to_change, \ - path_to_root, ICurrentBox, build_get_class, \ - map_branches, path_to_authzfail, ActionResource, \ - getRequestCharset -from buildbot.schedulers.forcesched import ForceScheduler -from buildbot.schedulers.forcesched import ValidationError -from buildbot.status.web.build import BuildsResource, StatusResourceBuild -from buildbot import util -import collections - -class ForceAction(ActionResource): - @defer.inlineCallbacks - def force(self, req, builderNames): - master = self.getBuildmaster(req) - owner = self.getAuthz(req).getUsernameFull(req) - schedulername = req.args.get("forcescheduler", ["<unknown>"])[0] - if schedulername == "<unknown>": - defer.returnValue((path_to_builder(req, self.builder_status), - "forcescheduler arg not found")) - return - - args = {} - # decode all of the args - encoding = getRequestCharset(req) - for name, argl in req.args.iteritems(): - if name == "checkbox": - # damn html's ungeneric checkbox implementation... - for cb in argl: - args[cb.decode(encoding)] = True - else: - args[name] = [ arg.decode(encoding) for arg in argl ] - - for sch in master.allSchedulers(): - if schedulername == sch.name: - try: - yield sch.force(owner, builderNames, **args) - msg = "" - except ValidationError, e: - msg = html.escape(e.message.encode('ascii','ignore')) - break - - # send the user back to the builder page - defer.returnValue(msg) - - -class ForceAllBuildsActionResource(ForceAction): - - def __init__(self, status, selectedOrAll): - self.status = status - self.selectedOrAll = selectedOrAll - self.action = "forceAllBuilds" - - @defer.inlineCallbacks - def performAction(self, req): - authz = self.getAuthz(req) - res = yield authz.actionAllowed('forceAllBuilds', req) - - if not res: - defer.returnValue(path_to_authzfail(req)) - return - - if self.selectedOrAll == 'all': - builderNames = None - elif self.selectedOrAll == 'selected': - builderNames = [b for b in req.args.get("selected", []) if b] - - msg = yield self.force(req, builderNames) - - # back to the welcome page - defer.returnValue((path_to_root(req) + "builders", msg)) - -class StopAllBuildsActionResource(ActionResource): - - def __init__(self, status, selectedOrAll): - self.status = status - self.selectedOrAll = selectedOrAll - self.action = "stopAllBuilds" - - @defer.inlineCallbacks - def performAction(self, req): - authz = self.getAuthz(req) - res = yield authz.actionAllowed('stopAllBuilds', req) - if not res: - defer.returnValue(path_to_authzfail(req)) - return - - builders = None - if self.selectedOrAll == 'all': - builders = self.status.getBuilderNames() - elif self.selectedOrAll == 'selected': - builders = [b for b in req.args.get("selected", []) if b] - - for bname in builders: - builder_status = self.status.getBuilder(bname) - (state, current_builds) = builder_status.getState() - if state != "building": - continue - for b in current_builds: - build_status = builder_status.getBuild(b.number) - if not build_status: - continue - build = StatusResourceBuild(build_status) - build.stop(req, auth_ok=True) - - # go back to the welcome page - defer.returnValue(path_to_root(req)) - - -class CancelAllPendingBuildsActionResource(ActionResource): - - def __init__(self, status, selectedOrAll): - self.status = status - self.selectedOrAll = selectedOrAll - self.action = 'cancelAllPendingBuilds' - - @defer.inlineCallbacks - def performAction(self, req): - authz = self.getAuthz(req) - res = yield authz.actionAllowed('cancelAllPendingBuilds', req) - if not res: - defer.returnValue(path_to_authzfail(req)) - return - - builders = None - if self.selectedOrAll == 'all': - builders = self.status.getBuilderNames() - elif self.selectedOrAll == 'selected': - builders = [b for b in req.args.get("selected", []) if b] - - c = interfaces.IControl(self.getBuildmaster(req)) - for bname in builders: - authz = self.getAuthz(req) - builder_control = c.getBuilder(bname) - - brcontrols = yield builder_control.getPendingBuildRequestControls() - - for build_req in brcontrols: - log.msg("Cancelling %s" % build_req) - build_req.cancel() - - # go back to the welcome page - defer.returnValue(path_to_root(req)) - - -class PingBuilderActionResource(ActionResource): - - def __init__(self, builder_status): - self.builder_status = builder_status - self.action = "pingBuilder" - - @defer.inlineCallbacks - def performAction(self, req): - log.msg("web ping of builder '%s'" % self.builder_status.getName()) - res = yield self.getAuthz(req).actionAllowed('pingBuilder', req, - self.builder_status) - if not res: - log.msg("..but not authorized") - defer.returnValue(path_to_authzfail(req)) - return - - c = interfaces.IControl(self.getBuildmaster(req)) - bc = c.getBuilder(self.builder_status.getName()) - bc.ping() - # send the user back to the builder page - defer.returnValue(path_to_builder(req, self.builder_status)) - -class ForceBuildActionResource(ForceAction): - - def __init__(self, builder_status): - self.builder_status = builder_status - self.action = "forceBuild" - - @defer.inlineCallbacks - def performAction(self, req): - # check if this is allowed - res = yield self.getAuthz(req).actionAllowed(self.action, req, - self.builder_status) - if not res: - log.msg("..but not authorized") - defer.returnValue(path_to_authzfail(req)) - return - - builderName = self.builder_status.getName() - - msg = yield self.force(req, [builderName]) - - # send the user back to the builder page - defer.returnValue((path_to_builder(req, self.builder_status), msg)) - -def buildForceContextForField(req, default_props, sch, field, master, buildername): - pname = "%s.%s"%(sch.name, field.fullName) - - default = field.default - - if "list" in field.type: - choices = field.getChoices(master, sch, buildername) - if choices: - default = choices[0] - default_props[pname+".choices"] = choices - - default = req.args.get(pname, [default])[0] - if "bool" in field.type: - default = "checked" if default else "" - elif isinstance(default, unicode): - # filter out unicode chars, and html stuff - default = html.escape(default.encode('utf-8','ignore')) - - default_props[pname] = default - - if "nested" in field.type: - for subfield in field.fields: - buildForceContextForField(req, default_props, sch, subfield, master, buildername) - -def buildForceContext(cxt, req, master, buildername=None): - force_schedulers = {} - default_props = collections.defaultdict(str) - for sch in master.allSchedulers(): - if isinstance(sch, ForceScheduler) and (buildername is None or(buildername in sch.builderNames)): - force_schedulers[sch.name] = sch - for field in sch.all_fields: - buildForceContextForField(req, default_props, sch, field, master, buildername) - - cxt['force_schedulers'] = force_schedulers - cxt['default_props'] = default_props - -# /builders/$builder -class StatusResourceBuilder(HtmlResource, BuildLineMixin): - addSlash = True - - def __init__(self, builder_status, numbuilds=20): - HtmlResource.__init__(self) - self.builder_status = builder_status - self.numbuilds = numbuilds - - def getPageTitle(self, request): - return "Buildbot: %s" % self.builder_status.getName() - - def builder(self, build, req): - b = {} - - b['num'] = build.getNumber() - b['link'] = path_to_build(req, build) - - when = build.getETA() - if when is not None: - b['when'] = util.formatInterval(when) - b['when_time'] = time.strftime("%H:%M:%S", - time.localtime(time.time() + when)) - - step = build.getCurrentStep() - # TODO: is this necessarily the case? - if not step: - b['current_step'] = "[waiting for Lock]" - else: - if step.isWaitingForLocks(): - b['current_step'] = "%s [waiting for Lock]" % step.getName() - else: - b['current_step'] = step.getName() - - b['stop_url'] = path_to_build(req, build) + '/stop' - - return b - - @defer.inlineCallbacks - def content(self, req, cxt): - b = self.builder_status - - cxt['name'] = b.getName() - cxt['description'] = b.getDescription() - req.setHeader('Cache-Control', 'no-cache') - slaves = b.getSlaves() - connected_slaves = [s for s in slaves if s.isConnected()] - - cxt['current'] = [self.builder(x, req) for x in b.getCurrentBuilds()] - - cxt['pending'] = [] - statuses = yield b.getPendingBuildRequestStatuses() - for pb in statuses: - changes = [] - - source = yield pb.getSourceStamp() - submitTime = yield pb.getSubmitTime() - bsid = yield pb.getBsid() - - properties = yield \ - pb.master.db.buildsets.getBuildsetProperties(bsid) - - if source.changes: - for c in source.changes: - changes.append({ 'url' : path_to_change(req, c), - 'who' : c.who, - 'revision' : c.revision, - 'repo' : c.repository }) - - cxt['pending'].append({ - 'when': time.strftime("%b %d %H:%M:%S", - time.localtime(submitTime)), - 'delay': util.formatInterval(util.now() - submitTime), - 'id': pb.brid, - 'changes' : changes, - 'num_changes' : len(changes), - 'properties' : properties, - }) - - numbuilds = cxt['numbuilds'] = int(req.args.get('numbuilds', [self.numbuilds])[0]) - recent = cxt['recent'] = [] - for build in b.generateFinishedBuilds(num_builds=int(numbuilds)): - recent.append(self.get_line_values(req, build, False)) - - sl = cxt['slaves'] = [] - connected_slaves = 0 - for slave in slaves: - s = {} - sl.append(s) - s['link'] = path_to_slave(req, slave) - s['name'] = slave.getName() - c = s['connected'] = slave.isConnected() - s['paused'] = slave.isPaused() - s['admin'] = unicode(slave.getAdmin() or '', 'utf-8') - if c: - connected_slaves += 1 - cxt['connected_slaves'] = connected_slaves - - cxt['authz'] = self.getAuthz(req) - cxt['builder_url'] = path_to_builder(req, b) - buildForceContext(cxt, req, self.getBuildmaster(req), b.getName()) - template = req.site.buildbot_service.templates.get_template("builder.html") - defer.returnValue(template.render(**cxt)) - - def ping(self, req): - return PingBuilderActionResource(self.builder_status) - - def getChild(self, path, req): - if path == "force": - return ForceBuildActionResource(self.builder_status) - if path == "ping": - return self.ping(req) - if path == "cancelbuild": - return CancelChangeResource(self.builder_status) - if path == "stopchange": - return StopChangeResource(self.builder_status) - if path == "builds": - return BuildsResource(self.builder_status) - - return HtmlResource.getChild(self, path, req) - -class CancelChangeResource(ActionResource): - - def __init__(self, builder_status): - ActionResource.__init__(self) - self.builder_status = builder_status - - @defer.inlineCallbacks - def performAction(self, req): - try: - request_id = req.args.get("id", [None])[0] - if request_id == "all": - cancel_all = True - else: - cancel_all = False - request_id = int(request_id) - except: - request_id = None - - authz = self.getAuthz(req) - if request_id: - c = interfaces.IControl(self.getBuildmaster(req)) - builder_control = c.getBuilder(self.builder_status.getName()) - - brcontrols = yield builder_control.getPendingBuildRequestControls() - - for build_req in brcontrols: - if cancel_all or (build_req.brid == request_id): - log.msg("Cancelling %s" % build_req) - res = yield authz.actionAllowed('cancelPendingBuild', req, - build_req) - if res: - build_req.cancel() - else: - defer.returnValue(path_to_authzfail(req)) - return - if not cancel_all: - break - - defer.returnValue(path_to_builder(req, self.builder_status)) - -class StopChangeMixin(object): - - @defer.inlineCallbacks - def stopChangeForBuilder(self, req, builder_status, auth_ok=False): - try: - request_change = req.args.get("change", [None])[0] - request_change = int(request_change) - except: - request_change = None - - authz = self.getAuthz(req) - if request_change: - c = interfaces.IControl(self.getBuildmaster(req)) - builder_control = c.getBuilder(builder_status.getName()) - - brcontrols = yield builder_control.getPendingBuildRequestControls() - build_controls = dict((x.brid, x) for x in brcontrols) - - build_req_statuses = yield \ - builder_status.getPendingBuildRequestStatuses() - - for build_req in build_req_statuses: - ss = yield build_req.getSourceStamp() - - if not ss.changes: - continue - - for change in ss.changes: - if change.number == request_change: - control = build_controls[build_req.brid] - log.msg("Cancelling %s" % control) - res = yield authz.actionAllowed('stopChange', req, control) - if (auth_ok or res): - control.cancel() - else: - defer.returnValue(False) - return - - defer.returnValue(True) - - -class StopChangeResource(StopChangeMixin, ActionResource): - - def __init__(self, builder_status): - ActionResource.__init__(self) - self.builder_status = builder_status - - @defer.inlineCallbacks - def performAction(self, req): - """Cancel all pending builds that include a given numbered change.""" - success = yield self.stopChangeForBuilder(req, self.builder_status) - - if not success: - defer.returnValue(path_to_authzfail(req)) - else: - defer.returnValue(path_to_builder(req, self.builder_status)) - - -class StopChangeAllResource(StopChangeMixin, ActionResource): - - def __init__(self, status): - ActionResource.__init__(self) - self.status = status - - @defer.inlineCallbacks - def performAction(self, req): - """Cancel all pending builds that include a given numbered change.""" - authz = self.getAuthz(req) - res = yield authz.actionAllowed('stopChange', req) - if not res: - defer.returnValue(path_to_authzfail(req)) - return - - for bname in self.status.getBuilderNames(): - builder_status = self.status.getBuilder(bname) - res = yield self.stopChangeForBuilder(req, builder_status, auth_ok=True) - if not res: - defer.returnValue(path_to_authzfail(req)) - return - - defer.returnValue(path_to_root(req)) - - -# /builders/_all -class StatusResourceAllBuilders(HtmlResource, BuildLineMixin): - - def __init__(self, status): - HtmlResource.__init__(self) - self.status = status - - def getChild(self, path, req): - if path == "forceall": - return self.forceall(req) - if path == "stopall": - return self.stopall(req) - if path == "stopchangeall": - return StopChangeAllResource(self.status) - if path == "cancelpendingall": - return CancelAllPendingBuildsActionResource(self.status, 'all') - - return HtmlResource.getChild(self, path, req) - - def forceall(self, req): - return ForceAllBuildsActionResource(self.status, 'all') - - def stopall(self, req): - return StopAllBuildsActionResource(self.status, 'all') - -# /builders/_selected - - -class StatusResourceSelectedBuilders(HtmlResource, BuildLineMixin): - - def __init__(self, status): - HtmlResource.__init__(self) - self.status = status - - def getChild(self, path, req): - if path == "forceselected": - return self.forceselected(req) - if path == "stopselected": - return self.stopselected(req) - if path == "cancelpendingselected": - return CancelAllPendingBuildsActionResource(self.status, 'selected') - - return HtmlResource.getChild(self, path, req) - - def forceselected(self, req): - return ForceAllBuildsActionResource(self.status, 'selected') - - def stopselected(self, req): - return StopAllBuildsActionResource(self.status, 'selected') - -# /builders - - -class BuildersResource(HtmlResource): - pageTitle = "Builders" - addSlash = True - - def __init__(self, numbuilds=20): - HtmlResource.__init__(self) - self.numbuilds = numbuilds - - @defer.inlineCallbacks - def content(self, req, cxt): - status = self.getStatus(req) - encoding = getRequestCharset(req) - - builders = req.args.get("builder", status.getBuilderNames()) - branches = [ b.decode(encoding) - for b in req.args.get("branch", []) - if b ] - - # get counts of pending builds for each builder - brstatus_ds = [] - brcounts = {} - def keep_count(statuses, builderName): - brcounts[builderName] = len(statuses) - for builderName in builders: - builder_status = status.getBuilder(builderName) - d = builder_status.getPendingBuildRequestStatuses() - d.addCallback(keep_count, builderName) - brstatus_ds.append(d) - yield defer.gatherResults(brstatus_ds) - - cxt['branches'] = branches - bs = cxt['builders'] = [] - - building = 0 - online = 0 - base_builders_url = path_to_root(req) + "builders/" - for bn in builders: - bld = { 'link': base_builders_url + urllib.quote(bn, safe=''), - 'name': bn } - bs.append(bld) - - builder = status.getBuilder(bn) - builds = list(builder.generateFinishedBuilds(map_branches(branches), - num_builds=1)) - if builds: - b = builds[0] - bld['build_url'] = (bld['link'] + "/builds/%d" % b.getNumber()) - label = None - all_got_revisions = b.getAllGotRevisions() - # If len = 1 then try if revision can be used as label. - if len(all_got_revisions) == 1: - label = all_got_revisions[all_got_revisions.keys()[0]] - if not label or len(str(label)) > 20: - label = "#%d" % b.getNumber() - - bld['build_label'] = label - bld['build_text'] = " ".join(b.getText()) - bld['build_css_class'] = build_get_class(b) - - current_box = ICurrentBox(builder).getBox(status, brcounts) - bld['current_box'] = current_box.td() - - builder_status = builder.getState()[0] - if builder_status == "building": - building += 1 - online += 1 - elif builder_status != "offline": - online += 1 - - cxt['authz'] = self.getAuthz(req) - cxt['num_building'] = building - cxt['num_online'] = online - buildForceContext(cxt, req, self.getBuildmaster(req)) - template = req.site.buildbot_service.templates.get_template("builders.html") - defer.returnValue(template.render(**cxt)) - - def getChild(self, path, req): - s = self.getStatus(req) - if path in s.getBuilderNames(): - builder_status = s.getBuilder(path) - return StatusResourceBuilder(builder_status, self.numbuilds) - if path == "_all": - return StatusResourceAllBuilders(self.getStatus(req)) - if path == "_selected": - return StatusResourceSelectedBuilders(self.getStatus(req)) - - return HtmlResource.getChild(self, path, req) - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/buildstatus.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/buildstatus.py deleted file mode 100644 index 9dd57b6c..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/buildstatus.py +++ /dev/null @@ -1,66 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -from buildbot.status.web.base import HtmlResource, IBox - -class BuildStatusStatusResource(HtmlResource): - def __init__(self, categories=None): - HtmlResource.__init__(self) - - def content(self, request, ctx): - """Display a build in the same format as the waterfall page. - The HTTP GET parameters are the builder name and the build - number.""" - - status = self.getStatus(request) - request.setHeader('Cache-Control', 'no-cache') - - # Get the parameters. - name = request.args.get("builder", [None])[0] - number = request.args.get("number", [None])[0] - if not name or not number: - return "builder and number parameter missing" - number = int(number) - - # Check if the builder in parameter exists. - try: - builder = status.getBuilder(name) - except: - return "unknown builder" - - # Check if the build in parameter exists. - build = builder.getBuild(int(number)) - if not build: - return "unknown build %s" % number - - rows = ctx['rows'] = [] - - # Display each step, starting by the last one. - for i in range(len(build.getSteps()) - 1, -1, -1): - step = build.getSteps()[i] - if step.isStarted() and step.getText(): - rows.append(IBox(step).getBox(request).td(align="center")) - - # Display the bottom box with the build number in it. - ctx['build'] = IBox(build).getBox(request).td(align="center") - - template = request.site.buildbot_service.templates.get_template("buildstatus.html") - data = template.render(**ctx) - - # We want all links to display in a new tab/window instead of in the - # current one. - # TODO: Move to template - data = data.replace('<a ', '<a target="_blank"') - return data diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/change_hook.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/change_hook.py deleted file mode 100644 index cc53e34b..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/change_hook.py +++ /dev/null @@ -1,136 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -# code inspired/copied from contrib/github_buildbot -# and inspired from code from the Chromium project -# otherwise, Andrew Melo <andrew.melo@gmail.com> wrote the rest -# but "the rest" is pretty minimal - -import re -from twisted.web import resource, server -from twisted.python.reflect import namedModule -from twisted.python import log -from twisted.internet import defer - -class ChangeHookResource(resource.Resource): - # this is a cheap sort of template thingy - contentType = "text/html; charset=utf-8" - children = {} - def __init__(self, dialects={}): - """ - The keys of 'dialects' select a modules to load under - master/buildbot/status/web/hooks/ - The value is passed to the module's getChanges function, providing - configuration options to the dialect. - """ - self.dialects = dialects - self.request_dialect = None - - def getChild(self, name, request): - return self - - def render_GET(self, request): - """ - Reponds to events and starts the build process - different implementations can decide on what methods they will accept - """ - return self.render_POST(request) - - def render_POST(self, request): - """ - Reponds to events and starts the build process - different implementations can decide on what methods they will accept - - :arguments: - request - the http request object - """ - - try: - changes, src = self.getChanges( request ) - except ValueError, err: - request.setResponseCode(400, err.args[0]) - return err.args[0] - except Exception, e: - log.err(e, "processing changes from web hook") - msg = "Error processing changes." - request.setResponseCode(500, msg) - return msg - - log.msg("Payload: " + str(request.args)) - - if not changes: - log.msg("No changes found") - return "no changes found" - d = self.submitChanges( changes, request, src ) - def ok(_): - request.setResponseCode(202) - request.finish() - def err(why): - log.err(why, "adding changes from web hook") - request.setResponseCode(500) - request.finish() - d.addCallbacks(ok, err) - return server.NOT_DONE_YET - - - def getChanges(self, request): - """ - Take the logic from the change hook, and then delegate it - to the proper handler - http://localhost/change_hook/DIALECT will load up - buildmaster/status/web/hooks/DIALECT.py - - and call getChanges() - - the return value is a list of changes - - if DIALECT is unspecified, a sample implementation is provided - """ - uriRE = re.search(r'^/change_hook/?([a-zA-Z0-9_]*)', request.uri) - - if not uriRE: - log.msg("URI doesn't match change_hook regex: %s" % request.uri) - raise ValueError("URI doesn't match change_hook regex: %s" % request.uri) - - changes = [] - src = None - - # Was there a dialect provided? - if uriRE.group(1): - dialect = uriRE.group(1) - else: - dialect = 'base' - - if dialect in self.dialects.keys(): - log.msg("Attempting to load module buildbot.status.web.hooks." + dialect) - tempModule = namedModule('buildbot.status.web.hooks.' + dialect) - changes, src = tempModule.getChanges(request,self.dialects[dialect]) - log.msg("Got the following changes %s" % changes) - self.request_dialect = dialect - else: - m = "The dialect specified, '%s', wasn't whitelisted in change_hook" % dialect - log.msg(m) - log.msg("Note: if dialect is 'base' then it's possible your URL is malformed and we didn't regex it properly") - raise ValueError(m) - - return (changes, src) - - @defer.inlineCallbacks - def submitChanges(self, changes, request, src): - master = request.site.buildbot_service.master - for chdict in changes: - change = yield master.addChange(src=src, **chdict) - log.msg("injected change %s" % change) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/changes.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/changes.py deleted file mode 100644 index e579669e..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/changes.py +++ /dev/null @@ -1,71 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -from zope.interface import implements -from twisted.python import components -from twisted.web.resource import NoResource - -from buildbot.changes.changes import Change -from buildbot.status.web.base import HtmlResource, IBox, Box - -class ChangeResource(HtmlResource): - def __init__(self, changeid): - self.changeid = changeid - self.pageTitle = "Change #%d" % changeid - - def content(self, req, cxt): - d = self.getStatus(req).getChange(self.changeid) - def cb(change): - if not change: - return "No change number %d" % self.changeid - templates = req.site.buildbot_service.templates - cxt['c'] = change.asDict() - template = templates.get_template("change.html") - data = template.render(cxt) - return data - d.addCallback(cb) - return d - -# /changes/NN -class ChangesResource(HtmlResource): - - def content(self, req, cxt): - cxt['sources'] = self.getStatus(req).getChangeSources() - template = req.site.buildbot_service.templates.get_template("change_sources.html") - return template.render(**cxt) - - def getChild(self, path, req): - try: - changeid = int(path) - except ValueError: - return NoResource("Expected a change number") - - return ChangeResource(changeid) - -class ChangeBox(components.Adapter): - implements(IBox) - - def getBox(self, req): - url = req.childLink("../changes/%d" % self.original.number) - template = req.site.buildbot_service.templates.get_template("change_macros.html") - text = template.module.box_contents(url=url, - who=self.original.getShortAuthor(), - pageTitle=self.original.comments, - revision=self.original.revision, - project=self.original.project) - return Box([text], class_="Change") -components.registerAdapter(ChangeBox, Change, IBox) - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/console.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/console.py deleted file mode 100644 index 60eb67c0..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/console.py +++ /dev/null @@ -1,744 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -import time -import operator -import re -import urllib -from twisted.internet import defer -from buildbot import util -from buildbot.status import builder -from buildbot.status.web.base import HtmlResource -from buildbot.changes import changes - -class DoesNotPassFilter(Exception): pass # Used for filtering revs - -def getResultsClass(results, prevResults, inProgress): - """Given the current and past results, return the class that will be used - by the css to display the right color for a box.""" - - if inProgress: - return "running" - - if results is None: - return "notstarted" - - if results == builder.SUCCESS: - return "success" - - if results == builder.WARNINGS: - return "warnings" - - if results == builder.FAILURE: - if not prevResults: - # This is the bottom box. We don't know if the previous one failed - # or not. We assume it did not. - return "failure" - - if prevResults != builder.FAILURE: - # This is a new failure. - return "failure" - else: - # The previous build also failed. - return "failure-again" - - # Any other results? Like EXCEPTION? - return "exception" - -class ANYBRANCH: pass # a flag value, used below - -class DevRevision: - """Helper class that contains all the information we need for a revision.""" - - def __init__(self, change): - self.revision = change.revision - self.comments = change.comments - self.who = change.who - self.date = change.getTime() - self.revlink = getattr(change, 'revlink', None) - self.when = change.when - self.repository = change.repository - self.project = change.project - - -class DevBuild: - """Helper class that contains all the information we need for a build.""" - - def __init__(self, revision, build, details): - self.revision = revision - self.results = build.getResults() - self.number = build.getNumber() - self.isFinished = build.isFinished() - self.text = build.getText() - self.eta = build.getETA() - self.details = details - self.when = build.getTimes()[0] - #TODO: support multiple sourcestamps - self.source = build.getSourceStamps()[0] - - -class ConsoleStatusResource(HtmlResource): - """Main console class. It displays a user-oriented status page. - Every change is a line in the page, and it shows the result of the first - build with this change for each slave.""" - - def __init__(self, orderByTime=False): - HtmlResource.__init__(self) - - self.status = None - - if orderByTime: - self.comparator = TimeRevisionComparator() - else: - self.comparator = IntegerRevisionComparator() - - def getPageTitle(self, request): - status = self.getStatus(request) - title = status.getTitle() - if title: - return "BuildBot: %s" % title - else: - return "BuildBot" - - def getChangeManager(self, request): - return request.site.buildbot_service.parent.change_svc - - ## - ## Data gathering functions - ## - - def getHeadBuild(self, builder): - """Get the most recent build for the given builder. - """ - build = builder.getBuild(-1) - - # HACK: Work around #601, the head build may be None if it is - # locked. - if build is None: - build = builder.getBuild(-2) - - return build - - def fetchChangesFromHistory(self, status, max_depth, max_builds, debugInfo): - """Look at the history of the builders and try to fetch as many changes - as possible. We need this when the main source does not contain enough - sourcestamps. - - max_depth defines how many builds we will parse for a given builder. - max_builds defines how many builds total we want to parse. This is to - limit the amount of time we spend in this function. - - This function is sub-optimal, but the information returned by this - function is cached, so this function won't be called more than once. - """ - - allChanges = list() - build_count = 0 - for builderName in status.getBuilderNames()[:]: - if build_count > max_builds: - break - - builder = status.getBuilder(builderName) - build = self.getHeadBuild(builder) - depth = 0 - while build and depth < max_depth and build_count < max_builds: - depth += 1 - build_count += 1 - sourcestamp = build.getSourceStamps()[0] - allChanges.extend(sourcestamp.changes[:]) - build = build.getPreviousBuild() - - debugInfo["source_fetch_len"] = len(allChanges) - return allChanges - - @defer.inlineCallbacks - def getAllChanges(self, request, status, debugInfo): - master = request.site.buildbot_service.master - - chdicts = yield master.db.changes.getRecentChanges(25) - - # convert those to Change instances - allChanges = yield defer.gatherResults([ - changes.Change.fromChdict(master, chdict) - for chdict in chdicts ]) - - allChanges.sort(key=self.comparator.getSortingKey()) - - # Remove the dups - prevChange = None - newChanges = [] - for change in allChanges: - rev = change.revision - if not prevChange or rev != prevChange.revision: - newChanges.append(change) - prevChange = change - allChanges = newChanges - - defer.returnValue(allChanges) - - def getBuildDetails(self, request, builderName, build): - """Returns an HTML list of failures for a given build.""" - details = {} - if not build.getLogs(): - return details - - for step in build.getSteps(): - (result, reason) = step.getResults() - if result == builder.FAILURE: - name = step.getName() - - # Remove html tags from the error text. - stripHtml = re.compile(r'<.*?>') - strippedDetails = stripHtml.sub('', ' '.join(step.getText())) - - details['buildername'] = builderName - details['status'] = strippedDetails - details['reason'] = reason - logs = details['logs'] = [] - - if step.getLogs(): - for log in step.getLogs(): - logname = log.getName() - logurl = request.childLink( - "../builders/%s/builds/%s/steps/%s/logs/%s" % - (urllib.quote(builderName), - build.getNumber(), - urllib.quote(name), - urllib.quote(logname))) - logs.append(dict(url=logurl, name=logname)) - return details - - def getBuildsForRevision(self, request, builder, builderName, codebase, - lastRevision, numBuilds, debugInfo): - """Return the list of all the builds for a given builder that we will - need to be able to display the console page. We start by the most recent - build, and we go down until we find a build that was built prior to the - last change we are interested in.""" - - revision = lastRevision - - builds = [] - build = self.getHeadBuild(builder) - number = 0 - while build and number < numBuilds: - debugInfo["builds_scanned"] += 1 - - got_rev = None - sourceStamps = build.getSourceStamps(absolute=True) - - # The console page cannot handle builds that have more than 1 revision - if codebase is not None: - # Get the last revision in this build for this codebase. - for ss in sourceStamps: - if ss.codebase == codebase: - got_rev = ss.revision - break - elif len(sourceStamps) == 1: - ss = sourceStamps[0] - # Get the last revision in this build. - got_rev = ss.revision - - # We ignore all builds that don't have last revisions. - # TODO(nsylvain): If the build is over, maybe it was a problem - # with the update source step. We need to find a way to tell the - # user that his change might have broken the source update. - if got_rev is not None: - number += 1 - details = self.getBuildDetails(request, builderName, build) - devBuild = DevBuild(got_rev, build, details) - builds.append(devBuild) - - # Now break if we have enough builds. - current_revision = self.getChangeForBuild( - build, revision) - if self.comparator.isRevisionEarlier( - devBuild, current_revision): - break - - build = build.getPreviousBuild() - - return builds - - def getChangeForBuild(self, build, revision): - if not build or not build.getChanges(): # Forced build - return DevBuild(revision, build, None) - - for change in build.getChanges(): - if change.revision == revision: - return change - - # No matching change, return the last change in build. - changes = list(build.getChanges()) - changes.sort(key=self.comparator.getSortingKey()) - return changes[-1] - - def getAllBuildsForRevision(self, status, request, codebase, lastRevision, - numBuilds, categories, builders, debugInfo): - """Returns a dictionary of builds we need to inspect to be able to - display the console page. The key is the builder name, and the value is - an array of build we care about. We also returns a dictionary of - builders we care about. The key is it's category. - - codebase is the codebase to get revisions from - lastRevision is the last revision we want to display in the page. - categories is a list of categories to display. It is coming from the - HTTP GET parameters. - builders is a list of builders to display. It is coming from the HTTP - GET parameters. - """ - - allBuilds = dict() - - # List of all builders in the dictionary. - builderList = dict() - - debugInfo["builds_scanned"] = 0 - # Get all the builders. - builderNames = status.getBuilderNames()[:] - for builderName in builderNames: - builder = status.getBuilder(builderName) - - # Make sure we are interested in this builder. - if categories and builder.category not in categories: - continue - if builders and builderName not in builders: - continue - - # We want to display this builder. - category = builder.category or "default" - # Strip the category to keep only the text before the first |. - # This is a hack to support the chromium usecase where they have - # multiple categories for each slave. We use only the first one. - # TODO(nsylvain): Create another way to specify "display category" - # in master.cfg. - category = category.split('|')[0] - if not builderList.get(category): - builderList[category] = [] - - # Append this builder to the dictionary of builders. - builderList[category].append(builderName) - # Set the list of builds for this builder. - allBuilds[builderName] = self.getBuildsForRevision(request, - builder, - builderName, - codebase, - lastRevision, - numBuilds, - debugInfo) - - return (builderList, allBuilds) - - - ## - ## Display functions - ## - - def displayCategories(self, builderList, debugInfo): - """Display the top category line.""" - - count = 0 - for category in builderList: - count += len(builderList[category]) - - categories = builderList.keys() - categories.sort() - - cs = [] - - for category in categories: - c = {} - - c["name"] = category - - # To be able to align the table correctly, we need to know - # what percentage of space this category will be taking. This is - # (#Builders in Category) / (#Builders Total) * 100. - c["size"] = (len(builderList[category]) * 100) / count - cs.append(c) - - return cs - - def displaySlaveLine(self, status, builderList, debugInfo): - """Display a line the shows the current status for all the builders we - care about.""" - - nbSlaves = 0 - - # Get the number of builders. - for category in builderList: - nbSlaves += len(builderList[category]) - - # Get the categories, and order them alphabetically. - categories = builderList.keys() - categories.sort() - - slaves = {} - - # For each category, we display each builder. - for category in categories: - slaves[category] = [] - # For each builder in this category, we set the build info and we - # display the box. - for builder in builderList[category]: - s = {} - s["color"] = "notstarted" - s["pageTitle"] = builder - s["url"] = "./builders/%s" % urllib.quote(builder) - state, builds = status.getBuilder(builder).getState() - # Check if it's offline, if so, the box is purple. - if state == "offline": - s["color"] = "offline" - else: - # If not offline, then display the result of the last - # finished build. - build = self.getHeadBuild(status.getBuilder(builder)) - while build and not build.isFinished(): - build = build.getPreviousBuild() - - if build: - s["color"] = getResultsClass(build.getResults(), None, - False) - - slaves[category].append(s) - - return slaves - - def displayStatusLine(self, builderList, allBuilds, revision, debugInfo): - """Display the boxes that represent the status of each builder in the - first build "revision" was in. Returns an HTML list of errors that - happened during these builds.""" - - details = [] - nbSlaves = 0 - for category in builderList: - nbSlaves += len(builderList[category]) - - # Sort the categories. - categories = builderList.keys() - categories.sort() - - builds = {} - - # Display the boxes by category group. - for category in categories: - - builds[category] = [] - - # Display the boxes for each builder in this category. - for builder in builderList[category]: - introducedIn = None - firstNotIn = None - - # Find the first build that does not include the revision. - for build in allBuilds[builder]: - if self.comparator.isRevisionEarlier(build, revision): - firstNotIn = build - break - else: - introducedIn = build - - # Get the results of the first build with the revision, and the - # first build that does not include the revision. - results = None - previousResults = None - if introducedIn: - results = introducedIn.results - if firstNotIn: - previousResults = firstNotIn.results - - isRunning = False - if introducedIn and not introducedIn.isFinished: - isRunning = True - - url = "./waterfall" - pageTitle = builder - tag = "" - current_details = {} - if introducedIn: - current_details = introducedIn.details or "" - url = "./buildstatus?builder=%s&number=%s" % (urllib.quote(builder), - introducedIn.number) - pageTitle += " " - pageTitle += urllib.quote(' '.join(introducedIn.text), ' \n\\/:') - - builderStrip = builder.replace(' ', '') - builderStrip = builderStrip.replace('(', '') - builderStrip = builderStrip.replace(')', '') - builderStrip = builderStrip.replace('.', '') - tag = "Tag%s%s" % (builderStrip, introducedIn.number) - - if isRunning: - pageTitle += ' ETA: %ds' % (introducedIn.eta or 0) - - resultsClass = getResultsClass(results, previousResults, isRunning) - - b = {} - b["url"] = url - b["pageTitle"] = pageTitle - b["color"] = resultsClass - b["tag"] = tag - - builds[category].append(b) - - # If the box is red, we add the explaination in the details - # section. - if current_details and resultsClass == "failure": - details.append(current_details) - - return (builds, details) - - def filterRevisions(self, revisions, filter=None, max_revs=None): - """Filter a set of revisions based on any number of filter criteria. - If specified, filter should be a dict with keys corresponding to - revision attributes, and values of 1+ strings""" - if not filter: - if max_revs is None: - for rev in reversed(revisions): - yield DevRevision(rev) - else: - for index,rev in enumerate(reversed(revisions)): - if index >= max_revs: - break - yield DevRevision(rev) - else: - for index, rev in enumerate(reversed(revisions)): - if max_revs and index >= max_revs: - break - try: - for field,acceptable in filter.iteritems(): - if not hasattr(rev, field): - raise DoesNotPassFilter - if type(acceptable) in (str, unicode): - if getattr(rev, field) != acceptable: - raise DoesNotPassFilter - elif type(acceptable) in (list, tuple, set): - if getattr(rev, field) not in acceptable: - raise DoesNotPassFilter - yield DevRevision(rev) - except DoesNotPassFilter: - pass - - def displayPage(self, request, status, builderList, allBuilds, codebase, - revisions, categories, repository, project, branch, - debugInfo): - """Display the console page.""" - # Build the main template directory with all the informations we have. - subs = dict() - subs["branch"] = branch or 'trunk' - subs["repository"] = repository - subs["project"] = project - subs["codebase"] = codebase - if categories: - subs["categories"] = ' '.join(categories) - subs["time"] = time.strftime("%a %d %b %Y %H:%M:%S", - time.localtime(util.now())) - subs["debugInfo"] = debugInfo - subs["ANYBRANCH"] = ANYBRANCH - - if builderList: - subs["categories"] = self.displayCategories(builderList, debugInfo) - subs['slaves'] = self.displaySlaveLine(status, builderList, debugInfo) - else: - subs["categories"] = [] - - subs['revisions'] = [] - - # For each revision we show one line - for revision in revisions: - r = {} - - # Fill the dictionary with this new information - r['id'] = revision.revision - r['link'] = revision.revlink - r['who'] = revision.who - r['date'] = revision.date - r['comments'] = revision.comments - r['repository'] = revision.repository - r['project'] = revision.project - - # Display the status for all builders. - (builds, details) = self.displayStatusLine(builderList, - allBuilds, - revision, - debugInfo) - r['builds'] = builds - r['details'] = details - - # Calculate the td span for the comment and the details. - r["span"] = len(builderList) + 2 - - subs['revisions'].append(r) - - # - # Display the footer of the page. - # - debugInfo["load_time"] = time.time() - debugInfo["load_time"] - return subs - - - def content(self, request, cxt): - "This method builds the main console view display." - - reload_time = None - # Check if there was an arg. Don't let people reload faster than - # every 15 seconds. 0 means no reload. - if "reload" in request.args: - try: - reload_time = int(request.args["reload"][0]) - if reload_time != 0: - reload_time = max(reload_time, 15) - except ValueError: - pass - - request.setHeader('Cache-Control', 'no-cache') - - # Sets the default reload time to 60 seconds. - if not reload_time: - reload_time = 60 - - # Append the tag to refresh the page. - if reload_time is not None and reload_time != 0: - cxt['refresh'] = reload_time - - # Debug information to display at the end of the page. - debugInfo = cxt['debuginfo'] = dict() - debugInfo["load_time"] = time.time() - - # get url parameters - # Categories to show information for. - categories = request.args.get("category", []) - # List of all builders to show on the page. - builders = request.args.get("builder", []) - # Repo used to filter the changes shown. - repository = request.args.get("repository", [None])[0] - # Project used to filter the changes shown. - project = request.args.get("project", [None])[0] - # Branch used to filter the changes shown. - branch = request.args.get("branch", [ANYBRANCH])[0] - # Codebase used to filter the changes shown. - codebase = request.args.get("codebase", [None])[0] - # List of all the committers name to display on the page. - devName = request.args.get("name", []) - - # and the data we want to render - status = self.getStatus(request) - - # Keep only the revisions we care about. - # By default we process the last 40 revisions. - # If a dev name is passed, we look for the changes by this person in the - # last 80 revisions. - numRevs = int(request.args.get("revs", [40])[0]) - if devName: - numRevs *= 2 - numBuilds = numRevs - - # Get all changes we can find. This is a DB operation, so it must use - # a deferred. - d = self.getAllChanges(request, status, debugInfo) - def got_changes(allChanges): - debugInfo["source_all"] = len(allChanges) - - revFilter = {} - if branch != ANYBRANCH: - revFilter['branch'] = branch - if devName: - revFilter['who'] = devName - if repository: - revFilter['repository'] = repository - if project: - revFilter['project'] = project - if codebase is not None: - revFilter['codebase'] = codebase - revisions = list(self.filterRevisions(allChanges, max_revs=numRevs, - filter=revFilter)) - debugInfo["revision_final"] = len(revisions) - - # Fetch all the builds for all builders until we get the next build - # after lastRevision. - builderList = None - allBuilds = None - if revisions: - lastRevision = revisions[len(revisions) - 1].revision - debugInfo["last_revision"] = lastRevision - - (builderList, allBuilds) = self.getAllBuildsForRevision(status, - request, - codebase, - lastRevision, - numBuilds, - categories, - builders, - debugInfo) - - debugInfo["added_blocks"] = 0 - - cxt.update(self.displayPage(request, status, builderList, - allBuilds, codebase, revisions, - categories, repository, project, - branch, debugInfo)) - - templates = request.site.buildbot_service.templates - template = templates.get_template("console.html") - data = template.render(cxt) - return data - d.addCallback(got_changes) - return d - -class RevisionComparator(object): - """Used for comparing between revisions, as some - VCS use a plain counter for revisions (like SVN) - while others use different concepts (see Git). - """ - - # TODO (avivby): Should this be a zope interface? - - def isRevisionEarlier(self, first_change, second_change): - """Used for comparing 2 changes""" - raise NotImplementedError - - def isValidRevision(self, revision): - """Checks whether the revision seems like a VCS revision""" - raise NotImplementedError - - def getSortingKey(self): - raise NotImplementedError - -class TimeRevisionComparator(RevisionComparator): - def isRevisionEarlier(self, first, second): - return first.when < second.when - - def isValidRevision(self, revision): - return True # No general way of determining - - def getSortingKey(self): - return operator.attrgetter('when') - -class IntegerRevisionComparator(RevisionComparator): - def isRevisionEarlier(self, first, second): - try: - return int(first.revision) < int(second.revision) - except (TypeError, ValueError): - return False - - def isValidRevision(self, revision): - try: - int(revision) - return True - except: - return False - - def getSortingKey(self): - return operator.attrgetter('revision') - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/feeds.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/feeds.py deleted file mode 100644 index f29a11ca..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/feeds.py +++ /dev/null @@ -1,275 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -# This module enables ATOM and RSS feeds from webstatus. -# -# It is based on "feeder.py" which was part of the Buildbot -# configuration for the Subversion project. The original file was -# created by Lieven Gobaerts and later adjusted by API -# (apinheiro@igalia.coma) and also here -# http://code.google.com/p/pybots/source/browse/trunk/master/Feeder.py -# -# All subsequent changes to feeder.py where made by Chandan-Dutta -# Chowdhury <chandan-dutta.chowdhury @ hp.com> and Gareth Armstrong -# <gareth.armstrong @ hp.com>. -# -# Those modifications are as follows: -# 1) the feeds are usable from baseweb.WebStatus -# 2) feeds are fully validated ATOM 1.0 and RSS 2.0 feeds, verified -# with code from http://feedvalidator.org -# 3) nicer xml output -# 4) feeds can be filtered as per the /waterfall display with the -# builder and category filters -# 5) cleaned up white space and imports -# -# Finally, the code was directly integrated into these two files, -# buildbot/status/web/feeds.py (you're reading it, ;-)) and -# buildbot/status/web/baseweb.py. - -import os -import re -import time -from twisted.web import resource -from buildbot.status import results - -class XmlResource(resource.Resource): - contentType = "text/xml; charset=UTF-8" - docType = '' - - def getChild(self, name, request): - return self - - def render(self, request): - data = self.content(request) - request.setHeader("content-type", self.contentType) - if request.method == "HEAD": - request.setHeader("content-length", len(data)) - return '' - return data - -_abbr_day = [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] -_abbr_mon = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', - 'Sep', 'Oct', 'Nov', 'Dec'] - -def rfc822_time(tstamp): - res = time.strftime("%%s, %d %%s %Y %H:%M:%S GMT", - tstamp) - res = res % (_abbr_day[tstamp.tm_wday], _abbr_mon[tstamp.tm_mon]) - return res - -class FeedResource(XmlResource): - pageTitle = None - link = 'http://dummylink' - language = 'en-us' - description = 'Dummy rss' - status = None - - def __init__(self, status, categories=None, pageTitle=None): - self.status = status - self.categories = categories - self.pageTitle = pageTitle - self.title = self.status.getTitle() - self.link = self.status.getBuildbotURL() - self.description = 'List of builds' - self.pubdate = time.gmtime(int(time.time())) - self.user = self.getEnv(['USER', 'USERNAME'], 'buildmaster') - self.hostname = self.getEnv(['HOSTNAME', 'COMPUTERNAME'], - 'buildmaster') - self.children = {} - - def getEnv(self, keys, fallback): - for key in keys: - if key in os.environ: - return os.environ[key] - return fallback - - def getBuilds(self, request): - builds = [] - # THIS is lifted straight from the WaterfallStatusResource Class in - # status/web/waterfall.py - # - # we start with all Builders available to this Waterfall: this is - # limited by the config-file -time categories= argument, and defaults - # to all defined Builders. - allBuilderNames = self.status.getBuilderNames(categories=self.categories) - builders = [self.status.getBuilder(name) for name in allBuilderNames] - - # but if the URL has one or more builder= arguments (or the old show= - # argument, which is still accepted for backwards compatibility), we - # use that set of builders instead. We still don't show anything - # outside the config-file time set limited by categories=. - showBuilders = request.args.get("show", []) - showBuilders.extend(request.args.get("builder", [])) - if showBuilders: - builders = [b for b in builders if b.name in showBuilders] - - # now, if the URL has one or category= arguments, use them as a - # filter: only show those builders which belong to one of the given - # categories. - showCategories = request.args.get("category", []) - if showCategories: - builders = [b for b in builders if b.category in showCategories] - - failures_only = request.args.get("failures_only", ["false"]) - failures_only = failures_only[0] not in ('false', '0', 'no', 'off') - - maxFeeds = 25 - - # Copy all failed builds in a new list. - # This could clearly be implemented much better if we had - # access to a global list of builds. - for b in builders: - if failures_only: - res = (results.FAILURE,) - else: - res = None - builds.extend(b.generateFinishedBuilds(results=res, max_search=maxFeeds)) - - # Sort build list by date, youngest first. - # To keep compatibility with python < 2.4, use this for sorting instead: - # We apply Decorate-Sort-Undecorate - deco = [(build.getTimes(), build) for build in builds] - deco.sort() - deco.reverse() - builds = [build for (b1, build) in deco] - - if builds: - builds = builds[:min(len(builds), maxFeeds)] - return builds - - def content(self, request): - builds = self.getBuilds(request) - - build_cxts = [] - - for build in builds: - start, finished = build.getTimes() - finishedTime = time.gmtime(int(finished)) - link = re.sub(r'index.html', "", self.status.getURLForThing(build)) - - # title: trunk r22191 (plus patch) failed on - # 'i686-debian-sarge1 shared gcc-3.3.5' - ss_list = build.getSourceStamps() - all_got_revisions = build.getAllGotRevisions() - src_cxts = [] - for ss in ss_list: - sc = {} - sc['codebase'] = ss.codebase - if (ss.branch is None and ss.revision is None and ss.patch is None - and not ss.changes): - sc['repository'] = None - sc['branch'] = None - sc['revision'] = "Latest revision" - else: - sc['repository'] = ss.repository - sc['branch'] = ss.branch - got_revision = all_got_revisions.get(ss.codebase, None) - if got_revision: - sc['revision'] = got_revision - else: - sc['revision'] = str(ss.revision) - if ss.patch: - sc['revision'] += " (plus patch)" - if ss.changes: - pass - src_cxts.append(sc) - res = build.getResults() - pageTitle = ('Builder "%s": %s' % - (build.getBuilder().getName(), results.Results[res])) - - # Add information about the failing steps. - failed_steps = [] - log_lines = [] - for s in build.getSteps(): - res = s.getResults()[0] - if res not in (results.SUCCESS, results.WARNINGS, - results.SKIPPED): - failed_steps.append(s.getName()) - - # Add the last 30 lines of each log. - for log in s.getLogs(): - log_lines.append('Last lines of build log "%s":' % - log.getName()) - log_lines.append([]) - try: - logdata = log.getText() - except IOError: - # Probably the log file has been removed - logdata ='** log file not available **' - unilist = list() - for line in logdata.split('\n')[-30:]: - unilist.append(unicode(line,'utf-8')) - log_lines.extend(unilist) - - bc = {} - bc['sources'] = src_cxts - bc['date'] = rfc822_time(finishedTime) - bc['summary_link'] = ('%sbuilders/%s' % - (self.link, - build.getBuilder().getName())) - bc['name'] = build.getBuilder().getName() - bc['number'] = build.getNumber() - bc['responsible_users'] = build.getResponsibleUsers() - bc['failed_steps'] = failed_steps - bc['pageTitle'] = pageTitle - bc['link'] = link - bc['log_lines'] = log_lines - - if finishedTime is not None: - bc['rfc822_pubdate'] = rfc822_time(finishedTime) - bc['rfc3339_pubdate'] = time.strftime("%Y-%m-%dT%H:%M:%SZ", - finishedTime) - - # Every RSS/Atom item must have a globally unique ID - guid = ('tag:%s@%s,%s:%s' % - (self.user, self.hostname, - time.strftime("%Y-%m-%d", finishedTime), - time.strftime("%Y%m%d%H%M%S", finishedTime))) - bc['guid'] = guid - - build_cxts.append(bc) - - pageTitle = self.pageTitle - if not pageTitle: - pageTitle = 'Build status of %s' % self.title - - cxt = {} - cxt['pageTitle'] = pageTitle - cxt['title_url'] = self.link - cxt['title'] = self.title - cxt['language'] = self.language - cxt['description'] = self.description - if self.pubdate is not None: - cxt['rfc822_pubdate'] = rfc822_time( self.pubdate) - cxt['rfc3339_pubdate'] = time.strftime("%Y-%m-%dT%H:%M:%SZ", - self.pubdate) - - cxt['builds'] = build_cxts - template = request.site.buildbot_service.templates.get_template(self.template_file) - return template.render(**cxt).encode('utf-8').strip() - -class Rss20StatusResource(FeedResource): - # contentType = 'application/rss+xml' (browser dependent) - template_file = 'feed_rss20.xml' - - def __init__(self, status, categories=None, pageTitle=None): - FeedResource.__init__(self, status, categories, pageTitle) - -class Atom10StatusResource(FeedResource): - # contentType = 'application/atom+xml' (browser dependent) - template_file = 'feed_atom10.xml' - - def __init__(self, status, categories=None, pageTitle=None): - FeedResource.__init__(self, status, categories, pageTitle) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/files/bg_gradient.jpg b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/files/bg_gradient.jpg Binary files differdeleted file mode 100644 index 6c2e5ddf..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/files/bg_gradient.jpg +++ /dev/null diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/files/default.css b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/files/default.css deleted file mode 100644 index d769f73a..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/files/default.css +++ /dev/null @@ -1,604 +0,0 @@ -body.interface { - margin-left: 30px; - margin-right: 30px; - margin-top: 20px; - margin-bottom: 50px; - padding: 0; - background: url(bg_gradient.jpg) repeat-x; - font-family: Verdana, sans-serif; - font-size: 10px; - background-color: #fff; - color: #333; -} - -.auth { -position:absolute; -top:5px; -right:40px; -} - -.alert { - color: #c30000; - background-color: #f2dcdc; - padding: 5px 5px 5px 25px; - margin-bottom: 20px; - border-top:1px solid #ccc; - border-bottom:1px solid #ccc; - border-color: #c30000; - font-size: 20px; -} -a:link,a:visited,a:active { - color: #444; -} - -table { - border-spacing: 1px 1px; -} - -table td { - padding: 3px 4px 3px 4px; - text-align: center; -} - -.Project { - min-width: 6em; -} - -.LastBuild,.Activity { - padding: 0 0 0 4px; -} - -.LastBuild,.Activity,.Builder,.BuildStep { - min-width: 5em; -} - -/* Chromium Specific styles */ -div.BuildResultInfo { - color: #444; -} - -div.Announcement { - margin-bottom: 1em; -} - -div.Announcement>a:hover { - color: black; -} - -div.Announcement>div.Notice { - background-color: #afdaff; - padding: 0.5em; - font-size: 16px; - text-align: center; -} - -div.Announcement>div.Open { - border: 3px solid #8fdf5f; - padding: 0.5em; - font-size: 16px; - text-align: center; -} - -div.Announcement>div.Closed { - border: 5px solid #e98080; - padding: 0.5em; - font-size: 24px; - font-weight: bold; - text-align: center; -} - -td.Time { - color: #000; - border-bottom: 1px solid #aaa; - background-color: #eee; -} - -td.Activity,td.Change,td.Builder { - color: #333333; - background-color: #CCCCCC; -} - -td.Change { - border-radius: 5px; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; -} - -td.Event { - color: #777; - background-color: #ddd; - border-radius: 5px; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; -} - -td.Activity { - border-top-left-radius: 10px; - -webkit-border-top-left-radius: 10px; - -moz-border-radius-topleft: 10px; - min-height: 20px; - padding: 2px 0 2px 0; -} - -td.idle,td.waiting,td.offline,td.building { - border-top-left-radius: 0px; - -webkit-border-top-left-radius: 0px; - -moz-border-radius-topleft: 0px; -} - -.LastBuild { - border-top-left-radius: 5px; - -webkit-border-top-left-radius: 5px; - -moz-border-radius-topleft: 5px; - border-top-right-radius: 5px; - -webkit-border-top-right-radius: 5px; - -moz-border-radius-topright: 5px; -} - -/* Console view styles */ -td.DevRev { - padding: 4px 8px 4px 8px; - color: #333333; - border-top-left-radius: 5px; - -webkit-border-top-left-radius: 5px; - -moz-border-radius-topleft: 5px; - background-color: #eee; - width: 1%; -} - -td.DevRevCollapse { - border-bottom-left-radius: 5px; - -webkit-border-bottom-left-radius: 5px; - -moz-border-radius-bottomleft: 5px; -} - -td.DevName { - padding: 4px 8px 4px 8px; - color: #333333; - background-color: #eee; - width: 1%; - text-align: left; -} - -td.DevStatus { - padding: 4px 4px 4px 4px; - color: #333333; - background-color: #eee; -} - -td.DevSlave { - padding: 4px 4px 4px 4px; - color: #333333; - background-color: #eee; -} - -td.first { - border-top-left-radius: 5px; - -webkit-border-top-left-radius: 5px; - -moz-border-radius-topleft: 5px; -} - -td.last { - border-top-right-radius: 5px; - -webkit-border-top-right-radius: 5px; - -moz-border-radius-topright: 5px; -} - -td.DevStatusCategory { - border-radius: 5px; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-width: 1px; - border-style: solid; -} - -td.DevStatusCollapse { - border-bottom-right-radius: 5px; - -webkit-border-bottom-right-radius: 5px; - -moz-border-radius-bottomright: 5px; -} - -td.DevDetails { - font-weight: normal; - padding: 8px 8px 8px 8px; - color: #333333; - background-color: #eee; - text-align: left; -} - -td.DevDetails li a { - padding-right: 5px; -} - -td.DevComment { - font-weight: normal; - padding: 8px 8px 8px 8px; - color: #333333; - background-color: #eee; - text-align: left; -} - -td.DevBottom { - border-bottom-right-radius: 5px; - -webkit-border-bottom-right-radius: 5px; - -moz-border-radius-bottomright: 5px; - border-bottom-left-radius: 5px; - -webkit-border-bottom-left-radius: 5px; - -moz-border-radius-bottomleft: 5px; -} - -td.Alt { - background-color: #ddd; -} - -.legend { - border-radius: 5px !important; - -webkit-border-radius: 5px !important; - -moz-border-radius: 5px !important; - width: 100px; - max-width: 100px; - text-align: center; - padding: 2px 2px 2px 2px; - height: 14px; - white-space: nowrap; -} - -.DevStatusBox { - text-align: center; - height: 20px; - padding: 0 2px; - line-height: 0; - white-space: nowrap; -} - -.DevStatusBox a { - opacity: 0.85; - border-width: 1px; - border-style: solid; - border-radius: 4px; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - display: block; - width: 90%; - height: 20px; - line-height: 20px; - margin-left: auto; - margin-right: auto; -} - -.DevSlaveBox { - text-align: center; - height: 10px; - padding: 0 2px; - line-height: 0; - white-space: nowrap; -} - -.DevSlaveBox a { - opacity: 0.85; - border-width: 1px; - border-style: solid; - border-radius: 4px; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - display: block; - width: 90%; - height: 10px; - line-height: 20px; - margin-left: auto; - margin-right: auto; -} - -a.noround { - border-radius: 0px; - -webkit-border-radius: 0px; - -moz-border-radius: 0px; - position: relative; - margin-top: -8px; - margin-bottom: -8px; - height: 36px; - border-top-width: 0; - border-bottom-width: 0; -} - -a.begin { - border-top-width: 1px; - position: relative; - margin-top: 0px; - margin-bottom: -7px; - height: 27px; - border-top-left-radius: 4px; - -webkit-border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; - border-top-right-radius: 4px; - -webkit-border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; -} - -a.end { - border-bottom-width: 1px; - position: relative; - margin-top: -7px; - margin-bottom: 0px; - height: 27px; - border-bottom-left-radius: 4px; - -webkit-border-bottom-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - border-bottom-right-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - -moz-border-radius-bottomright: 4px; -} - -.center_align { - text-align: center; -} - -.right_align { - text-align: right; -} - -.left_align { - text-align: left; -} - -div.BuildWaterfall { - border-radius: 7px; - -webkit-border-radius: 7px; - -moz-border-radius: 7px; - position: absolute; - left: 0px; - top: 0px; - background-color: #FFFFFF; - padding: 4px 4px 4px 4px; - float: left; - display: none; - border-width: 1px; - border-style: solid; -} - -/* LastBuild, BuildStep states */ -.success { - color: #000; - background-color: #8d4; - border-color: #4F8530; -} - -.failure { - color: #000; - background-color: #e88; - border-color: #A77272; -} - -.failure-again { - color: #000; - background-color: #eA9; - border-color: #A77272; -} - -.warnings { - color: #FFFFFF; - background-color: #fa3; - border-color: #C29D46; -} - -.skipped { - color: #000; - background: #AADDEE; - border-color: #AADDEE; -} - -.exception,.retry { - color: #FFFFFF; - background-color: #c6c; - border-color: #ACA0B3; -} - -.start { - color: #000; - background-color: #ccc; - border-color: #ccc; -} - -.running,.waiting,td.building { - color: #000; - background-color: #fd3; - border-color: #C5C56D; -} - -.paused { - color: #FFFFFF; - background-color: #8080FF; - border-color: #dddddd; -} - -.offline,td.offline { - color: #FFFFFF; - background-color: #777777; - border-color: #dddddd; -} - - -.start { - border-bottom-left-radius: 10px; - -webkit-border-bottom-left-radius: 10px; - -moz-border-radius-bottomleft: 10px; - border-bottom-right-radius: 10px; - -webkit-border-bottom-right-radius: 10px; - -moz-border-radius-bottomright: 10px; -} - -.notstarted { - border-width: 1px; - border-style: solid; - border-color: #aaa; - background-color: #fff; -} - -.closed { - background-color: #ff0000; -} - -.closed .large { - font-size: 1.5em; - font-weight: bolder; -} - -td.Project a:hover,td.start a:hover { - color: #000; -} - -.mini-box { - text-align: center; - height: 20px; - padding: 0 2px; - line-height: 0; - white-space: nowrap; -} - -.mini-box a { - border-radius: 0; - -webkit-border-radius: 0; - -moz-border-radius: 0; - display: block; - width: 100%; - height: 20px; - line-height: 20px; - margin-top: -30px; -} - -.mini-closed { - -box-sizing: border-box; - -webkit-box-sizing: border-box; - border: 4px solid red; -} - -/* 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; -} - -/* column container */ -div.column { - margin: 0 2em 2em 0; - float: left; -} - -/* info tables */ -table.info { - border-spacing: 1px; -} - -table.info td { - padding: 0.1em 1em 0.1em 1em; - text-align: center; -} - -table.info th { - padding: 0.2em 1.5em 0.2em 1.5em; - text-align: center; -} - -table.info td.left { - text-align: left -} - -.alt { - background-color: #f6f6f6; -} - -li { - padding: 0.1em 1em 0.1em 1em; -} - -.result { - padding: 0.3em 1em 0.3em 1em; -} - -/* log view */ -.log * { - vlink: #800080; - font-family: "Courier New", courier, monotype, monospace; -} - -span.stdout { - color: black; -} - -span.stderr { - color: red; -} - -span.header { - color: blue; -} - -/* revision & email */ -.revision .full { - display: none; -} - -.user .email { - display: none; -} - -pre { - white-space: pre-wrap; -} - -/* change comments (use regular colors here) */ -pre.comments>a:link,pre.comments>a:visited { - color: blue; -} - -pre.comments>a:active { - color: purple; -} - -form.command_forcebuild { - border-top: 1px solid black; - padding: .5em; - margin: .5em; -} - -form.command_forcebuild > .row { - border-top: 1px dotted gray; - padding: .5em 0; -} - -form.command_forcebuild .force-textarea > .label { - display: block; -} - -form.command_forcebuild .force-nested > .label { - font-weight: bold; - display: list-item; -} - -form.command_forcebuild .force-any .force-text { - display: inline; -} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/files/favicon.ico b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/files/favicon.ico Binary files differdeleted file mode 100644 index b0b0845d..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/files/favicon.ico +++ /dev/null diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/files/robots.txt b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/files/robots.txt deleted file mode 100644 index 7b5fc8da..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/files/robots.txt +++ /dev/null @@ -1,11 +0,0 @@ -User-agent: * -Disallow: /waterfall -Disallow: /builders -Disallow: /changes -Disallow: /buildslaves -Disallow: /schedulers -Disallow: /one_line_per_build -Disallow: /builders -Disallow: /grid -Disallow: /tgrid -Disallow: /json diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/files/templates_readme.txt b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/files/templates_readme.txt deleted file mode 100644 index 78201578..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/files/templates_readme.txt +++ /dev/null @@ -1,12 +0,0 @@ -This is the directory to place customized templates for webstatus. - -You can find the sources for the templates used in: -buildbot/status/web/templates - -You can copy any of those files to this directory, make changes, and buildbot will automatically -use your modified templates. - -Also of note is that you will not need to restart/reconfig buildbot master to have these changes take effect. - -The syntax of the templates is Jinja2: -http://jinja.pocoo.org/
\ No newline at end of file diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/grid.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/grid.py deleted file mode 100644 index dc4fbd62..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/grid.py +++ /dev/null @@ -1,341 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -from twisted.internet import defer -from buildbot.status.web.base import HtmlResource -from buildbot.status.web.base import build_get_class, path_to_builder, path_to_build -from buildbot.sourcestamp import SourceStamp - -class ANYBRANCH: pass # a flag value, used below - -class GridStatusMixin(object): - def getPageTitle(self, request): - status = self.getStatus(request) - p = status.getTitle() - if p: - return "BuildBot: %s" % p - else: - return "BuildBot" - - # 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 build_cxt(self, request, build): - if not build: - return {} - - if build.isFinished(): - # 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: - text = [ 'building' ] - - name = build.getBuilder().getName() - - cxt = {} - cxt['name'] = name - cxt['got_revision'] = build.getProperty("got_revision") - cxt['DEST'] = build.getProperty("DEST") - cxt['url'] = path_to_build(request, build) - cxt['text'] = text - cxt['class'] = build_get_class(build) - - if build.getProperty("repourl_poky"): - if build.getProperty("repourl_poky") == "git://git.yoctoproject.org/poky": - cxt['repository'] = "poky" - cxt['cgiturl'] = "http://git.yoctoproject.org/cgit/cgit.cgi/poky/commit/?h=" - elif "git://git.yoctoproject.org/poky-contrib" in build.getProperty("repourl_poky"): - cxt['repository'] = "poky-contrib" - cxt['cgiturl'] = "http://git.yoctoproject.org/cgit/cgit.cgi/poky-contrib/commit/?h=" - else: - cxt['repository'] = build.getProperty("repourl_poky") - - if build.getProperty("branch_poky"): - if build.getProperty("branch_poky") == "stage/master_under_test": - cxt['branchshortname']="mut" - else: - cxt['branchshortname']=build.getProperty("branch_poky") - cxt['branch'] = build.getProperty("branch_poky") - if 'cgiturl' in cxt and 'got_revision' in cxt and cxt['cgiturl'] is not None and cxt['got_revision'] is not None: - cxt['cgiturl'] = cxt['cgiturl'] + build.getProperty("branch_poky") + "&id=" + cxt['got_revision'] - if build.getProperty("custom_release_name_nightly"): - cxt['release_name'] = build.getProperty("custom_release_name_nightly") - else: - cxt['commit_desc'] = build.getProperty("commit-description") - - return cxt - - @defer.inlineCallbacks - def builder_cxt(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" - - n_pending = len((yield builder.getPendingBuildRequestStatuses())) - - cxt = { 'url': path_to_builder(request, builder), - 'name': builder.getName(), - 'state': state, - 'n_pending': n_pending } - - defer.returnValue(cxt) - - def getSourceStampKey(self, sourcestamps): - """Given two source stamps, we want to assign them to the same row if - they are the same version of code, even if they differ in minor detail. - - This function returns an appropriate comparison key for that. - """ - # TODO: Maybe order sourcestamps in key by codebases names? - return tuple([(ss.branch, ss.revision, ss.patch) for ss in sourcestamps]) - - def clearRecentBuildsCache(self): - self.__recentBuildsCache__ = {} - - def getRecentBuilds(self, builder, numBuilds, branch): - cache = getattr(self, '__recentBuildsCache__', {}) - key = (builder.getName(), branch, numBuilds) - try: - return cache[key] - except KeyError: - # cache miss, get the value and store it in the cache - result = [b for b in self.__getRecentBuilds(builder, numBuilds, branch)] - cache[key] = result - return result - - def __getRecentBuilds(self, builder, numBuilds, branch): - """ - get a list of most recent builds on given builder - """ - build = builder.getBuild(-1) - num = 0 - while build and num < numBuilds: - start = build.getTimes()[0] - #TODO: support multiple sourcestamps - ss = build.getSourceStamps(absolute=True)[0] - - okay_build = True - - # skip un-started builds - if not start: - okay_build = False - - # skip non-matching branches - if branch != ANYBRANCH and ss.branch != branch: - okay_build = False - - if okay_build: - num += 1 - yield build - - build = build.getPreviousBuild() - return - - 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 - for build in self.getRecentBuilds(builder, numBuilds, branch): - ss = build.getSourceStamps(absolute=True) - key = self.getSourceStampKey(ss) - start = min(x for x in build.getTimes() if x is not None) - if key not in sourcestamps or sourcestamps[key][1] > start: - sourcestamps[key] = (ss, start) - - # now sort those and take the NUMBUILDS most recent - sourcestamps = sorted(sourcestamps.itervalues(), key = lambda stamp: stamp[1]) - sourcestamps = [stamp[0] for stamp in sourcestamps][-numBuilds:] - - return sourcestamps - -class GridStatusResource(HtmlResource, GridStatusMixin): - # TODO: docs - status = None - changemaster = None - - @defer.inlineCallbacks - def content(self, request, cxt): - """This method builds the regular grid display. - That is, build stamps across the top, build hosts down the left side - """ - - # 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) - - cxt['refresh'] = self.get_reload_time(request) - - cxt.update({'categories': categories, - 'branch': branch, - 'ANYBRANCH': ANYBRANCH, - 'stamps': [map(SourceStamp.asDict, sstamp) for sstamp in stamps], - }) - sortedBuilderNames = sorted(status.getBuilderNames()) - - cxt['builders'] = [] - cxt['build_triggers'] = build_triggers = [] - cxt['range'] = range(len(stamps)) - - # For each sstamp we want to know the name of the builder which - # triggered the builds and the buildid of that builder. We'll keep - # that in a list of dicts which align with the stamps objects list. - for _ in range(len(stamps)): - build_triggers.append({'builder': '', 'id': ''}) - - for bn in sortedBuilderNames: - builds = [None] * len(stamps) - - builder = status.getBuilder(bn) - if categories and builder.category not in categories: - continue - - for build in self.getRecentBuilds(builder, numBuilds, branch): - ss = build.getSourceStamps(absolute=True) - key = self.getSourceStampKey(ss) - - for i, sstamp in enumerate(stamps): - if key == self.getSourceStampKey(sstamp) and builds[i] is None: - builds[i] = build - if build_triggers[i].get('builder', None) != 'nightly': - build_triggers[i]['builder'] = bn - build_triggers[i]['id'] = str(build.getNumber()) - - b = yield self.builder_cxt(request, builder) - - b['builds'] = [] - for build in builds: - b['builds'].append(self.build_cxt(request, build)) - - cxt['builders'].append(b) - - self.clearRecentBuildsCache() - template = request.site.buildbot_service.templates.get_template("grid.html") - defer.returnValue(template.render(**cxt)) - - -class TransposedGridStatusResource(HtmlResource, GridStatusMixin): - # TODO: docs - status = None - changemaster = None - default_rev_order = "desc" - - @defer.inlineCallbacks - def content(self, request, cxt): - """This method builds the transposed grid display. - That is, build hosts across the top, build stamps down the left side - """ - - # get url parameters - numBuilds = int(request.args.get("length", [5])[0]) - categories = request.args.get("category", []) - branch = request.args.get("branch", [ANYBRANCH])[0] - if branch == 'trunk': branch = None - - rev_order = request.args.get("rev_order", [self.default_rev_order])[0] - if rev_order not in ["asc", "desc"]: - rev_order = self.default_rev_order - - cxt['refresh'] = self.get_reload_time(request) - - # and the data we want to render - status = self.getStatus(request) - stamps = self.getRecentSourcestamps(status, numBuilds, categories, branch) - - cxt.update({'categories': categories, - 'branch': branch, - 'ANYBRANCH': ANYBRANCH, - 'stamps': [map(SourceStamp.asDict, sstamp) for sstamp in stamps], - }) - - sortedBuilderNames = sorted(status.getBuilderNames()) - - cxt['sorted_builder_names'] = sortedBuilderNames - cxt['builder_builds'] = builder_builds = [] - cxt['builders'] = builders = [] - cxt['build_triggers'] = build_triggers = [] - cxt['range'] = range(len(stamps)) - if rev_order == "desc": - cxt['range'].reverse() - - # For each sstamp we want to know the name of the builder which - # triggered the builds and the buildid of that builder. We'll keep - # that in a list of dicts which align with the stamps objects list. - for _ in range(len(stamps)): - build_triggers.append({'builder': '', 'id': ''}) - - for bn in sortedBuilderNames: - builds = [None] * len(stamps) - - builder = status.getBuilder(bn) - if categories and builder.category not in categories: - continue - - for build in self.getRecentBuilds(builder, numBuilds, branch): - #TODO: support multiple sourcestamps - ss = build.getSourceStamps(absolute=True) - key = self.getSourceStampKey(ss) - - for i, sstamp in enumerate(stamps): - if key == self.getSourceStampKey(sstamp) and builds[i] is None: - builds[i] = build - # At this point we know that there's a build for the sstamp by this builder - # update the corresponding dict in the build_triggers list with the builder - # name and buildid unless 'nightly' is already set as the builder name. - if build_triggers[i].get('builder', None) != 'nightly': - build_triggers[i]['builder'] = bn - build_triggers[i]['id'] = str(build.getNumber()) - - b = yield self.builder_cxt(request, builder) - builders.append(b) - - builder_builds.append(map(lambda b: self.build_cxt(request, b), builds)) - - self.clearRecentBuildsCache() - template = request.site.buildbot_service.templates.get_template('grid_transposed.html') - defer.returnValue(template.render(**cxt)) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/hooks/__init__.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/hooks/__init__.py deleted file mode 100644 index 00bcb6e3..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/hooks/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# test
\ No newline at end of file diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/hooks/base.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/hooks/base.py deleted file mode 100644 index 3980e8f9..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/hooks/base.py +++ /dev/null @@ -1,80 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -# code inspired/copied from contrib/github_buildbot -# and inspired from code from the Chromium project -# otherwise, Andrew Melo <andrew.melo@gmail.com> wrote the rest -# but "the rest" is pretty minimal - -from buildbot.util import json - -def getChanges(request, options=None): - """ - Consumes a naive build notification (the default for now) - basically, set POST variables to match commit object parameters: - revision, revlink, comments, branch, who, files, links - - files, links and properties will be de-json'd, the rest are interpreted as strings - """ - - def firstOrNothing( value ): - """ - Small helper function to return the first value (if value is a list) - or return the whole thing otherwise - """ - if ( type(value) == type([])): - return value[0] - else: - return value - - args = request.args - - # first, convert files, links and properties - files = None - if args.get('files'): - files = json.loads( args.get('files')[0] ) - else: - files = [] - - properties = None - if args.get('properties'): - properties = json.loads( args.get('properties')[0] ) - else: - properties = {} - - revision = firstOrNothing(args.get('revision')) - when = firstOrNothing(args.get('when')) - if when is not None: - when = float(when) - author = firstOrNothing(args.get('author')) - if not author: - author = firstOrNothing(args.get('who')) - comments = firstOrNothing(args.get('comments')) - isdir = firstOrNothing(args.get('isdir',0)) - branch = firstOrNothing(args.get('branch')) - category = firstOrNothing(args.get('category')) - revlink = firstOrNothing(args.get('revlink')) - repository = firstOrNothing(args.get('repository')) - project = firstOrNothing(args.get('project')) - - chdict = dict(author=author, files=files, comments=comments, - isdir=isdir, revision=revision, when=when, - branch=branch, category=category, revlink=revlink, - properties=properties, repository=repository, - project=project) - return ([ chdict ], None) - - - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/hooks/github.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/hooks/github.py deleted file mode 100644 index 2ff05f12..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/hooks/github.py +++ /dev/null @@ -1,147 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -#!/usr/bin/env python -""" -github_buildbot.py is based on git_buildbot.py - -github_buildbot.py will determine the repository information from the JSON -HTTP POST it receives from github.com and build the appropriate repository. -If your github repository is private, you must add a ssh key to the github -repository for the user who initiated the build on the buildslave. - -""" - -import re -import datetime -from twisted.python import log -import calendar - -try: - import json - assert json -except ImportError: - import simplejson as json - -# python is silly about how it handles timezones -class fixedOffset(datetime.tzinfo): - """ - fixed offset timezone - """ - def __init__(self, minutes, hours, offsetSign = 1): - self.minutes = int(minutes) * offsetSign - self.hours = int(hours) * offsetSign - self.offset = datetime.timedelta(minutes = self.minutes, - hours = self.hours) - - def utcoffset(self, dt): - return self.offset - - def dst(self, dt): - return datetime.timedelta(0) - -def convertTime(myTestTimestamp): - #"1970-01-01T00:00:00+00:00" - matcher = re.compile(r'(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)([-+])(\d\d):(\d\d)') - result = matcher.match(myTestTimestamp) - (year, month, day, hour, minute, second, offsetsign, houroffset, minoffset) = \ - result.groups() - if offsetsign == '+': - offsetsign = 1 - else: - offsetsign = -1 - - offsetTimezone = fixedOffset( minoffset, houroffset, offsetsign ) - myDatetime = datetime.datetime( int(year), - int(month), - int(day), - int(hour), - int(minute), - int(second), - 0, - offsetTimezone) - return calendar.timegm( myDatetime.utctimetuple() ) - -def getChanges(request, options = None): - """ - Reponds only to POST events and starts the build process - - :arguments: - request - the http request object - """ - payload = json.loads(request.args['payload'][0]) - user = payload['repository']['owner']['name'] - repo = payload['repository']['name'] - repo_url = payload['repository']['url'] - project = request.args.get('project', None) - if project: - project = project[0] - elif project is None: - project = '' - # This field is unused: - #private = payload['repository']['private'] - changes = process_change(payload, user, repo, repo_url, project) - log.msg("Received %s changes from github" % len(changes)) - return (changes, 'git') - -def process_change(payload, user, repo, repo_url, project): - """ - Consumes the JSON as a python object and actually starts the build. - - :arguments: - payload - Python Object that represents the JSON sent by GitHub Service - Hook. - """ - changes = [] - newrev = payload['after'] - refname = payload['ref'] - - # We only care about regular heads, i.e. branches - match = re.match(r"^refs\/heads\/(.+)$", refname) - if not match: - log.msg("Ignoring refname `%s': Not a branch" % refname) - return [] - - branch = match.group(1) - if re.match(r"^0*$", newrev): - log.msg("Branch `%s' deleted, ignoring" % branch) - return [] - else: - for commit in payload['commits']: - files = [] - if 'added' in commit: - files.extend(commit['added']) - if 'modified' in commit: - files.extend(commit['modified']) - if 'removed' in commit: - files.extend(commit['removed']) - when = convertTime( commit['timestamp']) - log.msg("New revision: %s" % commit['id'][:8]) - chdict = dict( - who = commit['author']['name'] - + " <" + commit['author']['email'] + ">", - files = files, - comments = commit['message'], - revision = commit['id'], - when = when, - branch = branch, - revlink = commit['url'], - repository = repo_url, - project = project) - changes.append(chdict) - return changes - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/hooks/googlecode.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/hooks/googlecode.py deleted file mode 100644 index f40cf174..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/hooks/googlecode.py +++ /dev/null @@ -1,87 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright 2011, Louis Opter <kalessin@kalessin.fr> - -# Quite inspired from the github hook. - -import hmac -from twisted.python import log -from buildbot.util import json - -class GoogleCodeAuthFailed(Exception): - pass - -class Payload(object): - def __init__(self, headers, body, branch): - self._auth_code = headers['Google-Code-Project-Hosting-Hook-Hmac'] - self._body = body # we need to save it if we want to authenticate it - self._branch = branch - - payload = json.loads(body) - self.project = payload['project_name'] - self.repository = payload['repository_path'] - self.revisions = payload['revisions'] - self.revision_count = payload['revision_count'] - - def authenticate(self, secret_key): - m = hmac.new(secret_key) - m.update(self._body) - digest = m.hexdigest() - return digest == self._auth_code - - def changes(self): - changes = [] - - for r in self.revisions: - files = set() - files.update(r['added']) - files.update(r['modified']) - files.update(r['removed']) - changes.append(dict( - author=r['author'], - files=list(files), - comments=r['message'], - revision=r['revision'], - when=r['timestamp'], - # Let's hope Google add the branch one day: - branch=r.get('branch', self._branch), - revlink=r['url'], - repository=self.repository, - project=self.project - )) - - return changes - -def getChanges(request, options=None): - headers = request.received_headers - body = request.content.getvalue() - - # Instantiate a Payload object: this will parse the body, get the - # authentication code from the headers and remember the branch picked - # up by the user (Google Code doesn't send on which branch the changes - # were made) - payload = Payload(headers, body, options.get('branch', 'default')) - - if 'secret_key' in options: - if not payload.authenticate(options['secret_key']): - raise GoogleCodeAuthFailed() - else: - log.msg("Missing secret_key in the Google Code WebHook options: " - "cannot authenticate the request!") - - log.msg('Received %d changes from Google Code' % - (payload.revision_count,)) - changes = payload.changes() - - return changes, 'Google Code' diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/hooks/poller.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/hooks/poller.py deleted file mode 100644 index 1ac29a4f..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/hooks/poller.py +++ /dev/null @@ -1,54 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -# This change hook allows GitHub or a hand crafted curl inovcation to "knock on -# the door" and trigger a change source to poll. - -from buildbot.changes.base import PollingChangeSource - - -def getChanges(req, options=None): - change_svc = req.site.buildbot_service.master.change_svc - poll_all = not "poller" in req.args - - allow_all = True - allowed = [] - if isinstance(options, dict) and "allowed" in options: - allow_all = False - allowed = options["allowed"] - - pollers = [] - - for source in change_svc: - if not isinstance(source, PollingChangeSource): - continue - if not hasattr(source, "name"): - continue - if not poll_all and not source.name in req.args['poller']: - continue - if not allow_all and not source.name in allowed: - continue - pollers.append(source) - - if not poll_all: - missing = set(req.args['poller']) - set(s.name for s in pollers) - if missing: - raise ValueError("Could not find pollers: %s" % ",".join(missing)) - - for p in pollers: - p.doPoll() - - return [], None - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/logs.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/logs.py deleted file mode 100644 index d7da8111..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/logs.py +++ /dev/null @@ -1,177 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -from zope.interface import implements -from twisted.python import components -from twisted.spread import pb -from twisted.web import server -from twisted.web.resource import Resource, NoResource - -from buildbot import interfaces -from buildbot.status import logfile -from buildbot.status.web.base import IHTMLLog, HtmlResource, path_to_root - -class ChunkConsumer: - implements(interfaces.IStatusLogConsumer) - - def __init__(self, original, textlog): - self.original = original - self.textlog = textlog - def registerProducer(self, producer, streaming): - self.producer = producer - self.original.registerProducer(producer, streaming) - def unregisterProducer(self): - self.original.unregisterProducer() - def writeChunk(self, chunk): - formatted = self.textlog.content([chunk]) - try: - if isinstance(formatted, unicode): - formatted = formatted.encode('utf-8') - self.original.write(formatted) - except pb.DeadReferenceError: - self.producing.stopProducing() - 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. - implements(IHTMLLog) - - asText = False - subscribed = False - - def __init__(self, original): - Resource.__init__(self) - self.original = original - - def getChild(self, path, req): - if path == "text": - self.asText = True - return self - return Resource.getChild(self, path, req) - - def content(self, entries): - html_entries = [] - text_data = '' - for type, entry in entries: - if type >= len(logfile.ChunkTypes) or type < 0: - # non-std channel, don't display - continue - - is_header = type == logfile.HEADER - - if not self.asText: - # jinja only works with unicode, or pure ascii, so assume utf-8 in logs - if not isinstance(entry, unicode): - entry = unicode(entry, 'utf-8', 'replace') - html_entries.append(dict(type = logfile.ChunkTypes[type], - text = entry, - is_header = is_header)) - elif not is_header: - text_data += entry - - if self.asText: - return text_data - else: - return self.template.module.chunks(html_entries) - - def render_HEAD(self, req): - self._setContentType(req) - - # vague approximation, ignores markup - req.setHeader("content-length", self.original.length) - return '' - - def render_GET(self, req): - self._setContentType(req) - self.req = req - - if self.original.isFinished(): - req.setHeader("Cache-Control", "max-age=604800") - else: - req.setHeader("Cache-Control", "no-cache") - - if not self.asText: - self.template = req.site.buildbot_service.templates.get_template("logs.html") - - data = self.template.module.page_header( - pageTitle = "Log File contents", - texturl = req.childLink("text"), - path_to_root = path_to_root(req)) - data = data.encode('utf-8') - req.write(data) - - self.original.subscribeConsumer(ChunkConsumer(req, self)) - return server.NOT_DONE_YET - - def _setContentType(self, req): - if self.asText: - req.setHeader("content-type", "text/plain; charset=utf-8") - else: - req.setHeader("content-type", "text/html; charset=utf-8") - - def finished(self): - if not self.req: - return - try: - if not self.asText: - data = self.template.module.page_footer() - data = data.encode('utf-8') - self.req.write(data) - self.req.finish() - except pb.DeadReferenceError: - pass - # break the cycle, the Request's .notifications list includes the - # Deferred (from req.notifyFinish) that's pointing at us. - self.req = None - - # release template - self.template = None - -components.registerAdapter(TextLog, interfaces.IStatusLog, IHTMLLog) - - -class HTMLLog(Resource): - implements(IHTMLLog) - - def __init__(self, original): - Resource.__init__(self) - self.original = original - - def render(self, request): - request.setHeader("content-type", "text/html") - return self.original.html - -components.registerAdapter(HTMLLog, logfile.HTMLLogFile, IHTMLLog) - - -class LogsResource(HtmlResource): - addSlash = True - - def __init__(self, step_status): - HtmlResource.__init__(self) - self.step_status = step_status - - def getChild(self, path, req): - for log in self.step_status.getLogs(): - if path == log.getName(): - if log.hasContents(): - return IHTMLLog(interfaces.IStatusLog(log)) - return NoResource("Empty Log '%s'" % path) - return HtmlResource.getChild(self, path, req) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/olpb.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/olpb.py deleted file mode 100644 index 1bfd647c..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/olpb.py +++ /dev/null @@ -1,118 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -from buildbot.status.web.base import HtmlResource, BuildLineMixin, map_branches - -# /one_line_per_build -# accepts builder=, branch=, numbuilds=, reload= -class OneLinePerBuild(HtmlResource, BuildLineMixin): - """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. - reload=: reload the page after this many seconds - """ - - pageTitle = "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, numbuilds=self.numbuilds) - - 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 content(self, req, cxt): - status = self.getStatus(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, max_search=numbuilds) - - cxt['refresh'] = self.get_reload_time(req) - cxt['num_builds'] = numbuilds - cxt['branches'] = branches - cxt['builders'] = builders - - builds = cxt['builds'] = [] - for build in g: - builds.append(self.get_line_values(req, build)) - - cxt['authz'] = self.getAuthz(req) - - # get information on the builders - mostly just a count - building = 0 - online = 0 - for bn in builders: - builder = status.getBuilder(bn) - builder_status = builder.getState()[0] - if builder_status == "building": - building += 1 - online += 1 - elif builder_status != "offline": - online += 1 - - cxt['num_online'] = online - cxt['num_building'] = building - - template = req.site.buildbot_service.templates.get_template('onelineperbuild.html') - return template.render(**cxt) - - - -# /one_line_per_build/$BUILDERNAME -# accepts branch=, numbuilds= - -class OneLinePerBuildOneBuilder(HtmlResource, BuildLineMixin): - def __init__(self, builder, numbuilds=20): - HtmlResource.__init__(self) - self.builder = builder - self.builder_name = builder.getName() - self.numbuilds = numbuilds - self.pageTitle = "Recent Builds of %s" % self.builder_name - - def content(self, req, cxt): - 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) - - cxt['builds'] = map(lambda b: self.get_line_values(req, b), g) - cxt.update(dict(num_builds=numbuilds, - builder_name=self.builder_name, - branches=branches)) - - template = req.site.buildbot_service.templates.get_template('onelineperbuildonebuilder.html') - return template.render(**cxt) - - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/root.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/root.py deleted file mode 100644 index 01a926ed..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/root.py +++ /dev/null @@ -1,57 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -from twisted.web.util import redirectTo -from twisted.internet import defer - -from buildbot.status.web.base import HtmlResource, path_to_authzfail -from buildbot.util.eventual import eventually - -class RootPage(HtmlResource): - pageTitle = "Buildbot" - - @defer.inlineCallbacks - def content(self, request, cxt): - status = self.getStatus(request) - - res = yield self.getAuthz(request).actionAllowed("cleanShutdown", - request) - - if request.path == '/shutdown': - if res: - eventually(status.cleanShutdown) - defer.returnValue(redirectTo("/", request)) - return - else: - defer.returnValue( - redirectTo(path_to_authzfail(request), request)) - return - elif request.path == '/cancel_shutdown': - if res: - eventually(status.cancelCleanShutdown) - defer.returnValue(redirectTo("/", request)) - return - else: - defer.returnValue( - redirectTo(path_to_authzfail(request), request)) - return - - cxt.update( - shutting_down = status.shuttingDown, - shutdown_url = request.childLink("shutdown"), - cancel_shutdown_url = request.childLink("cancel_shutdown"), - ) - template = request.site.buildbot_service.templates.get_template("root.html") - defer.returnValue(template.render(**cxt)) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/session.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/session.py deleted file mode 100644 index 35c0ad24..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/session.py +++ /dev/null @@ -1,121 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members -# -# Insipration, and some code, from: -# :copyright: (c) 2011 by the Werkzeug Team, see Werkzeug's AUTHORS for more -# details. - -try: - from hashlib import sha1 - sha1 = sha1 # make pyflakes happy -except ImportError: - from sha import new as sha1 -from time import time -from random import random -from datetime import datetime, timedelta -import os -def _urandom(): - if hasattr(os, 'urandom'): - return os.urandom(30) - return random() - -def generate_cookie(): - return sha1('%s%s' % (time(), _urandom())).hexdigest() - - -class Session(object): - """I'm a user's session. Contains information about a user's session - a user can have several session - a session is associated with a cookie - """ - user = "" - infos = {} - def __init__(self, user, infos): - self.user = user - self.infos = infos - self.renew() - - def renew(self): - # one day expiration. hardcoded for now... - self.expiration = datetime.now()+ timedelta(1) - return self.expiration - - def expired(self): - return datetime.now() > self.expiration - - def userInfosHTML(self): - return ('%(fullName)s [<a href="mailto:%(email)s">%(email)s</a>]' % - (self.infos)) - - def getExpiration(self): - delim = '-' - d = self.expiration.utctimetuple() - return '%s, %02d%s%s%s%s %02d:%02d:%02d GMT' % ( - ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')[d.tm_wday], - d.tm_mday, delim, - ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', - 'Oct', 'Nov', 'Dec')[d.tm_mon - 1], - delim, str(d.tm_year), d.tm_hour, d.tm_min, d.tm_sec - ) - -class SessionManager(object): - """I'm the session manager. Holding the current sessions - managing cookies, and their expiration - - KISS version for the moment: - - The sessions are stored in RAM so that you have to relogin after buildbot - reboot - - Old sessions are searched at every connection, which is not very good for - scaling - - """ - - # borg pattern (similar to singleton) not too loose sessions with reconfig - __shared_state = dict(sessions={},users={}) - - def __init__(self): - self.__dict__ = self.__shared_state - - def new(self, user, infos): - cookie = generate_cookie() - user = infos["userName"] - self.users[user] = self.sessions[cookie] = s = Session(user, infos) - return cookie, s - - def gc(self): - """remove old cookies""" - expired = [] - for cookie in self.sessions: - s = self.sessions[cookie] - if s.expired(): - expired.append(cookie) - for cookie in expired: - del self.sessions[cookie] - - def get(self, cookie): - self.gc() - if cookie in self.sessions: - return self.sessions[cookie] - return None - - def remove(self, cookie): - if cookie in self.sessions: - del self.sessions[cookie] - - def getUser(self, user): - return self.users.get(user) - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/slaves.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/slaves.py deleted file mode 100644 index 323b5463..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/slaves.py +++ /dev/null @@ -1,203 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -import time, urllib -from twisted.web import html -from twisted.web.util import Redirect -from twisted.web.resource import NoResource -from twisted.internet import defer - -from buildbot.status.web.base import HtmlResource, abbreviate_age, \ - BuildLineMixin, ActionResource, path_to_slave, path_to_authzfail -from buildbot import util - -class ShutdownActionResource(ActionResource): - - def __init__(self, slave): - self.slave = slave - self.action = "gracefulShutdown" - - @defer.inlineCallbacks - def performAction(self, request): - res = yield self.getAuthz(request).actionAllowed(self.action, - request, - self.slave) - - url = None - if res: - self.slave.setGraceful(True) - url = path_to_slave(request, self.slave) - else: - url = path_to_authzfail(request) - defer.returnValue(url) - -class PauseActionResource(ActionResource): - - def __init__(self, slave, state): - self.slave = slave - self.action = "pauseSlave" - self.state = state - - @defer.inlineCallbacks - def performAction(self, request): - res = yield self.getAuthz(request).actionAllowed(self.action, - request, - self.slave) - - url = None - if res: - self.slave.setPaused(self.state) - url = path_to_slave(request, self.slave) - else: - url = path_to_authzfail(request) - defer.returnValue(url) - -# /buildslaves/$slavename -class OneBuildSlaveResource(HtmlResource, BuildLineMixin): - addSlash = False - def __init__(self, slavename): - HtmlResource.__init__(self) - self.slavename = slavename - - def getPageTitle(self, req): - return "Buildbot: %s" % self.slavename - - def getChild(self, path, req): - s = self.getStatus(req) - slave = s.getSlave(self.slavename) - if path == "shutdown": - return ShutdownActionResource(slave) - if path == "pause" or path == "unpause": - return PauseActionResource(slave, path == "pause") - return Redirect(path_to_slave(req, slave)) - - def content(self, request, ctx): - s = self.getStatus(request) - slave = s.getSlave(self.slavename) - - my_builders = [] - for bname in s.getBuilderNames(): - b = s.getBuilder(bname) - for bs in b.getSlaves(): - if bs.getName() == self.slavename: - my_builders.append(b) - - # Current builds - current_builds = [] - for b in my_builders: - for cb in b.getCurrentBuilds(): - if cb.getSlavename() == self.slavename: - current_builds.append(self.get_line_values(request, cb)) - - try: - max_builds = int(request.args.get('numbuilds')[0]) - except: - max_builds = 10 - - recent_builds = [] - n = 0 - for rb in s.generateFinishedBuilds(builders=[b.getName() for b in my_builders]): - if rb.getSlavename() == self.slavename: - n += 1 - recent_builds.append(self.get_line_values(request, rb)) - if n > max_builds: - break - - # connects over the last hour - slave = s.getSlave(self.slavename) - connect_count = slave.getConnectCount() - - if slave.isPaused(): - pause_url = request.childLink("unpause") - else: - pause_url = request.childLink("pause") - - ctx.update(dict(slave=slave, - slavename = self.slavename, - current = current_builds, - recent = recent_builds, - shutdown_url = request.childLink("shutdown"), - pause_url = pause_url, - authz = self.getAuthz(request), - this_url = "../../../" + path_to_slave(request, slave), - access_uri = slave.getAccessURI()), - admin = unicode(slave.getAdmin() or '', 'utf-8'), - host = unicode(slave.getHost() or '', 'utf-8'), - slave_version = slave.getVersion(), - show_builder_column = True, - connect_count = connect_count) - template = request.site.buildbot_service.templates.get_template("buildslave.html") - data = template.render(**ctx) - return data - -# /buildslaves -class BuildSlavesResource(HtmlResource): - pageTitle = "BuildSlaves" - addSlash = True - - def content(self, request, ctx): - s = self.getStatus(request) - - #?no_builders=1 disables build column - show_builder_column = not (request.args.get('no_builders', '0')[0])=='1' - ctx['show_builder_column'] = show_builder_column - - used_by_builder = {} - for bname in s.getBuilderNames(): - b = s.getBuilder(bname) - for bs in b.getSlaves(): - slavename = bs.getName() - if slavename not in used_by_builder: - used_by_builder[slavename] = [] - used_by_builder[slavename].append(bname) - - slaves = ctx['slaves'] = [] - for name in util.naturalSort(s.getSlaveNames()): - info = {} - slaves.append(info) - slave = s.getSlave(name) - slave_status = s.botmaster.slaves[name].slave_status - info['running_builds'] = len(slave_status.getRunningBuilds()) - info['link'] = request.childLink(urllib.quote(name,'')) - info['name'] = name - - if show_builder_column: - info['builders'] = [] - for b in used_by_builder.get(name, []): - info['builders'].append(dict(link=request.childLink("../builders/%s" % b), name=b)) - - info['version'] = slave.getVersion() - info['connected'] = slave.isConnected() - info['connectCount'] = slave.getConnectCount() - info['paused'] = slave.isPaused() - - info['admin'] = unicode(slave.getAdmin() or '', 'utf-8') - last = slave.lastMessageReceived() - if last: - info['last_heard_from_age'] = abbreviate_age(time.time() - last) - info['last_heard_from_time'] = time.strftime("%Y-%b-%d %H:%M:%S", - time.localtime(last)) - - template = request.site.buildbot_service.templates.get_template("buildslaves.html") - data = template.render(**ctx) - return data - - def getChild(self, path, req): - try: - self.getStatus(req).getSlave(path) - return OneBuildSlaveResource(path) - except KeyError: - return NoResource("No such slave '%s'" % html.escape(path)) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/status_json.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/status_json.py deleted file mode 100644 index a725a35d..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/status_json.py +++ /dev/null @@ -1,741 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Portions Copyright Buildbot Team Members -# Original Copyright (c) 2010 The Chromium Authors. - -"""Simple JSON exporter.""" - -import datetime -import os -import re - -from twisted.internet import defer -from twisted.web import html, resource, server - -from buildbot.status.web.base import HtmlResource -from buildbot.util import json - - -_IS_INT = re.compile('^[-+]?\d+$') - - -FLAGS = """\ - - as_text - - By default, application/json is used. Setting as_text=1 change the type - to text/plain and implicitly sets compact=0 and filter=1. Mainly useful to - look at the result in a web browser. - - compact - - By default, the json data is compact and defaults to 1. For easier to read - indented output, set compact=0. - - select - - By default, most children data is listed. You can do a random selection - of data by using select=<sub-url> multiple times to coagulate data. - "select=" includes the actual url otherwise it is skipped. - - numbuilds - - By default, only in memory cached builds are listed. You can as for more data - by using numbuilds=<number>. - - filter - - Filters out null, false, and empty string, list and dict. This reduce the - amount of useless data sent. - - callback - - Enable uses of JSONP as described in - http://en.wikipedia.org/wiki/JSONP. Note that - Access-Control-Allow-Origin:* is set in the HTTP response header so you - can use this in compatible browsers. -""" - -EXAMPLES = """\ - - /json - - Root node, that *doesn't* mean all the data. Many things (like logs) must - be explicitly queried for performance reasons. - - /json/builders/ - - All builders. - - /json/builders/<A_BUILDER> - - A specific builder as compact text. - - /json/builders/<A_BUILDER>/builds - - All *cached* builds. - - /json/builders/<A_BUILDER>/builds/_all - - All builds. Warning, reads all previous build data. - - /json/builders/<A_BUILDER>/builds/<A_BUILD> - - Where <A_BUILD> is either positive, a build number, or negative, a past - build. - - /json/builders/<A_BUILDER>/builds/-1/source_stamp/changes - - Build changes - - /json/builders/<A_BUILDER>/builds?select=-1&select=-2 - - Two last builds on '<A_BUILDER>' builder. - - /json/builders/<A_BUILDER>/builds?select=-1/source_stamp/changes&select=-2/source_stamp/changes - - Changes of the two last builds on '<A_BUILDER>' builder. - - /json/builders/<A_BUILDER>/slaves - - Slaves associated to this builder. - - /json/builders/<A_BUILDER>?select=&select=slaves - - Builder information plus details information about its slaves. Neat eh? - - /json/slaves/<A_SLAVE> - - A specific slave. - - /json?select=slaves/<A_SLAVE>/&select=project&select=builders/<A_BUILDER>/builds/<A_BUILD> - - A selection of random unrelated stuff as an random example. :) -""" - - -def RequestArg(request, arg, default): - return request.args.get(arg, [default])[0] - - -def RequestArgToBool(request, arg, default): - value = RequestArg(request, arg, default) - if value in (False, True): - return value - value = value.lower() - if value in ('1', 'true'): - return True - if value in ('0', 'false'): - return False - # Ignore value. - return default - - -def FilterOut(data): - """Returns a copy with None, False, "", [], () and {} removed. - Warning: converts tuple to list.""" - if isinstance(data, (list, tuple)): - # Recurse in every items and filter them out. - items = map(FilterOut, data) - if not filter(lambda x: not x in ('', False, None, [], {}, ()), items): - return None - return items - elif isinstance(data, dict): - return dict(filter(lambda x: not x[1] in ('', False, None, [], {}, ()), - [(k, FilterOut(v)) for (k, v) in data.iteritems()])) - else: - return data - - -class JsonResource(resource.Resource): - """Base class for json data.""" - - contentType = "application/json" - cache_seconds = 60 - help = None - pageTitle = None - level = 0 - - def __init__(self, status): - """Adds transparent lazy-child initialization.""" - resource.Resource.__init__(self) - # buildbot.status.builder.Status - self.status = status - - def getChildWithDefault(self, path, request): - """Adds transparent support for url ending with /""" - if path == "" and len(request.postpath) == 0: - return self - if path == 'help' and self.help: - pageTitle = '' - if self.pageTitle: - pageTitle = self.pageTitle + ' help' - return HelpResource(self.help, - pageTitle=pageTitle, - parent_node=self) - # Equivalent to resource.Resource.getChildWithDefault() - if self.children.has_key(path): - return self.children[path] - return self.getChild(path, request) - - def putChild(self, name, res): - """Adds the resource's level for help links generation.""" - - def RecurseFix(res, level): - res.level = level + 1 - for c in res.children.itervalues(): - RecurseFix(c, res.level) - - RecurseFix(res, self.level) - resource.Resource.putChild(self, name, res) - - def render_GET(self, request): - """Renders a HTTP GET at the http request level.""" - d = defer.maybeDeferred(lambda : self.content(request)) - def handle(data): - if isinstance(data, unicode): - data = data.encode("utf-8") - request.setHeader("Access-Control-Allow-Origin", "*") - if RequestArgToBool(request, 'as_text', False): - request.setHeader("content-type", 'text/plain') - else: - request.setHeader("content-type", self.contentType) - request.setHeader("content-disposition", - "attachment; filename=\"%s.json\"" % request.path) - # Make sure we get fresh pages. - if self.cache_seconds: - now = datetime.datetime.utcnow() - expires = now + datetime.timedelta(seconds=self.cache_seconds) - request.setHeader("Expires", - expires.strftime("%a, %d %b %Y %H:%M:%S GMT")) - request.setHeader("Pragma", "no-cache") - return data - d.addCallback(handle) - def ok(data): - request.write(data) - request.finish() - def fail(f): - request.processingFailed(f) - return None # processingFailed will log this for us - d.addCallbacks(ok, fail) - return server.NOT_DONE_YET - - @defer.inlineCallbacks - def content(self, request): - """Renders the json dictionaries.""" - # Supported flags. - select = request.args.get('select') - as_text = RequestArgToBool(request, 'as_text', False) - filter_out = RequestArgToBool(request, 'filter', as_text) - compact = RequestArgToBool(request, 'compact', not as_text) - callback = request.args.get('callback') - - # Implement filtering at global level and every child. - if select is not None: - del request.args['select'] - # Do not render self.asDict()! - data = {} - # Remove superfluous / - select = [s.strip('/') for s in select] - select.sort(cmp=lambda x,y: cmp(x.count('/'), y.count('/')), - reverse=True) - for item in select: - # Start back at root. - node = data - # Implementation similar to twisted.web.resource.getChildForRequest - # but with a hacked up request. - child = self - prepath = request.prepath[:] - postpath = request.postpath[:] - request.postpath = filter(None, item.split('/')) - while request.postpath and not child.isLeaf: - pathElement = request.postpath.pop(0) - node[pathElement] = {} - node = node[pathElement] - request.prepath.append(pathElement) - child = child.getChildWithDefault(pathElement, request) - - # some asDict methods return a Deferred, so handle that - # properly - if hasattr(child, 'asDict'): - child_dict = yield defer.maybeDeferred(lambda : - child.asDict(request)) - else: - child_dict = { - 'error' : 'Not available', - } - node.update(child_dict) - - request.prepath = prepath - request.postpath = postpath - else: - data = yield defer.maybeDeferred(lambda : self.asDict(request)) - - if filter_out: - data = FilterOut(data) - if compact: - data = json.dumps(data, sort_keys=True, separators=(',',':')) - else: - data = json.dumps(data, sort_keys=True, indent=2) - if callback: - # Only accept things that look like identifiers for now - callback = callback[0] - if re.match(r'^[a-zA-Z$][a-zA-Z$0-9.]*$', callback): - data = '%s(%s);' % (callback, data) - defer.returnValue(data) - - @defer.inlineCallbacks - def asDict(self, request): - """Generates the json dictionary. - - By default, renders every childs.""" - if self.children: - data = {} - for name in self.children: - child = self.getChildWithDefault(name, request) - if isinstance(child, JsonResource): - data[name] = yield defer.maybeDeferred(lambda : - child.asDict(request)) - # else silently pass over non-json resources. - defer.returnValue(data) - else: - raise NotImplementedError() - - -def ToHtml(text): - """Convert a string in a wiki-style format into HTML.""" - indent = 0 - in_item = False - output = [] - for line in text.splitlines(False): - match = re.match(r'^( +)\- (.*)$', line) - if match: - if indent < len(match.group(1)): - output.append('<ul>') - indent = len(match.group(1)) - elif indent > len(match.group(1)): - while indent > len(match.group(1)): - output.append('</ul>') - indent -= 2 - if in_item: - # Close previous item - output.append('</li>') - output.append('<li>') - in_item = True - line = match.group(2) - elif indent: - if line.startswith((' ' * indent) + ' '): - # List continuation - line = line.strip() - else: - # List is done - if in_item: - output.append('</li>') - in_item = False - while indent > 0: - output.append('</ul>') - indent -= 2 - - if line.startswith('/'): - if not '?' in line: - line_full = line + '?as_text=1' - else: - line_full = line + '&as_text=1' - output.append('<a href="' + html.escape(line_full) + '">' + - html.escape(line) + '</a>') - else: - output.append(html.escape(line).replace(' ', ' ')) - if not in_item: - output.append('<br>') - - if in_item: - output.append('</li>') - while indent > 0: - output.append('</ul>') - indent -= 2 - return '\n'.join(output) - - -class HelpResource(HtmlResource): - def __init__(self, text, pageTitle, parent_node): - HtmlResource.__init__(self) - self.text = text - self.pageTitle = pageTitle - self.parent_level = parent_node.level - self.parent_children = parent_node.children.keys() - - def content(self, request, cxt): - cxt['level'] = self.parent_level - cxt['text'] = ToHtml(self.text) - cxt['children'] = [ n for n in self.parent_children if n != 'help' ] - cxt['flags'] = ToHtml(FLAGS) - cxt['examples'] = ToHtml(EXAMPLES).replace( - 'href="/json', - 'href="../%sjson' % (self.parent_level * '../')) - - template = request.site.buildbot_service.templates.get_template("jsonhelp.html") - return template.render(**cxt) - -class BuilderPendingBuildsJsonResource(JsonResource): - help = """Describe pending builds for a builder. -""" - pageTitle = 'Builder' - - def __init__(self, status, builder_status): - JsonResource.__init__(self, status) - self.builder_status = builder_status - - def asDict(self, request): - # buildbot.status.builder.BuilderStatus - d = self.builder_status.getPendingBuildRequestStatuses() - def to_dict(statuses): - return defer.gatherResults( - [ b.asDict_async() for b in statuses ]) - d.addCallback(to_dict) - return d - - -class BuilderJsonResource(JsonResource): - help = """Describe a single builder. -""" - pageTitle = 'Builder' - - def __init__(self, status, builder_status): - JsonResource.__init__(self, status) - self.builder_status = builder_status - self.putChild('builds', BuildsJsonResource(status, builder_status)) - self.putChild('slaves', BuilderSlavesJsonResources(status, - builder_status)) - self.putChild( - 'pendingBuilds', - BuilderPendingBuildsJsonResource(status, builder_status)) - - def asDict(self, request): - # buildbot.status.builder.BuilderStatus - return self.builder_status.asDict_async() - - -class BuildersJsonResource(JsonResource): - help = """List of all the builders defined on a master. -""" - pageTitle = 'Builders' - - def __init__(self, status): - JsonResource.__init__(self, status) - for builder_name in self.status.getBuilderNames(): - self.putChild(builder_name, - BuilderJsonResource(status, - status.getBuilder(builder_name))) - - -class BuilderSlavesJsonResources(JsonResource): - help = """Describe the slaves attached to a single builder. -""" - pageTitle = 'BuilderSlaves' - - def __init__(self, status, builder_status): - JsonResource.__init__(self, status) - self.builder_status = builder_status - for slave_name in self.builder_status.slavenames: - self.putChild(slave_name, - SlaveJsonResource(status, - self.status.getSlave(slave_name))) - - -class BuildJsonResource(JsonResource): - help = """Describe a single build. -""" - pageTitle = 'Build' - - def __init__(self, status, build_status): - JsonResource.__init__(self, status) - self.build_status = build_status - # TODO: support multiple sourcestamps - sourcestamp = build_status.getSourceStamps()[0] - self.putChild('source_stamp', - SourceStampJsonResource(status, sourcestamp)) - self.putChild('steps', BuildStepsJsonResource(status, build_status)) - - def asDict(self, request): - return self.build_status.asDict() - - -class AllBuildsJsonResource(JsonResource): - help = """All the builds that were run on a builder. -""" - pageTitle = 'AllBuilds' - - def __init__(self, status, builder_status): - JsonResource.__init__(self, status) - self.builder_status = builder_status - - def getChild(self, path, request): - # Dynamic childs. - if isinstance(path, int) or _IS_INT.match(path): - build_status = self.builder_status.getBuild(int(path)) - if build_status: - return BuildJsonResource(self.status, build_status) - return JsonResource.getChild(self, path, request) - - def asDict(self, request): - results = {} - # If max > buildCacheSize, it'll trash the cache... - cache_size = self.builder_status.master.config.caches['Builds'] - max = int(RequestArg(request, 'max', cache_size)) - for i in range(0, max): - child = self.getChildWithDefault(-i, request) - if not isinstance(child, BuildJsonResource): - continue - results[child.build_status.getNumber()] = child.asDict(request) - return results - - -class BuildsJsonResource(AllBuildsJsonResource): - help = """Builds that were run on a builder. -""" - pageTitle = 'Builds' - - def __init__(self, status, builder_status): - AllBuildsJsonResource.__init__(self, status, builder_status) - self.putChild('_all', AllBuildsJsonResource(status, builder_status)) - - def getChild(self, path, request): - # Transparently redirects to _all if path is not ''. - return self.children['_all'].getChildWithDefault(path, request) - - def asDict(self, request): - # This would load all the pickles and is way too heavy, especially that - # it would trash the cache: - # self.children['builds'].asDict(request) - # TODO(maruel) This list should also need to be cached but how? - builds = dict([ - (int(file), None) - for file in os.listdir(self.builder_status.basedir) - if _IS_INT.match(file) - ]) - return builds - - -class BuildStepJsonResource(JsonResource): - help = """A single build step. -""" - pageTitle = 'BuildStep' - - def __init__(self, status, build_step_status): - # buildbot.status.buildstep.BuildStepStatus - JsonResource.__init__(self, status) - self.build_step_status = build_step_status - # TODO self.putChild('logs', LogsJsonResource()) - - def asDict(self, request): - return self.build_step_status.asDict() - - -class BuildStepsJsonResource(JsonResource): - help = """A list of build steps that occurred during a build. -""" - pageTitle = 'BuildSteps' - - def __init__(self, status, build_status): - JsonResource.__init__(self, status) - self.build_status = build_status - # The build steps are constantly changing until the build is done so - # keep a reference to build_status instead - - def getChild(self, path, request): - # Dynamic childs. - build_step_status = None - if isinstance(path, int) or _IS_INT.match(path): - build_step_status = self.build_status.getSteps()[int(path)] - else: - steps_dict = dict([(step.getName(), step) - for step in self.build_status.getSteps()]) - build_step_status = steps_dict.get(path) - if build_step_status: - # Create it on-demand. - child = BuildStepJsonResource(self.status, build_step_status) - # Cache it. - index = self.build_status.getSteps().index(build_step_status) - self.putChild(str(index), child) - self.putChild(build_step_status.getName(), child) - return child - return JsonResource.getChild(self, path, request) - - def asDict(self, request): - # Only use the number and not the names! - results = {} - index = 0 - for step in self.build_status.getSteps(): - results[index] = step.asDict() - index += 1 - return results - - -class ChangeJsonResource(JsonResource): - help = """Describe a single change that originates from a change source. -""" - pageTitle = 'Change' - - def __init__(self, status, change): - # buildbot.changes.changes.Change - JsonResource.__init__(self, status) - self.change = change - - def asDict(self, request): - return self.change.asDict() - - -class ChangesJsonResource(JsonResource): - help = """List of changes. -""" - pageTitle = 'Changes' - - def __init__(self, status, changes): - JsonResource.__init__(self, status) - for c in changes: - # c.number can be None or clash another change if the change was - # generated inside buildbot or if using multiple pollers. - if c.number is not None and str(c.number) not in self.children: - self.putChild(str(c.number), ChangeJsonResource(status, c)) - else: - # Temporary hack since it creates information exposure. - self.putChild(str(id(c)), ChangeJsonResource(status, c)) - - def asDict(self, request): - """Don't throw an exception when there is no child.""" - if not self.children: - return {} - return JsonResource.asDict(self, request) - - -class ChangeSourcesJsonResource(JsonResource): - help = """Describe a change source. -""" - pageTitle = 'ChangeSources' - - def asDict(self, request): - result = {} - n = 0 - for c in self.status.getChangeSources(): - # buildbot.changes.changes.ChangeMaster - change = {} - change['description'] = c.describe() - result[n] = change - n += 1 - return result - - -class ProjectJsonResource(JsonResource): - help = """Project-wide settings. -""" - pageTitle = 'Project' - - def asDict(self, request): - return self.status.asDict() - - -class SlaveJsonResource(JsonResource): - help = """Describe a slave. -""" - pageTitle = 'Slave' - - def __init__(self, status, slave_status): - JsonResource.__init__(self, status) - self.slave_status = slave_status - self.name = self.slave_status.getName() - self.builders = None - - def getBuilders(self): - if self.builders is None: - # Figure out all the builders to which it's attached - self.builders = [] - for builderName in self.status.getBuilderNames(): - if self.name in self.status.getBuilder(builderName).slavenames: - self.builders.append(builderName) - return self.builders - - def asDict(self, request): - results = self.slave_status.asDict() - # Enhance it by adding more informations. - results['builders'] = {} - for builderName in self.getBuilders(): - builds = [] - builder_status = self.status.getBuilder(builderName) - cache_size = builder_status.master.config.caches['Builds'] - numbuilds = int(request.args.get('numbuilds', [cache_size - 1])[0]) - for i in range(1, numbuilds): - build_status = builder_status.getBuild(-i) - if not build_status or not build_status.isFinished(): - # If not finished, it will appear in runningBuilds. - break - if build_status.getSlavename() == self.name: - builds.append(build_status.getNumber()) - results['builders'][builderName] = builds - return results - - -class SlavesJsonResource(JsonResource): - help = """List the registered slaves. -""" - pageTitle = 'Slaves' - - def __init__(self, status): - JsonResource.__init__(self, status) - for slave_name in status.getSlaveNames(): - self.putChild(slave_name, - SlaveJsonResource(status, - status.getSlave(slave_name))) - - -class SourceStampJsonResource(JsonResource): - help = """Describe the sources for a SourceStamp. -""" - pageTitle = 'SourceStamp' - - def __init__(self, status, source_stamp): - # buildbot.sourcestamp.SourceStamp - JsonResource.__init__(self, status) - self.source_stamp = source_stamp - self.putChild('changes', - ChangesJsonResource(status, source_stamp.changes)) - # TODO(maruel): Should redirect to the patch's url instead. - #if source_stamp.patch: - # self.putChild('patch', StaticHTML(source_stamp.path)) - - def asDict(self, request): - return self.source_stamp.asDict() - -class MetricsJsonResource(JsonResource): - help = """Master metrics. -""" - title = "Metrics" - - def asDict(self, request): - metrics = self.status.getMetrics() - if metrics: - return metrics.asDict() - else: - # Metrics are disabled - return None - - - -class JsonStatusResource(JsonResource): - """Retrieves all json data.""" - help = """JSON status - -Root page to give a fair amount of information in the current buildbot master -status. You may want to use a child instead to reduce the load on the server. - -For help on any sub directory, use url /child/help -""" - pageTitle = 'Buildbot JSON' - - def __init__(self, status): - JsonResource.__init__(self, status) - self.level = 1 - self.putChild('builders', BuildersJsonResource(status)) - self.putChild('change_sources', ChangeSourcesJsonResource(status)) - self.putChild('project', ProjectJsonResource(status)) - self.putChild('slaves', SlavesJsonResource(status)) - self.putChild('metrics', MetricsJsonResource(status)) - # This needs to be called before the first HelpResource().body call. - self.hackExamples() - - def content(self, request): - result = JsonResource.content(self, request) - # This is done to hook the downloaded filename. - request.path = 'buildbot' - return result - - def hackExamples(self): - global EXAMPLES - # Find the first builder with a previous build or select the last one. - builder = None - for b in self.status.getBuilderNames(): - builder = self.status.getBuilder(b) - if builder.getBuild(-1): - break - if not builder: - return - EXAMPLES = EXAMPLES.replace('<A_BUILDER>', builder.getName()) - build = builder.getBuild(-1) - if build: - EXAMPLES = EXAMPLES.replace('<A_BUILD>', str(build.getNumber())) - if builder.slavenames: - EXAMPLES = EXAMPLES.replace('<A_SLAVE>', builder.slavenames[0]) - -# vim: set ts=4 sts=4 sw=4 et: diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/step.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/step.py deleted file mode 100644 index 138fa3cc..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/step.py +++ /dev/null @@ -1,96 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -import urllib -from buildbot.status.web.base import HtmlResource, path_to_builder, \ - path_to_build, css_classes -from buildbot.status.web.logs import LogsResource -from buildbot import util -from time import ctime - -# /builders/$builder/builds/$buildnum/steps/$stepname -class StatusResourceBuildStep(HtmlResource): - pageTitle = "Build Step" - addSlash = True - - def __init__(self, build_status, step_status): - HtmlResource.__init__(self) - self.status = build_status - self.step_status = step_status - - def content(self, req, cxt): - s = self.step_status - b = s.getBuild() - - logs = cxt['logs'] = [] - for l in s.getLogs(): - # FIXME: If the step name has a / in it, this is broken - # either way. If we quote it but say '/'s are safe, - # it chops up the step name. If we quote it and '/'s - # are not safe, it escapes the / that separates the - # step name from the log number. - logs.append({'has_contents': l.hasContents(), - 'name': l.getName(), - 'link': req.childLink("logs/%s" % urllib.quote(l.getName())) }) - - stepStatistics = s.getStatistics() - statistics = cxt['statistics'] = [] - for stat in stepStatistics: - statistics.append({'name': stat, 'value': stepStatistics[stat]}) - - start, end = s.getTimes() - - if start: - cxt['start'] = ctime(start) - if end: - cxt['end'] = ctime(end) - cxt['elapsed'] = util.formatInterval(end - start) - else: - cxt['end'] = "Not Finished" - cxt['elapsed'] = util.formatInterval(util.now() - start) - - cxt.update(dict(builder_link = path_to_builder(req, b.getBuilder()), - build_link = path_to_build(req, b), - b = b, - s = s, - result_css = css_classes[s.getResults()[0]])) - - template = req.site.buildbot_service.templates.get_template("buildstep.html"); - return template.render(**cxt) - - def getChild(self, path, req): - if path == "logs": - return LogsResource(self.step_status) - return HtmlResource.getChild(self, path, req) - - - -# /builders/$builder/builds/$buildnum/steps -class StepsResource(HtmlResource): - addSlash = True - - def __init__(self, build_status): - HtmlResource.__init__(self) - self.build_status = build_status - - def content(self, req, ctx): - return "subpages show data for each step" - - def getChild(self, path, req): - for s in self.build_status.getSteps(): - if s.getName() == path: - return StatusResourceBuildStep(self.build_status, s) - return HtmlResource.getChild(self, path, req) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/about.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/about.html deleted file mode 100644 index f7b799a2..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/about.html +++ /dev/null @@ -1,32 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -<h1>About this Buildbot</h1> - -<div class="column"> - -<h2>Version Information</h2> -<ul> -{% set item_class=cycler('alt', '') %} - - <li class="{{ item_class.next() }}">Buildbot: {{ buildbot }}</li> - <li class="{{ item_class.next() }}">Twisted: {{ twisted }}</li> - <li class="{{ item_class.next() }}">Jinja: {{ jinja }}</li> - <li class="{{ item_class.next() }}">Python: {{ python }}</li> - <li class="{{ item_class.next() }}">Buildmaster platform: {{ platform }}</li> - -</ul> - -<h2>Source code</h2> - -<p>Buildbot is a free software project, released under the terms of the -<a href="http://www.gnu.org/licenses/gpl.html">GNU GPL</a>.</p> - -<p>Please visit the <a href="http://buildbot.net/">Buildbot Home Page</a> for -more information, including documentation, bug reports, and source -downloads.</p> - -</div> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/authfail.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/authfail.html deleted file mode 100644 index bae600f7..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/authfail.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -<h1>Authentication Failed</h1> - -<p>The username or password you entered were not correct. - Please go back and try again. -</p> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/authzfail.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/authzfail.html deleted file mode 100644 index e2bfcd99..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/authzfail.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -<h1>Authorization Failed</h1> - -<p>You are not allowed to perform this action. -</p> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/box_macros.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/box_macros.html deleted file mode 100644 index ec5b80b6..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/box_macros.html +++ /dev/null @@ -1,37 +0,0 @@ -{% macro box(text=[], comment=None) -%} - -{%- if comment -%}<!-- {{ comment }} -->{%- endif -%} - <td class="{{ kwargs.class or kwargs.class_ or "" }}" - {%- for prop in ("align", "colspan", "rowspan", "border", "valign", "halign") -%} - {%- if prop in kwargs %} {{ prop }}="{{ kwargs[prop] }}"{% endif -%} - {%- endfor -%}> - - {%- if text is string -%} - {{ text }} - {%- else -%} - {{- text|join("<br/>") -}} - {%- endif -%} - </td> -{% endmacro %} - -{# this is currently just the text part of the boxes #} - -{% macro build_box(reason, url, number) -%} - <a title="Reason: {{ reason|e }}" href="{{ url }}">Build {{ number }}</a> -{%- endmacro %} - -{% macro step_box(text, logs, urls, stepinfo) -%} - {%- if text is string -%} - {{ text }} - {%- else -%} - {{- text|join("<br/>") -}} - {%- endif -%} - <br/> - {%- for l in logs %} - <a {% if l.url %}href="{{ l.url }}"{% endif %}>{{ l.name|e }}</a><br/> - {%- endfor -%} - - {%- for u in urls %} - [<a href="{{ u.link }}" class="BuildStep external">{{ u.name|e }}</a>]<br/> - {%- endfor -%} -{%- endmacro %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/build.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/build.html deleted file mode 100644 index 0f508880..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/build.html +++ /dev/null @@ -1,238 +0,0 @@ -{% extends "layout.html" %} -{% import 'forms.html' as forms %} -{% from "change_macros.html" import change with context %} - -{% block content %} - -<h1> -Builder <a href="{{ path_to_builder }}">{{ b.getBuilder().getName() }}</a> -Build #{{ b.getNumber() }} -</h1> - -<div class="column"> - -{% if not b.isFinished() %} - <h2>Build In Progress:</h2> - - {% if when_time %} - <p>ETA: {{ when_time }} [{{ when }}]</p> - {% endif %} - - {{ current_step }} - - {% if authz.advertiseAction('stopBuild', request) %} - <h2>Stop Build</h2> - {{ forms.stop_build(build_url+"/stop", authz, on_all=False, short=False, label='This Build') }} - {% endif %} -{% else %} - <h2>Results:</h2> - - <p class="{{ result_css }} result"> - {{ b.getText()|join(' ')|capitalize }} - </p> - - {% if b.getTestResults() %} - <h3><a href="{{ tests_link }}"/></h3> - {% endif %} -{% endif %} - -<h2> -{% if sourcestamps|count == 1 %} -SourceStamp: -{% else %} -SourceStamps: -{% endif %} -</h2> - -{% for ss in sourcestamps %} -<h3>{{ ss.codebase }}</h3> - <table class="info" width="100%"> - {% set ss_class = cycler('alt','') %} - - {% if ss.project %} - <tr class="{{ ss_class.next() }}"><td class="left">Project</td><td>{{ ss.project|projectlink }}</td></tr> - {% endif %} - - {% if ss.repository %} - <tr class="{{ ss_class.next() }}"><td class="left">Repository</td><td>{{ ss.repository|repolink }}</td></tr> - {% endif %} - - {% if ss.branch %} - <tr class="{{ ss_class.next() }}"><td class="left">Branch</td><td>{{ ss.branch|e }}</td></tr> - {% endif %} - - {% if ss.revision %} - <tr class="{{ ss_class.next() }}"><td class="left">Revision</td><td>{{ ss.revision|revlink(ss.repository) }}</td></tr> - {% endif %} - - {% if got_revisions[ss.codebase] %} - <tr class="{{ ss_class.next() }}"><td class="left">Got Revision</td><td>{{ got_revisions[ss.codebase]|revlink(ss.repository) }}</td></tr> - {% endif %} - - {% if ss.patch %} - <tr class="{{ ss_class.next() }}"><td class="left">Patch</td><td>YES</td></tr> - {% endif %} - - {% if ss.changes %} - <tr class="{{ ss_class.next() }}"><td class="left">Changes</td><td><a href="#changes-{{ ss.codebase }}">{{ ss.changes|count }} change{{ 's' if ss.changes|count > 1 else '' }}</a></td></tr> - {% endif %} - - {% if not ss.branch and not ss.revision and not ss.patch and not ss.changes %} - <tr class="{{ ss_class.next() }}"><td class="left" colspan="2">Build of most recent revision</td></tr> - {% endif %} - </table> -{% endfor %} - -{# - # TODO: turn this into a table, or some other sort of definition-list - # that doesn't take up quite so much vertical space - #} - -<h2>BuildSlave:</h2> - -{% if slave_url %} - <a href="{{ slave_url|e }}">{{ b.getSlavename()|e }}</a> -{% else %} - {{ b.getSlavename()|e }} -{% endif %} - -<h2>Reason:</h2> -<p> -{{ b.getReason()|e }} -</p> - -<h2>Steps and Logfiles:</h2> - -{# - # 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))) - #} - -<ol> -{% for s in steps %} - <li> - <div class="{{ s.css_class }} result"> - <a href="{{ s.link }}">{{ s.name }}</a> - {{ s.text }} <span style="float:right">{{ '( ' + s.time_to_run + ' )' if s.time_to_run else '' }}</span> - </div> - - <ol> - {% set item_class = cycler('alt', '') %} - {% for l in s.logs %} - <li class="{{ item_class.next() }}"><a href="{{ l.link }}">{{ l.name }}</a></li> - {% else %} - <li class="{{ item_class.next() }}">- no logs -</li> - {% endfor %} - - {% for u in s.urls %} - <li class="{{ item_class.next() }}"><a href="{{ u.url }}">{{ u.logname }}</a></li> - {% endfor %} - </ol> - </li> -{% endfor %} -</ol> - -</div> -<div class="column"> - -<h2>Build Properties:</h2> - -<table class="info" width="100%"> -<tr><th>Name</th><th>Value</th><th>Source</th></tr> - -{% for p in properties %} -{% if p.source != "Force Build Form" %} - <tr class="{{ loop.cycle('alt', '') }}"> - <td class="left">{{ p.name|e }}</td> - {% if p.short_value %} - <td>{{ p.short_value|e }} .. [property value too long]</td> - {% else %} - {% if p.value is not mapping %} - <td>{{ p.value|e }}</td> - {% else %} - <td> - <table class="info" width="100%"> - {%- for key, value in p.value.items() recursive %} - <tr><td>{{ key|e }}</td><td>{{ value|e }}</td></tr> - {% endfor %} - </table> - </td> - {% endif %} - {% endif %} - <td>{{ p.source|e }}</td> - </tr> -{% endif %} -{% endfor %} -</table> -<h2>Forced Build Properties:</h2> -<table class="info" width="100%"> -<tr><th>Name</th><th>Label</th><th>Value</th></tr> - -{% for p in properties %} - {% if p.source == "Force Build Form" %} - <tr class="{{ loop.cycle('alt', '') }}"> - <td class="left">{{ p.name|e }}</td> - <td class="left"> - {% if p.label %} - {{ p.label }} - {% endif %} - </td> - {% if p.text %} - <td><textarea readonly cols="{{p.cols}}" rows="{{p.rows}}">{{ p.text|e }}</textarea></td> - {% else %} - <td>{{ p.value|e }}</td> - {% endif %} - </tr> - {% endif %} -{% endfor %} -</table> - -<h2>Responsible Users:</h2> - -{% if responsible_users %} - <ol> - {% for u in responsible_users %} - <li class="{{ loop.cycle('alt', '') }}">{{ u|user }}</li> - {% endfor %} - </ol> -{% else %} - <p>no responsible users</p> -{% endif %} - - -<h2>Timing:</h2> -<table class="info" width="100%"> - <tr class="alt"><td class="left">Start</td><td>{{ start }}</td></tr> -{% if end %} - <tr><td class="left">End</td><td>{{ end }}</td></tr> -{% endif %} - <tr {{ 'class="alt"' if end else '' }}><td class="left">Elapsed</td><td>{{ elapsed }}</td></tr> -</table> - -</div> - -<br style="clear:both"/> - -{% if has_changes %} - <div class="column"> - <h2>All Changes:</h2> - {% for ss in sourcestamps %} - {% if ss.changes %} - <h3 id="changes-{{ ss.codebase }}"> {{ ss.codebase }}:</h3> - <ol> - {% for c in ss.changes %} - <li><h3>Change #{{ c.number }}</h3> - {{ change(c.asDict()) }} - </li> - {% endfor %} - </ol> - {% endif %} - {% endfor %} - </div> -{% endif %} - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/build_line.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/build_line.html deleted file mode 100644 index fc08b9dc..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/build_line.html +++ /dev/null @@ -1,45 +0,0 @@ -{% macro build_line(b, include_builder=False) %} - <small>({{ b.time }})</small> - Rev: {{ b.rev|shortrev(b.rev_repo) }} - <span class="{{ b.class }}">{{ b.results }}</span> - {% if include_builder %} - <a href="{{ b.builderurl }}">{{ b.builder_name }}</a> - {% endif %} - <a href="{{ b.buildurl }}">#{{ b.buildnum }}</a> - - {{ b.text|capitalize }} -{% endmacro %} - -{% macro build_tr(b, include_builder=False, loop=None) %} - <tr class="{{ loop.cycle('alt', '') if loop }}"> - <td>{{ b.time }}</td> - <td>{{ b.rev|shortrev(b.rev_repo) }}</td> - <td class="{{ b.class }}">{{ b.results }}</td> - {%- if include_builder %} - <td><a href="{{ b.builderurl }}">{{ b.builder_name }}</a></td> - {% endif %} - <td><a href="{{ b.buildurl }}">#{{ b.buildnum }}</a></td> - <td class="left">{{ b.text|capitalize }}</td> - </tr> -{% endmacro %} - -{% macro build_table(builds, include_builder=False) %} -{% if builds %} -<table class="info"> - <tr> - <th>Time</th> - <th>Revision</th> - <th>Result</th> - {%- if include_builder %} - <th>Builder</th> - {% endif %} - <th>Build #</th> - <th>Info</th> - </tr> - {% for b in builds %} - {{ build_tr(b, include_builder, loop) }} - {% endfor %} -</table> -{% else %} - No matching builds found -{% endif %} -{% endmacro %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/builder.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/builder.html deleted file mode 100644 index f557d7c5..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/builder.html +++ /dev/null @@ -1,184 +0,0 @@ -{% from 'build_line.html' import build_table %} -{% import 'forms.html' as forms %} - -{% extends "layout.html" %} - -{% block head %} -{{ super() }} -<script type='text/javascript'> -// <![CDATA[ -// - -// Mapping OE Core branch names to bitbake branch names -var bbvermap = { - 'jethro': '1.28', - 'krogoth': '1.30', - 'morty': '1.32', - 'pyro': '1.34', - 'rocko': '1.36', - 'sumo': '1.38', - 'master': 'master' -}; - -var eclipseBranchFormat = "branch_eclipse-poky-"; - -function isEclipseBranch(elementName) { - var pattern = eclipseBranchFormat + "*"; - return new RegExp(pattern).test(elementName); -} - -function updateFormBranchInputs(pokybranch) { - var elements = document.querySelectorAll('input[name^="branch_"]'); - - for (var i = 0, len = elements.length; i < len; i++) { - var newval = pokybranch; - if (elements[i].name == "branch_bitbake") { - newval = bbvermap[pokybranch]; - } else if (isEclipseBranch(elements[i].name)) { - eclipseRelease = elements[i].name.split(eclipseBranchFormat, 2)[1]; - // Match the Eclipse branch format - if (pokybranch != "master") { - newval = eclipseRelease + "/" + pokybranch; - } else { - newval = eclipseRelease + "-master"; - } - } else if (elements[i].name == "branch_refkit") { - // Refkit only tracks OE-Core master - newval = "master"; - } else if (elements[i].name == "branch_meta-qt3") { - // won't get a rocko, or newer, branch - if (['jethro', 'krogoth', 'morty', 'pyro'].indexOf(pokybranch) < 0) { - newval = "master" - } - } else if (elements[i].name == "branch_meta-gplv2") { - // only has pyro and master branches - if (pokybranch != "pyro") { - newval = "master" - } - } - elements[i].value = newval; - } -} - -// ]]> -</script> -{% endblock %} - -{% block content %} - -<h1>Builder {{ name }}</h1> - -<p>(<a href="{{ path_to_root }}waterfall?show={{ name }}">view in waterfall</a>)</p> - -{% if description %} - <div class="BuilderDescription">{{ description }}</div> -{% endif %} - -<div class="column"> - -{% if current %} - <h2>Current Builds:</h2> - <ul> - {% for b in current %} - <li><a href="{{ b.link }}">{{ b.num }}</a> - {% if b.when %} - ETA: {{ b.when_time }} [{{ b.when }}] - {% endif %} - - {{ b.current_step }} - - {% if authz.advertiseAction('stopBuild', request) %} - {{ forms.stop_build(b.stop_url, authz, on_all=False, short=True, label='Build') }} - {% endif %} - </li> - {% endfor %} - </ul> -{% else %} - <h2>No current builds</h2> -{% endif %} - -{% if pending %} - <h2>Pending Build Requests:</h2> - <ul> - {% for b in pending %} - <li><small>({{ b.when }}, waiting {{ b.delay }})</small> - - {% if authz.advertiseAction('cancelPendingBuild', request) %} - {{ forms.cancel_pending_build(builder_url+"/cancelbuild", authz, short=True, id=b.id) }} - {% endif %} - - {% if b.num_changes < 4 %} - {% for c in b.changes %}{{ c.revision|shortrev(c.repo) }} - (<a href="{{ c.url }}">{{ c.who|email }}</a>){% if not loop.last %},{% endif %} - {% endfor %} - {% else %} - ({{ b.num_changes }} changes) - {% endif %} - - {% if 'owner' in b.properties %} - <b>Forced build</b> - by {{b.properties['owner'][0]}} - <small>{{b.properties['reason'][0]}}</small> - {% endif %} - </li> - {% endfor %} - </ul> - - {% if authz.advertiseAction('cancelPendingBuild', request) %} - {{ forms.cancel_pending_build(builder_url+"/cancelbuild", authz, short=False, id='all') }} - {% endif %} - -{% else %} - <h2>No Pending Build Requests</h2> -{% endif %} - -<h2>Recent Builds:</h2> - -{{ build_table(recent) }} - -<a href="?numbuilds={{numbuilds + 5}}">Show more</a> - -</div> -<div class="column"> - -<h2>Buildslaves:</h2> -<table class="info"> -{% if slaves %} -<tr> - <th>Name</th> - <th>Status</th> - <th>Admin</th> -</tr> -{% endif %} -{% for s in slaves %} - <tr class="{{ loop.cycle('alt', '') }}"> - <td><b><a href="{{ s.link|e }}">{{ s.name|e }}</a></b></td> - {% if s.connected %} - {% if s.paused %} - <td class="paused">paused</td> - {% else %} - <td class="idle">connected</td> - {% endif %} - {% else %} - <td class="offline">offline</td> - {% endif %} - <td>{{ s.admin|email if s.admin else ""}}</td> - </tr> -{% else %} - <td>no slaves attached</td> -{% endfor %} -</table> - -{% if authz.advertiseAction('pingBuilder', request) %} - <h2>Ping slaves</h2> - {{ forms.ping_builder(builder_url+"/ping", authz) }} -{% endif %} - -{% if authz.advertiseAction('forceBuild', request) and force_schedulers != {} %} - <h2>Force build</h2> - {{ forms.force_build(builder_url+"/force", authz, request, False, force_schedulers=force_schedulers,default_props=default_props) }} -{% endif %} - -</div> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/builders.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/builders.html deleted file mode 100644 index 67cb0225..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/builders.html +++ /dev/null @@ -1,50 +0,0 @@ -{% extends 'layout.html' %} -{% import 'forms.html' as forms %} -{% from "box_macros.html" import box %} - -{% block content %} -<h1>Builders: {{ branches|join(', ')|e }}</h1> - -<table> -{% for b in builders %} - <tr> - <td class="box"><a href="{{ b.link }}">{{ b.name|e }}</a></td> - {% if b.build_url %} - <td class="LastBuild box {{ b.build_css_class }}"> - <a href="{{ b.build_url }}">{{ b.build_label }}</a> - <br/>{{ b.build_text }} - </td> - {% else %} - <td class="LastBuild box">no build</td> - {% endif %} - {{ box(**b.current_box) }} - </tr> -{% endfor %} -</table> - -{% if num_building > 0 %} - {% if authz.advertiseAction('stopAllBuilds', request) or authz.advertiseAction('stopBuild', request) %} - <h2>Stop Selected Builds</h2> - {{ forms.stop_build(path_to_root+"builders/_selected/stopselected", authz, on_selected=True, builders=builders, label='Selected Builds') }} - <h2>Stop All Builds</h2> - {{ forms.stop_build(path_to_root+"builders/_all/stopall", authz, on_all=True, label='All Builds') }} - {% endif %} -{% endif %} - -{% if authz.advertiseAction('cancelAllPendingBuilds', request) %} - <h2>Cancel Selected Pending Builds</h2> - {{ forms.cancel_build(path_to_root+"builders/_selected/cancelpendingselected", authz, on_selected=True, builders=builders, label='Selected Pending Builds') }} - <h2>Cancel All Pending Builds</h2> - {{ forms.cancel_build(path_to_root+"builders/_all/cancelpendingall", authz, on_all=True, label='All Pending Builds') }} -{% endif %} - -{% if num_online > 0 %} - {% if authz.advertiseAction('forceAllBuilds', request) or authz.advertiseAction('forceBuild', request) %} - <h2>Force Selected Builds</h2> - {{ forms.force_build(path_to_root+"builders/_selected/forceselected", authz, request, on_selected=True, builders=builders, force_schedulers=force_schedulers, default_props=default_props) }} - <h2>Force All Builds</h2> - {{ forms.force_build(path_to_root+"builders/_all/forceall", authz,request, on_all=True, force_schedulers=force_schedulers, default_props=default_props) }} - {% endif %} -{% endif %} - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/buildslave.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/buildslave.html deleted file mode 100644 index d9f55038..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/buildslave.html +++ /dev/null @@ -1,68 +0,0 @@ -{% from 'build_line.html' import build_table, build_line %} -{% import 'forms.html' as forms %} - -{% extends "layout.html" %} -{% block content %} -<h1>Buildslave: {{ slavename|e }}</h1> - -<div class="column"> - -{% if current %} - <h2>Currently building:</h2> - <ul> - {% for b in current %} - <li>{{ build_line(b, True) }} - <form method="post" action="{{ b.buildurl }}/stop" class="command stopbuild" style="display:inline"> - <input type="submit" value="Stop Build" /> - <input type="hidden" name="url" value="{{ this_url }}" /> - </form> - </li> - {% endfor %} - </ul> -{% else %} - <h2>No current builds</h2> -{% endif %} - -<h2>Recent builds</h2> -{{ build_table(recent, True) }} - -</div> -<div class="column"> -{% if access_uri %} - <a href="{{ access_uri|e }}">Click to Access Slave</a> -{% endif %} - -{% if admin %} - <h2>Administrator</h2> - <p>{{ admin|email }}</p> -{% endif %} - -{% if host %} - <h2>Slave information</h2> - Buildbot-Slave {{ slave_version }} - <pre>{{ host|e }}</pre> -{% endif %} - -<h2>Connection Status</h2> -<p> -{{ connect_count }} connection(s) in the last hour -{% if not slave.isConnected() %} -(not currently connected) -{% else %} -</p> - {% if authz.advertiseAction('gracefulShutdown', request) %} - <h2>Graceful Shutdown</h2> - {% if slave.getGraceful() %} - <p>Slave will shut down gracefully when it is idle.</p> - {% else %} - {{ forms.graceful_shutdown(shutdown_url, authz) }} - {% endif %} - {% endif %} - {% if authz.advertiseAction('pauseSlave', request) %} - <h2>Pause Slave</h2> - {{ forms.pause_slave(pause_url, authz, slave.isPaused()) }} - {% endif %} -{% endif %} -</div> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/buildslaves.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/buildslaves.html deleted file mode 100644 index 567e3747..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/buildslaves.html +++ /dev/null @@ -1,76 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -<h1>Buildslaves</h1> - -<div class="column"> - -<table class="info"> - -<tr> - <th>Name</th> - {%- if show_builder_column %} - <th>Builders</th> - {%- endif %} - <th>BuildBot</th> - <th>Admin</th> - <th>Last heard from</th> - <th>Connects/Hour</th> - <th>Status</th> -</tr> - -{% for s in slaves %} - <tr class="{{ loop.cycle('alt','') }}"> - <td><b><a href="{{ s.link }}">{{ s.name }}</a></b></td> - - {%- if show_builder_column %} - <td> - {%- if s.builders %} - {%- for b in s.builders %} - <a href="{{ b.link }}">{{ b.name }}</a> - {%- endfor %} - {%- else %} - <span class="Warning">no builders</span> - {%- endif -%} - </td> - {%- endif %} - - - <td>{{ (s.version or '-')|e }}</td> - - {%- if s.admin -%} - <td>{{ s.admin|email }}</td> - {%- else -%} - <td>-</td> - {%- endif -%} - - <td> - {%- if s.last_heard_from_age -%} - {{ s.last_heard_from_age }} <small>({{ s.last_heard_from_time }})</small> - {%- endif -%} - </td> - <td> - {{ s.connectCount }} - </td> - - {% if s.connected %} - {% if s.running_builds %} - <td class="building">Running {{ s.running_builds }} build(s)</td> - {% elif s.paused %} - <td class="paused">Paused</td> - {% else %} - <td class="idle">Idle</td> - {% endif %} - - {% else %} - <td class="offline">Not connected</td> - {% endif %} - - </tr> -{% endfor %} -</table> - -</div> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/buildstatus.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/buildstatus.html deleted file mode 100644 index df33878a..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/buildstatus.html +++ /dev/null @@ -1,19 +0,0 @@ -{% extends "layout.html" %} -{% from "box_macros.html" import box %} - -{% block header %} -{% endblock %} - -{% block barecontent %} -<table> - {% for r in rows %} - <tr>{{ box(**r) }}</tr> - {% endfor %} - - <tr>{{ box(**build) }}</tr> -</table> -{% endblock %} - -{% block footer %} -{% endblock %} - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/buildstep.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/buildstep.html deleted file mode 100644 index c2c75140..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/buildstep.html +++ /dev/null @@ -1,73 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -<h1> - Builder <a href="{{ builder_link }}">{{ b.getBuilder().getName() }}</a> - build <a href="{{ build_link }}">#{{ b.getNumber() }}</a> - step <a href="">{{ s.getName() }}</a> -</h1> - -<div class="column"> - -{% if s.isFinished() %} - <h2>Finished</h2> - <p class="{{ result_css }} result"> - {%- set text = s.getText() -%} - {%- if text is string %}{{ text|e }} - {%- else %}{{ text|join(" ")|e }}{% endif -%} - </p> -{% else %} - <h2>Not Finished</h2> - <p>ETA {{ s.getETA()|e }} seconds</p> -{% endif %} - -{% set exp = s.getExpectations() %} -{% if exp %} - <h2>Expectations</h2> - <ul> - {% for e in exp %} - <li>{{ e[0]|e }}: current={{ e[1] }}, target={{ e[2] }}</li> - {% endfor %} - </ul> -{% endif %} - -<h2>Timing</h2> -{% if start %} - <table class="info"> - <tr class="alt"><td class="left">Start</td><td>{{ start }}</td></tr> - <tr><td class="left">End</td><td>{{ end or "Not finished" }}</td></tr> - <tr class="alt"><td class="left">Elapsed</td><td>{{ elapsed }}</td></tr> - </table> -{% else %} - <b>Not started</b> -{% endif %} - -<h2>Logs</h2> -<ul> -{% for l in logs %} - <li class="{{ loop.cycle('alt', '') }}"> - {% if l.has_contents %} - <a href="{{ l.link|e }}">{{ l.name|e }}</a> - {% else %} - {{ l.name|e }} - {% endif %} - </li> -{% else %} - <li class="alt">- No logs -</li> -{% endfor %} -</ul> - -{% if statistics %} -<h2>Statistics</h2> -<table class="info"> - <tr><th>Name</th><th>Value</th></tr> - {% for stat in statistics %} - <tr class="{{ loop.cycle('alt', '') }}"><td>{{ stat.name|e }}</td><td>{{ stat.value|e }}</td></tr> - {% endfor %} -</table> -{% endif %} - -</div> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/change.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/change.html deleted file mode 100644 index f12161da..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/change.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "layout.html" %} -{% from "change_macros.html" import change with context %} -{% import 'forms.html' as forms %} - -{% block content %} - -<h1>{{ pageTitle }}</h1> - -<div class="column"> - -{{ change(c) }} - -{% if authz.advertiseAction('stopChange', request) %} - <h3>Cancel Builds For Change:</h3> - {{ forms.stop_change_builds("/builders/_all/stopchangeall", c.number, authz) }} -{% endif %} - -</div> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/change_macros.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/change_macros.html deleted file mode 100644 index 2ca01d9f..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/change_macros.html +++ /dev/null @@ -1,76 +0,0 @@ -{% macro change(c) %} - -<table class="info"> - {% set row_class=cycler('alt','') %} - <tr class="{{ row_class.next() }}"> - <td class="left">Category</td> - <td><b>{{ c.category }}</b></td> - </tr> - <tr class="{{ row_class.next() }}"> - <td class="left">Changed by</td> - <td><b>{{ c.who|email }}</b></td> - </tr> - <tr class="{{ row_class.next() }}"> - <td class="left">Changed at</td> - <td><b>{{ c.at }}</b></td> - </tr> - - {% if c.repository %} - <tr class="{{ row_class.next() }}"> - <td class="left">Repository</td> - <td><b>{{ c.repository|repolink }}</b></td> - </tr> - {% endif %} {% if c.project %} - <tr class="{{ row_class.next() }}"> - <td class="left">Project</td> - <td><b>{{ c.project|projectlink }}</b></td> - </tr> - {% endif %} {% if c.branch %} - <tr class="{{ row_class.next() }}"> - <td class="left">Branch</td> - <td><b>{{ c.branch|e }}</b></td> - </tr> - {% endif %} {% if c.rev %} - <tr class="{{ row_class.next() }}"> - <td class="left">Revision</td> - <td>{%- if c.revlink -%}<a href="{{ c.revlink }}">{{ c.rev|e }}</a> - {%- else -%}{{ c.rev|revlink(c.repository) }} {%- endif -%}</td> - </tr> - {% endif %} -</table> - -{% if c.comments %} -<h3>Comments</h3> -<pre class="comments">{{ c.comments|changecomment(c.project) }}</pre> -{% endif %} - -<h3 class="files">Changed files</h3> -<ul> - {% for f in c.files -%} - <li class="{{ loop.cycle('alt', '') }}">{%- if f.url %}<a - href="{{ f.url }}"><b>{{ f.name|e }}</b></a></li> - {%- else %} - <b>{{ f.name|e }}</b> - {%- endif -%} - </li> - {% else %} - <li>no files</li> - {% endfor %} -</ul> - -{% if c.properties %} -<h3>Properties</h3> -<table class="info"> - {% for p in c.properties %} - <tr class="{{ loop.cycle('alt') }}"> - <td class="left">{{ p[0]|capitalize|e }}</td> - <td>{{ p[1]|e }}</td> - </tr> - {% endfor %} -</table> -{% endif %} -{%- endmacro %} - -{% macro box_contents(who, url, pageTitle, revision, project) -%} -<a href="{{ url }}" title="{{ pageTitle|e }}">{{ who|user }}</a> -{%- endmacro %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/change_sources.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/change_sources.html deleted file mode 100644 index 24674dd5..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/change_sources.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -<h1>Changesources</h1> - -<div class="column"> - -{% if sources %} - <ol> - {% for s in sources -%} - <li class="{{ loop.cycle('alt', '') }}">{{ s.describe() }}</li> - {% endfor -%} - </ol> -{% else %} - none (push only) -{% endif %} - -</div> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/console.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/console.html deleted file mode 100644 index a51c946e..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/console.html +++ /dev/null @@ -1,276 +0,0 @@ -{% extends "layout.html" %} - -{% block head %} -{{ super() }} -<script type='text/javascript'> -// <![CDATA[ -// - -// -// Functions used to display the build status bubble on box click. -// - -// show the build status box. This is called when the user clicks on a block. -function showBuildBox(url, event) { - // Find the current curson position. - var cursorPosTop = (window.event ? window.event.clientY : event.pageY) - var cursorPosLeft = (window.event ? window.event.clientX : event.pageX) - - // Offset the position by 5, to make the window appears under the cursor. - cursorPosTop = cursorPosTop + document.body.scrollTop -5 ; - cursorPosLeft = cursorPosLeft + document.body.scrollLeft - 5; - - // Move the div (hidden) under the cursor. - var divBox = document.getElementById('divBox'); - divBox.style.top = parseInt(cursorPosTop) + 'px'; - divBox.style.left = parseInt(cursorPosLeft) + 'px'; - - // Reload the hidden frame with the build page we want to show. - // The onload even on this frame will update the div and make it visible. - document.getElementById("frameBox").src = url - - // We don't want to reload the page. - return false; -} - -// OnLoad handler for the iframe containing the build to show. -function updateDiv(event) { - // Get the frame innerHTML. - var iframeContent = document.getElementById("frameBox").contentWindow.document.body.innerHTML; - - // If there is any content, update the div, and make it visible. - if (iframeContent) { - var divBox = document.getElementById('divBox'); - divBox.innerHTML = iframeContent ; - divBox.style.display = "block"; - } -} - -// Util functions to know if an element is contained inside another element. -// We use this to know when we mouse out our build status div. -function containsDOM (container, containee) { - var isParent = false; - do { - if ((isParent = container == containee)) - break; - containee = containee.parentNode; - } while (containee != null); - - return isParent; -} - -// OnMouseOut handler. Returns true if the mouse moved out of the element. -// It is false if the mouse is still in the element, but in a blank part of it, -// like in an empty table cell. -function checkMouseLeave(element, event) { - if (element.contains && event.toElement) { - return !element.contains(event.toElement); - } - else if (event.relatedTarget) { - return !containsDOM(element, event.relatedTarget); - } -} - -// ]]> -</script> -{% endblock %} - -{% block content %} - -<h1>Console View</h1> - -<div align="center"> - <table width="95%" class="Grid" border="0" cellspacing="0"> - <tr> - <td width="33%" align="left" class="left_align"> -{% if categories|length > 1 %} - <br><b>Categories:</b> {% for c in categories %}{{ c.name|e }} {% endfor %} -{% endif %} -{% if codebase %} - <br><b>Codebase:</b> {{ codebase|e }} -{% endif %} -{% if repository %} - <br><b>Repository:</b> {{ repository|e }} -{% endif %} -{% if project %} - <br><b>Project:</b> {{ project|e }} -{% endif %} -{% if branch != ANYBRANCH %} - <br><b>Branch:</b> {{ branch|e }} -{% endif %} - </td> - <td width="33%" align="center" class="center_align"> - <div align="center"> - <table class="info"> - <tr> - <td>Legend: </td> - <td class='legend success' title='All tests passed'>Passed</td> - <td class='legend failure' title='There is a new failure. Take a look!'>Failed</td> - <td class='legend warnings' title='Build has warnings'>Warnings</td> - <td class='legend failure-again' title='It was failing before, and it is still failing. Make sure you did not introduce new regressions'>Failed Again</td> - <td class='legend running' title='The tests are still running'>Running</td> - <td class='legend exception' title='Something went wrong with the test, there is no result'>Exception</td> - <td class='legend offline' title='The builder is offline, as there are no slaves connected to it'>Offline</td> - <td class='legend notstarted' title='No result yet.'>No data</td> - </tr> - </table> - </div> - </td> - <td width="33%" align="right" class="right_align"> - <script type="text/javascript"> -// <![CDATA[ - function reload_page() { - name_value = document.getElementById('namebox').value - if (document.location.href.lastIndexOf('?') == -1) - document.location.href = document.location.href+ '?name=' + name_value; - else - document.location.href = document.location.href+ '&name=' + name_value; - } -// ]]> - </script> - <form onsubmit='reload_page()'> - <input id='namebox' name='name' type='text' style='color:#999;' - onblur='this.value = this.value || this.defaultValue; this.style.color = "#999";' - onfocus='this.value=""; this.style.color = "#000";' - value='Personalized for...'/> - <input type='submit' value='Go'/> - </form> - </td> - </tr> - </table> -</div> - -<br/> - -{% set alt_class = cycler('', 'Alt') %} - -<div align="center"> -<table width="96%"> - -{% if categories|length > 1 %} - <tr> - <td width="1%"> - </td> - <td width="1%"> - </td> - {% for c in categories %} - <td class='DevStatus {{ alt_class.next() }} {{ "first" if loop.first else '' }} {{ "last" if loop.last else '' }}' width='{{ c.size }}%'> - {{ c.name|e }} - </td> - {% endfor %} - </tr> - <tr class='DevStatusSpacing'> - </tr> -{% endif %} - -{% if slaves %} - <tr> - <td width="1%"> - </td> - <td width="1%"> - </td> - {% for c in categories %} - <td class='DevSlave {{ alt_class.next() }}'> - <table width="100%"> - <tr> - {% for s in slaves[c.name] %} - <td class='DevSlaveBox'> - <a href='{{ s.url }}' title='{{ s.pageTitle }}' class='DevSlaveBox {{ s.color }}' target="_blank"> - </a> - </td> - {% endfor %} - </tr> - </table> - </td> - {% endfor %} - </tr> -{% endif %} - -{% for r in revisions %} - {% set alt = alt_class.next() %} - {% set firstrev = "first" if loop.first else '' %} - - <tr> - <td class='DevRev {{ alt }}' width="1%"> - {{ r.id|shortrev(r.repository) }} - </td> - <td class='DevName {{ alt }}' width="1%"> - {{ r.who|user }} - </td> - - {% for c in categories %} - {% set last = "last" if loop.last else "" %} - <td class='DevStatus {{ alt }} {{ last if not firstrev else '' }}'> - <table width="100%"> - <tr> - {% for b in r.builds[c.name] %} - <td class='DevStatusBox'> - <a href='#' onclick='showBuildBox("{{ b.url }}", event); return false;' - title='{{ b.pageTitle|e }}' class='DevStatusBox {{ b.color }} {{ b.tag }}' - target="_blank"></a> - </td> - {% endfor %} - </tr> - </table> - </td> - {% endfor %} - </tr> - - <tr> - <td colspan="{{ r.span }}" class='DevComment {{ alt }} {% if not r.details %}DevBottom{% endif %}'> - {{ r.comments|changecomment(r.project or None)|replace('\n', '<br/>')|replace(' ',' ') }} - </td> - </tr> - - {% if r.details %} - <tr> - <td colspan="{{ r.span }}" class='DevDetails {{ alt }} DevBottom'> - <ul style='margin: 0px; padding: 0 0 0 1.5em;'> - {% for d in r.details %} - <li>{{ d.buildername }}: {{ d.status }} - - {%- for l in d.logs -%} - <a href="{{ l.url }}">{{ l.name }}</a> - {%- endfor -%} - </li> - {% endfor %} - </ul> - </td> - </tr> - {% endif %} - - <tr class='DevStatusSpacing'> - <td> - </td> - </tr> - -{% else %} - <tr><td>No revisions available</td></tr> -{% endfor %} - -</table> -</div> - - -<div id="divBox" onmouseout="if (checkMouseLeave(this, event)) this.style.display = 'None'" class="BuildWaterfall"> -</div> - - -<iframe id="frameBox" style="display: none;"></iframe> - -<script type="text/javascript"> -// replace 'onload="updateDiv(event);" with this, as iframe doesn't have onload event in xhtml -window.onload = function() { - document.getElementById('frameBox').onload = function(event) { - updateDiv(event); - }; -}; -</script> - -{% endblock %} - - -{% block footer %} - -{{ super() }} -{# <p>Debug info: {{ debuginfo }}</p> #} -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/directory.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/directory.html deleted file mode 100644 index 662355de..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/directory.html +++ /dev/null @@ -1,37 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -<h1>Directory listing for {{ path }}</h1> - -{% set row_class = cycler('alt', '') %} - -<table> - -<tr class="{{ row_class.next() }}"> -<th style="min-width:18em">Name</th> -<th style="min-width:8em">Size</th> -<th style="min-width:10em">Type</th> -<th style="min-width:10em">Encoding</th> -</tr> - -{% for d in directories %} - <tr class="directory {{ row_class.next() }}"> - <td><a href="{{ d.href }}"><b>{{ d.text }}</b></a></td> - <td><b>{{ d.size }}</b></td> - <td><b>{{ d.type }}</b></td> - <td><b>{{ d.encoding }}</b></td> - </tr> -{% endfor %} - -{% for f in files %} - <tr class="file {{ row_class.next() }}"> - <td><a href="{{ f.href }}">{{ f.text }}</a></td> - <td>{{ f.size }}</td> - <td>{{ f.type }}</td> - <td>{{ f.encoding }}</td> - </tr> -{% endfor %} -</table> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/empty.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/empty.html deleted file mode 100644 index 91f4e591..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/empty.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} -{{ content }} -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/feed_atom10.xml b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/feed_atom10.xml deleted file mode 100644 index c423e7b0..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/feed_atom10.xml +++ /dev/null @@ -1,40 +0,0 @@ -{% from 'feed_description.html' import item_desc %} - -<?xml version="1.0" encoding="utf-8"?> -<feed xmlns="http://www.w3.org/2005/Atom"> - <id>{{ title_url }}</id> - <title>{{ pageTitle|e }}</title> - {% if project_url -%} - <link rel="self" href="{{ title_url }}/atom"/> - <link rel="alternate" href="{{ title_url }}"/> - {% endif %} - {%- if description -%} - <subtitle>{{ description }}</subtitle> - {% endif %} - {%- if rfc3339_pubdate -%} - <updated>{{ rfc3339_pubdate }}</updated> - {% endif -%} - <author> - <name>BuildBot</name> - </author> - - {% for b in builds -%} - <entry> - <title>{{ b.pageTitle }}</title> - <link href="{{ b.link }}"/> - <content type="xhtml"> - <div xmlns="http://www.w3.org/1999/xhtml"> - {{ item_desc(b, title_url, title)|indent(6) }} - <pre xml:space="preserve">{{ b.log_lines|join('\n')|e }}</pre> - </div> - </content> - {% if b.rfc3339_pubdate -%} - <updated>{{ b.rfc3339_pubdate }}</updated> - <id>{{ b.guid }}</id> - {% endif -%} - <author>Buildbot</author> - </entry> - - {% endfor -%} - -</feed> diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/feed_description.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/feed_description.html deleted file mode 100644 index 7f387c67..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/feed_description.html +++ /dev/null @@ -1,18 +0,0 @@ -{% from 'feed_sources.html' import srcs_desc %} - -{% macro item_desc(b, title_url, title) -%} - <p> - Date: {{ b.date }}<br/> - Project home: <a href="{{ title_url }}">{{ title|e }}</a><br/> - Builder summary: <a href="{{ b.summary_link }}">{{ b.name }}</a><br/> - Build details: <a href="{{ b.link }}">Build {{ b.number }}</a><br/> - Author list: <b>{{ b.responsible_users|join(', ') }}</b><br/> - Failed step(s): <b>{{ b.failed_steps|join(', ') }}</b><br/> - </p> - {% for src in b.sources %} - {{ srcs_desc(src) }} - {% endfor %} - <p> - <i>Last lines of the build log:</i> - </p> -{%- endmacro %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/feed_rss20.xml b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/feed_rss20.xml deleted file mode 100644 index 9f30112a..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/feed_rss20.xml +++ /dev/null @@ -1,39 +0,0 @@ -{% from 'feed_description.html' import item_desc %} - -<?xml version="1.0" encoding="utf-8"?> -<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> - <channel> - <title>{{ pageTitle|e }}</title> - <link>{{ title_url }}</link> - <atom:link href="{{ title_url }}rss" rel="self" type="application/rss+xml"/> - {% if language -%} - <language>{{ language }}</language> - {% endif %} - {%- if description -%} - <description>{{ description }}</description> - {% endif %} - {%- if rfc822_pubdate -%} - <pubDate>{{ rfc822_pubdate }}</pubDate> - {% endif %} - - {% for b in builds -%} - <item> - <title>{{ b.pageTitle }}</title> - <link>{{ b.link }}</link> - <description> - <![CDATA[ - {{ item_desc(b, title_url, title)|indent(8) }} - <pre>{{ b.log_lines|join('\n')|e }}</pre> - ]]> - </description> - {% if b.rfc822_pubdate -%} - <pubDate>{{ b.rfc822_pubdate }}</pubDate> - <guid isPermaLink="false">{{ b.guid }}</guid> - {%- endif %} - - </item> - - {% endfor %} - - </channel> -</rss> diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/feed_sources.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/feed_sources.html deleted file mode 100644 index 4ec3bc8f..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/feed_sources.html +++ /dev/null @@ -1,16 +0,0 @@ -{% macro srcs_desc(src) -%} - <div> - {%- if src.codebase -%} - <b>Codebase: {{ src.codebase }}</b><br/> - {% endif %} - <p> - Repository: {{ src.repository }}<br/> - {%- if src.branch -%} - Branch: {{ src.branch }}<br/> - {% endif %} - {%- if src.revision -%} - Revision: {{ src.revision }}<br/> - {% endif %} - </p> - </div> -{%- endmacro %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/footer.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/footer.html deleted file mode 100644 index 30c664c7..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/footer.html +++ /dev/null @@ -1,23 +0,0 @@ -<hr /> -<div class="footer"> - -<a href="{{ welcomeurl }}">Welcome Page</a> -<br /> - -<a href="http://buildbot.net/">Buildbot</a>-{{ version }} - -{% if title %} - working for - {% if title_url %} - <a href="{{ title_url }}">{{ title }}</a> - {% else %} - {{ title }} - {% endif %} - project -{% endif %} - -<br /> - -Page built: {{ time }} ({{ tz }}) -</div> - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/forms.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/forms.html deleted file mode 100644 index 133c465a..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/forms.html +++ /dev/null @@ -1,288 +0,0 @@ - -{% macro cancel_pending_build(cancel_url, authz, short=False, id='all') %} - <form method="post" name="cancel" action="{{ cancel_url }}" class='command cancelbuild' - {{ 'style="display:inline"' if short else '' }}> - {% if not short %} - {% if id == 'all' %} - <p>To cancel all builds, push the 'Cancel' button</p> - <p>To cancel individual builds, click the 'Cancel' buttons above.</p> - {% else %} - <p>To cancel this build, push the 'Cancel' button</p> - {% endif %} - {% endif %} - <input type="hidden" name="id" value="{{ id }}" /> - <input type="submit" value="Cancel" /> - </form> -{% endmacro %} - -{% macro stop_change_builds(stopchange_url, changenum, authz) %} - {% if not changenum %} - <form method="post" action="{{ stopchange_url }}" class='command stopchange'> - {% if changenum %} - <p>To cancel all builds for this change, push the 'Cancel' button</p> - {% else %} - <p>To cancel builds for this builder for a given change, fill out the - following field and push the 'Cancel' button</p> - {% endif %} - - {% if changenum %} - <input type="hidden" name="change" value="{{ changenum }}" /> - {% else %} - <div class="row"> - <span class="label">Change #:</span> - <input type="text" name="change"/> - </div> - {% endif %} - <input type="submit" value="Cancel" /> - </form> - {% endif %} -{% endmacro %} - -{% macro stop_build(stop_url, authz, on_all=False, on_selected=False, builders=[], short=False, label="Build") %} - {% if not short %} - <form method="post" name="stop_build" action="{{ stop_url }}" class='command stopbuild' - {{ 'style="display:inline"' if short else '' }}> - {% if not short %} - {% if on_all %} - <p>To stop all builds, fill out the following field and - push the <i>Stop {{ label }}</i> button</p> - {% elif on_selected %} - <p>To stop selected builds, select the builders, fill out the - following field and push the <i>Stop {{ label }}</i> button</p> - <table> - {% for b in builders %} - <tr> - <td align="center"><input type="checkbox" name="selected" value="{{ b.name }}"></td> - <td class="box"><a href="{{ b.link }}">{{ b.name|e }}</a></td> - </tr> - {% endfor %} - </table> - - {% else %} - <p>To stop this build, fill out the following field and - push the <i>Stop {{ label }}</i> button</p> - {% endif %} - {% endif %} - - {% if not short %} - <div class="row"> - <span class="label">Reason:</span> - <input type="text" name="comments"/> - </div> - {% endif %} - - <input type="submit" value="Stop {{ label }}" /> - </form> - {% endif %} -{% endmacro %} - -{% macro cancel_build(cancel_url, authz, on_all=False, on_selected=False, builders=[], short=False, label="Build") %} - {% if not short %} - <form method="post" name="cancel_build" action="{{ cancel_url }}" class='command cancelbuild' - {{ 'style="display:inline"' if short else '' }}> - {% if not short %} - {% if on_all %} - <p>To cancel all pending builds, fill out the following field and - push the <i>Cancel {{ label }}</i> button</p> - {% elif on_selected %} - <p>To cancel selected pending builds, select the builders, fill out the - following field and push the <i>Cancel {{ label }}</i> button</p> - <table> - {% for b in builders %} - <tr> - <td align="center"><input type="checkbox" name="selected" value="{{ b.name }}"></td> - <td class="box"><a href="{{ b.link }}">{{ b.name|e }}</a></td> - </tr> - {% endfor %} - </table> - - {% else %} - <p>To cancel this pending build, fill out the following field and - push the <i>Cancel {{ label }}</i> button</p> - {% endif %} - {% endif %} - - {% if not short %} - <div class="row"> - <span class="label">Reason:</span> - <input type="text" name="comments"/> - </div> - {% endif %} - - <input type="submit" value="Cancel {{ label }}" /> - </form> - {% endif %} -{% endmacro %} - -{% macro force_build_scheduler_parameter(f, authz, request, sch, default_props) %} - {% if f and not f.hide and (f.fullName != "username" or not authz.authenticated(request)) %} - <div class="row{% for subtype in f.type %} force-{{subtype}}{%endfor%}"{% if f.name %} id="force-{{sch.name}}-{{f.fullName}}"{% endif %}> - {% if 'text' in f.type or 'int' in f.type %} - <span class="label">{{f.label}}</span> - <input type='text' size='{{f.size}}' name='{{f.fullName}}' value='{{default_props[sch.name+"."+f.fullName]}}' /> - {% elif 'bool' in f.type%} - <input type='checkbox' name='checkbox' value='{{f.fullName}}' {{default_props[sch.name+"."+f.fullName]}} /> - <span class="label">{{f.label}}</span> - {% elif 'textarea' in f.type %} - <span class="label">{{f.label}}</span> - <textarea name='{{f.fullName}}' rows={{f.rows}} cols={{f.cols}}>{{default_props[sch.name+"."+f.fullName]}}</textarea> - {% elif 'list' in f.type %} - <span class="label">{{f.label}}</span> - <span class="select"> - <select name='{{f.fullName}}' {{ f.multiple and "multiple" or ""}}> - {% for c in default_props[sch.name+"."+f.fullName+".choices"] %} - <option {{(c in default_props[sch.name+"."+f.fullName]) and "selected" or ""}}>{{c}}</option> - {% endfor %} - </select> - </span> - {% elif 'nested' in f.type %} - {% if f.label %}<span class="label">{{f.label}}</span>{% endif %} - {% for subfield in f.fields %} - {{ force_build_scheduler_parameter(subfield, authz, request, sch, default_props) }} - {% endfor %} - {% endif %} - </div> - {% endif %} -{% endmacro %} - -{% macro force_build_one_scheduler(force_url, authz, request, on_all, on_selected, builders, sch, default_props) %} - <form method="post" name="force_build" action="{{ force_url }}" class="command_forcebuild"> - - <h3>{{ sch.name|e }}</h3> - {% if on_all %} - <p>To force a build on <strong>all Builders</strong>, fill out the following fields - and push the 'Force Build' button</p> - {% elif on_selected %} - <p>To force a build on <strong>certain Builders</strong>, select the - builders, fill out the following fields and push the - 'Force Build' button</p> - - <table> - {% for b in builders %} - {% if b.name in sch.builderNames %} - <tr> - <td align="center"><input type="checkbox" name="selected" value="{{ b.name }}"></td> - <td class="box"><a href="{{ b.link }}">{{ b.name|e }}</a></td> - </tr> - {% endif %} - {% endfor %} - </table> - - {% else %} - <p>To force a build, fill out the following fields and - push the 'Force Build' button</p> - {% endif %} - <input type='hidden' name='forcescheduler' value='{{sch.name}}' /> - <p> - <hr /> - {% if "nightly" in sch.builderNames %} - Set up branch values for Poky branch: <select id ="select_release" - name="build_release" onchange="updateFormBranchInputs(this.value)"> - <option value="master" selected>master</option> - <option value="rocko">rocko</option> - <option value="pyro">pyro</option> - <option value="morty">morty</option> - <option value="krogoth">krogoth</option> - </select> - <hr/> - {% endif %} - - {% for f in sch.all_fields %} - {{ force_build_scheduler_parameter(f, authz, request, sch, default_props) }} - {% endfor %} - - <input type="submit" value="Force Build" /> - </form> -{% endmacro %} -{% macro force_build(force_url, authz, request, on_all=False, on_selected=False, builders=[], force_schedulers={},default_props={}) %} - {% for name, sch in force_schedulers.items() | sort %} - {{ force_build_one_scheduler(force_url, authz, request, on_all, on_selected, builders, sch, default_props=default_props) }} - {% endfor %} - -{% endmacro %} - -{% macro graceful_shutdown(shutdown_url, authz) %} - <form method="post" action="{{ shutdown_url }}" class='command graceful_shutdown'> - - <p>To cause this slave to shut down gracefully when it is idle, - push the 'Graceful Shutdown' button</p> - <input type="submit" value="Graceful Shutdown" /> - </form> -{% endmacro %} - -{% macro pause_slave(pause_url, authz, paused) %} - <form method="post" action="{{ pause_url }}" class='command pause_slave'> - - {% if paused %} - <p>To cause this slave to start running new builds again, - push the 'Unpause Slave' button</p> - {% else %} - <p>To cause this slave to stop running new builds, - push the 'Pause Slave' button</p> - {% endif %} - - {% if paused %} - <input type="submit" value="Unpause Slave" /> - {% else %} - <input type="submit" value="Pause Slave" /> - {% endif %} - </form> -{% endmacro %} - -{% macro clean_shutdown(shutdown_url, authz) %} - <form method="post" action="{{ shutdown_url }}" class='command clean_shutdown'> - <p>To cause this master to shut down cleanly, push the 'Clean Shutdown' button.</p> - <p>No other builds will be started on this master, and the master will - stop once all current builds are finished.</p> - - <input type="submit" value="Clean Shutdown" /> - </form> -{% endmacro %} - -{% macro cancel_clean_shutdown(cancel_shutdown_url, authz) %} - <form method="post" action="{{ cancel_shutdown_url }}" class='command cancel_clean_shutdown'> - <p>To cancel a previously initiated shutdown, push the 'Cancel Shutdown' button.</p> - - <input type="submit" value="Cancel Shutdown" /> - </form> -{% endmacro %} - -{% macro ping_builder(ping_url, authz) %} - <form method="post" action="{{ ping_url }}" class='command ping_builder'> - <p>To ping the buildslave(s), push the 'Ping' button</p> - <input type="submit" value="Ping Builder" /> - </form> -{% endmacro %} - -{% macro rebuild_build(rebuild_url, authz, ss) %} - <form method="post" action="{{ rebuild_url }}" class="command rebuild"> - - {% if on_all %} - <p>To force a build on <strong>all Builders</strong>, fill out the following fields - and push the 'Force Build' button</p> - {% else %} - <p>To force a build, fill out the following fields and - push the 'Force Build' button</p> - {% endif %} - <div class="row"> - <span class="label">Reason for re-running build:</span> - <input type='text' name='comments' /> - </div> - <div class="row"> - Rebuild using: - <select name="useSourcestamp"> - <option value='exact' selected>Exact same revisions</option> - <option value='updated'>Same sourcestamps (ignoring 'got revision')</option> - </select> - </div> - <input type="submit" value="Rebuild" /> - </form> -{% endmacro %} - -{% macro show_users(users_url, authz) %} - <form method="post" action="{{ users_url }}" class='command show_users'> - <p>To show users, press the 'Show Users' button</p> - - <input type="submit" value="Show Users" /> - </form> -{% endmacro %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/grid.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/grid.html deleted file mode 100644 index ed44ea10..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/grid.html +++ /dev/null @@ -1,31 +0,0 @@ -{% extends "layout.html" %} -{% import 'grid_macros.html' as grid with context %} - -{% block content %} - -<h1>Grid View</h1> - -<table class="Grid" border="0" cellspacing="0"> - -<tr> - <td class="title"><a href="{{ title_url }}">{{ title }}</a> - {{ grid.category_title() }} - </td> - - {% for i in range %} - {{ grid.stamp_td(stamps[i], build_triggers[i]) }} - {% endfor %} -</tr> - -{% for builder in builders %} - <tr> - {{ grid.builder_td(builder) }} - {% for build in builder.builds %} - {{ grid.build_td(build) }} - {% endfor %} - </tr> -{% endfor %} - -</table> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/grid_macros.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/grid_macros.html deleted file mode 100644 index 42a1b451..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/grid_macros.html +++ /dev/null @@ -1,65 +0,0 @@ -{% macro category_title() -%} - {% if categories %} - <br> - {% trans categories=categories %} - <b>Category:</b></br> - {% pluralize categories %} - <b>Categories:</b><br/> - {% endtrans %} - {% for c in categories %} - {{ c|e }}<br/> - {% endfor %} - {% endif %} - - {% if branch != ANYBRANCH %} - <br><b>Branch:</b> {{ branch|e or "trunk" }} - {% endif %} -{%- endmacro %} - - -{% macro stamp_td(sourcestamps, trigger) -%} - <td valign="bottom" class="sourcestamp"> - {% for ss in sourcestamps %} - {%- if ss.codebase %}{{ ss.codebase|e }}: {% endif %} - {%- if ss.revision -%} - <br/><b>Commit: </b>{{ ss.revision|shortrev(ss.repository) }} - {%- else %}<br/><b>Commit: </b>latest{% endif %} - {%- if ss.branch %} in {{ ss.branch|e }}{% endif %} - {%- if ss.hasPatch %} [patch]{% endif -%} - (<a href="http://errors.yoctoproject.org/Errors/Latest/Autobuilder/?filter={{ss.revision}}&type=commit">errors</a>, - <a href="https://wiki.yoctoproject.org/wiki/BuildLog#{{trigger.builder}}_{{trigger.id}}_-_{{ss.branch}}_{{ss.revision}}">log</a>) - <br/> - {%- endfor %} - </td> -{%- endmacro %} - -{% macro builder_td(b) -%} - <td valign="middle" style="text-align: center" class="builder {{ b.state }}"> - <a href="{{ b.url }}">{{ b.name }}</a> - {%- if b.state != 'idle' or b.n_pending > 0 -%} - <br/>({{ b.state }} - {%- if b.n_pending > 0 -%} - , plus {{ b.n_pending }} - {%- endif -%} - ) - {%- endif -%} - {%- if b.time %}<br/><b>Time: </b> {{ b.time }} {% endif %} - </td> -{%- endmacro %} - -{% macro build_td(build) -%} -{% if build %} - <td valign="top" class="build {{ build.class }}" style="text-align: left" style="white-space: nowrap" valign="bottom" > - {%- if build.release_name %}<br/><b>RELEASE NAME:</b> {{ build.release_name }}{% endif %} - {%- if build.branchshortname %}<br/><b>Branch:</b> {{ build.branchshortname }} {% endif %} - {%- if build.repository %}<br/><b>Repo: </b> - {%- if build.cgiturl %} <a href="{{ build.cgiturl }}"> {% endif %} - {{ build.repository }} - {%- if build.cgiturl %} </a> {% endif %} - {% endif %} - <br/><b>Build:</b> <a href="{{ build.url }}">{{ build.text|join('<br/>') }}</a> - </td> -{% else %} - <td class="build"> </td> -{% endif %} -{%- endmacro %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/grid_transposed.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/grid_transposed.html deleted file mode 100644 index 9b6cadad..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/grid_transposed.html +++ /dev/null @@ -1,30 +0,0 @@ -{% extends "layout.html" %} -{% import 'grid_macros.html' as grid with context %} - -{% block content %} - -<h1>Transposed Grid View</h1> - -<table class="Grid" border="0" cellspacing="0"> - -<tr> - <td class="title"><a href="{{ title_url }}">{{ title }}</a> - {{ grid.category_title() }} - </td> - {% for builder in builders %} - {{ grid.builder_td(builder) }} - {% endfor %} -</tr> - -{% for i in range %} - <tr> - {{ grid.stamp_td(stamps[i], build_triggers[i]) }} - {% for b in builder_builds %} - {{ grid.build_td(b[i]) }} - {% endfor %} - </tr> -{% endfor %} - -</table> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/jsonhelp.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/jsonhelp.html deleted file mode 100644 index 63c155b9..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/jsonhelp.html +++ /dev/null @@ -1,28 +0,0 @@ -{% extends "layout.html" %} -{% block content %} - -<p>{{ text }}</p> -<h2>More Help:</h2> - -{% if level != 1 %} -<p><a href="../help">Parent's Help</a></p> -{% endif %} - -{% if children %} -<p>Child Nodes</p> -<ul> -{% for child in children %} - <li><a href="{{ child|e }}">{{ child|e }}</a> - (<a href="{{ child|e }}/help">{{ child|e }}/help</a>) - </li> -{% endfor %} -</ul> -{% endif %} - -<h2>Flags:</h2> -{{ flags }} - -<h2>Examples:</h2> -{{ examples }} - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/layout.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/layout.html deleted file mode 100644 index 01dfb80c..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/layout.html +++ /dev/null @@ -1,90 +0,0 @@ -{%- block doctype -%} -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" -"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -{% endblock %} -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - {% block head %} - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - {% if metatags %} - {{ metatags }} - {% endif %} - {% if refresh %} - <meta http-equiv="refresh" content="{{ refresh|e }}"/> - {% endif %} - <title>{{ pageTitle|e }}</title> - <link rel="stylesheet" href="{{ stylesheet }}" type="text/css" /> - <link rel="alternate" type="application/rss+xml" title="RSS" href="{{ path_to_root }}rss"> - <link rel="shortcut icon" href="{{ path_to_root }}favicon.ico"> - {% endblock %} - </head> - <body class="interface"> - {% block header -%} - <div class="header"> - <a href="{{ path_to_root or '.' }}">Home</a> - {% if authz.advertiseAction('view', request) %} - - <a href="{{ path_to_root }}waterfall">Waterfall</a> - <a href="{{ path_to_root }}grid">Grid</a> - <a href="{{ path_to_root }}tgrid">T-Grid</a> - <a href="{{ path_to_root }}console">Console</a> - <a href="{{ path_to_root }}builders">Builders</a> - <a href="{{ path_to_root }}one_line_per_build">Recent Builds</a> - <a href="{{ path_to_root }}buildslaves">Buildslaves</a> - <a href="{{ path_to_root }}changes">Changesources</a> - {% if authz.advertiseAction('showUsersPage', request) %} - <a href="{{ path_to_root }}users">Users</a> - {% endif %} - - <a href="{{ path_to_root }}json/help">JSON API</a> - - <a href="{{ path_to_root }}about">About</a> - {% endif %} - <div class="auth"> - {% if authz.authenticated(request) %} - {{ authz.getUsernameHTML(request) }} - |<a href="{{ path_to_root }}logout">Logout</a> - {% elif authz.useHttpHeader and authz.httpLoginUrl %} - <a href="{{ authz.httpLoginUrl }}">Login</a> - {% elif authz.auth %} - <form method="post" name="login" action="{{ path_to_root }}login"> - <input type="text" name="username" size=10 /> - <input type="password" name="passwd" size=10 /> - <input type="submit" value="login" /> - </form> - {% endif %} - </div> - </div> - {% endblock %} - - {%- block barecontent -%} - <hr/> - - {% if alert_msg != "" %} - <div class="alert"> - {{ alert_msg }} - </div> - {% endif %} - - <div class="content"> - {%- block content -%} - {%- endblock -%} - </div> - {%- endblock -%} - - {%- block footer -%} - <div class="footer" style="clear:both"> - <hr/> - <a href="http://buildbot.net/">BuildBot</a> ({{version}}) - {% if title -%} - working for the - {%- if title_url -%} - <a href="{{ title_url }}">{{ title }}</a> - {%- else -%} - {{ title }} - {%- endif -%} - project. - {%- endif -%} - <br/> - Page built: <b>{{ time }}</b> ({{ tz }}) - </div> - {% endblock -%} - </body> -</html> diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/logs.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/logs.html deleted file mode 100644 index 116bbd6a..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/logs.html +++ /dev/null @@ -1,23 +0,0 @@ -{%- macro page_header(pageTitle, path_to_root, texturl) -%} -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> - <html> - <head><title>{{ pageTitle }}</title> - <link rel="stylesheet" href="{{ path_to_root }}default.css" type="text/css" /> - </head> - <body class='log'> - <a href="{{ texturl }}">(view as text)</a><br/> - <pre> -{%- endmacro -%} - -{%- macro chunks(entries) -%} -{%- for entry in entries -%} - <span class="{{ entry.type }}">{{ entry.text|e }}</span> -{%- endfor -%} -{%- endmacro -%} - -{%- macro page_footer() -%} -</pre> -</body> -</html> -{%- endmacro -%} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/onelineperbuild.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/onelineperbuild.html deleted file mode 100644 index d2484465..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/onelineperbuild.html +++ /dev/null @@ -1,36 +0,0 @@ -{% extends "layout.html" %} -{% from 'build_line.html' import build_table %} -{% import 'forms.html' as forms %} - -{% block content %} -<h1>Last {{ num_builds }} finished builds: {{ branches|join(', ')|e }}</h1> - -{% if builders %} - <p>of builders: {{ builders|join(", ")|e }}</p> -{% endif %} - -<div class="column"> - -{{ build_table(builds, True) }} - -</div> -<div class="column"> - -{% if num_building > 0 %} - {% if authz.advertiseAction('stopBuild', request) %} - <h2>Stop All Builds</h2> - {{ forms.stop_build("builders/_all/stopall", authz, on_all=True, label='All Builds') }} - {% endif %} -{% endif %} - -{% if num_online > 0 %} - {% if authz.advertiseAction('forceAllBuilds', request) %} - <h2>Force All Builds</h2> - {{ forms.force_build("builders/_all/forceall", authz, request, True, force_schedulers=force_schedulers, default_props=default_props) }} - {% endif %} -{% endif %} - - -</div> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/onelineperbuildonebuilder.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/onelineperbuildonebuilder.html deleted file mode 100644 index 051501f2..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/onelineperbuildonebuilder.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "layout.html" %} -{% from 'build_line.html' import build_line %} - -{% block content %} - -<h1>Last {{ num_builds }} builds of builder {{ builder_name|e }}: - {{ branches|join(', ')|e }} -</h1> - -<ul> -{% for b in builds %} - <li>{{ build_line(b) }}</li> -{% else %} - <li>No matching builds found</li> -{% endfor %} -</ul> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/revmacros.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/revmacros.html deleted file mode 100644 index 87a87a02..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/revmacros.html +++ /dev/null @@ -1,35 +0,0 @@ -{# both macro pairs must have the same signature #} - -{% macro id_replace(rev, url) -%} - <span class="revision" title="Revision {{ rev }}"><a href="{{ url }}"> - {%- if rev|length > 40 %}{{ rev[:40] }}... - {%- else %}{{ rev }} - {%- endif -%} - </a></span> -{%- endmacro %} - -{% macro shorten_replace(short, rev, url) %} - <div class="revision"> - <div class="short" title="Revision {{ rev }}"> - <a href="{{ url }}">{{ short }}...</a> - </div> - <div class="full"> - <a href="{{ url }}">{{ rev }}</a> - </div> - </div> -{% endmacro %} - -{% macro id(rev, url) -%} - <span class="revision" title="Revision {{ rev }}"> - {%- if rev|length > 40 %}{{ rev[:40] }}... - {%- else %}{{ rev }} - {%- endif -%} - </span> -{%- endmacro %} - -{% macro shorten(short, rev, url) %} - <div class="revision"> - <div class="short" title="Revision {{ rev }}">{{ short }}...</div> - <div class="full">{{ rev }}</div> - </div> -{% endmacro %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/root.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/root.html deleted file mode 100644 index 561973e4..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/root.html +++ /dev/null @@ -1,61 +0,0 @@ -{% extends 'layout.html' %} -{% import 'forms.html' as forms %} - -{% block content %} - -<h1>Welcome to the Buildbot -{%- if title -%} - for the - {%- if title_url -%} - <a href="{{ title_url }}">{{ title }}</a> - {%- else -%} - {{ title }} - {%- endif -%} - project -{%- endif -%} -! -</h1> - -<div class="column"> - -<ul> - {% set item_class=cycler('alt', '') %} - - <li class="{{ item_class.next() }}">The <a href="waterfall">Waterfall Display</a> will give you a - time-oriented summary of recent buildbot activity. <a href="waterfall/help">Waterfall Help.</a></li> - - <li class="{{ item_class.next() }}">The <a href="grid">Grid Display</a> will give you a - developer-oriented summary of recent buildbot activity.</li> - - <li class="{{ item_class.next() }}">The <a href="tgrid">Transposed Grid Display</a> presents - the same information as the grid, but lists the revisions down the side.</li> - - <li class="{{ item_class.next() }}">The <a href="console">Console</a> presents - a user-oriented status page.</li> - - <li class="{{ item_class.next() }}">The <a href="builders">Builders</a> and their most recent builds are - here.</li> - - <li class="{{ item_class.next() }}"><a href="one_line_per_build">Recent Builds</a> are summarized here, one - per line.</li> - - <li class="{{ item_class.next() }}"><a href="buildslaves">Buildslave</a> information</li> - <li class="{{ item_class.next() }}"><a href="changes">Changesource</a> information.</li> - - <li class="{{ item_class.next() }}"><a href="about">About</a> this Buildbot</li> -</ul> - -{%- if authz.advertiseAction('cleanShutdown', request) -%} -{%- if shutting_down -%} -Master is shutting down<br/> -{{ forms.cancel_clean_shutdown(cancel_shutdown_url, authz) }} -{%- else -%} -{{ forms.clean_shutdown(shutdown_url, authz) }} -{%- endif -%} -{%- endif -%} - -<p><i>This and other pages can be overridden and customized.</i></p> - -</div> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/testresult.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/testresult.html deleted file mode 100644 index 70dac7ae..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/testresult.html +++ /dev/null @@ -1,34 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -<h1> - Builder <a href="{{ builder_link }}">{{ b.getBuilder().getName() }}</a> - build <a href="{{ build_link }}">#{{ b.getNumber() }}</a> - test <a href="">{{ '.'.join(tr.getName()) }}</a> -</h1> - -<div class="column"> - - <h2>Result</h2> - <p class="{{ result_css }} result"> - {{ result_word }} - {%- set text = tr.getText() -%} - {%- if text is string %}{{ text|e }} - {%- else %}{{ text|join(" ")|e }}{% endif -%} - </p> - -<h2>Logs</h2> -<ul> -{% for l in logs %} - <h3>Log: {{ l.name|e }}</h3> - <samp><pre>{{ l.log|e }}</pre></samp> - </br> -{% else %} - <li class="alt">- No logs -</li> -{% endfor %} -</ul> - -</div> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/user.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/user.html deleted file mode 100644 index eb136175..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/user.html +++ /dev/null @@ -1,29 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -<h1>User: {{ user_identifier|e }}</h1> - -<div class="column"> - -<table class="info"> - -<tr> - <th>Attribute Type</th> - <th>Attribute Value</th> -</tr> - -{% for attr in user %} - <tr class="{{ loop.cycle('alt','') }}"> - - <td>{{ attr|e }}</td> - <td>{{ user[attr]|e }}</td> - - </tr> -{% endfor %} - -</table> - -</div> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/users.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/users.html deleted file mode 100644 index 5cb2dafc..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/users.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} - -<h1>Users</h1> - -<table class="info"> - -<tr> - <th>Uid</th> - <th>Identifier</th> -</tr> - -{% for user in users %} - <tr class="{{ loop.cycle('alt','') }}"> - - <td><b><a href="{{ user.user_link }}">{{ user.uid }}</a></b></td> - <td>{{ user.identifier|e }}</td> - - </tr> -{% endfor %} - -</table> - -</div> - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/waterfall.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/waterfall.html deleted file mode 100644 index e380406e..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/waterfall.html +++ /dev/null @@ -1,66 +0,0 @@ -{% extends "layout.html" %} -{% from "box_macros.html" import box %} - -{% block content %} - -<div> - <h1 style="display: inline;">Waterfall</h1> - <a style="float: right;" href="{{ help_url }}">waterfall help</a> -</div> - -<table border="0" cellspacing="0"> - -<tr class="LastBuild"> - <td align="right" colspan="2" class="Project"> - last build - </td> - -{% for b in builders %} - <td align="center" class="{{ b.top_class }}"> - <a href="{{ b.url }}">{{ b.name }}</a><br/> - {{ " ".join(b.top) }} - </td> -{% endfor %} -</tr> - -<tr class="Activity"> -<td align="right" colspan="2">current activity</td> - -{% for b in builders %} - <td align="center" class="{{ b.status_class }}"> - {{ "<br/>".join(b.status) }} - </td> -{% endfor %} -</tr> - -<tr> -<td align="center" class="Time">{{ tz }}</td> -<td align="center" class="Change"><a href="{{ changes_url }}">changes</a></td> - -{% for b in builders %} - <td align="center" class="Builder"><a href="{{ b.url }}">{{ b.name }}</a></td> -{% endfor %} -</tr> - -{# waterfall contents goes here #} -{% for i in range(gridlen) -%} - <tr> - {% for strip in grid -%} - {%- if strip[i] -%}{{ box(**strip[i]) }} - {%- elif no_bubble -%}{{ box() }} - {%- endif -%} - {%- endfor -%} - </tr> -{% endfor %} - -</table> - -{% if nextpage %} - <a href="{{ nextpage }}">next page</a> -{% endif %} - -{% if no_reload_page %} - <a href="{{ no_reload_page }}">Stop Reloading</a> -{% endif %} - -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/waterfallhelp.html b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/waterfallhelp.html deleted file mode 100644 index c0e5ec6e..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/templates/waterfallhelp.html +++ /dev/null @@ -1,140 +0,0 @@ -{% extends "layout.html" %} -{% block content %} - -<form action="../waterfall" method="get"> - -<h1>The Waterfall Display</h1> - -<p>The Waterfall display can be controlled by adding query arguments to the -URL. 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 that -query arguments are separated from each other with ampersands, but they are -separated 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&builder=unix&builder=macos</tt>.</p> - -<h2>Limiting the Displayed Interval</h2> - -<p>The <tt>last_time=</tt> argument is a unix timestamp (seconds since the -start of 1970) that will be used as an upper bound on the interval of events -displayed: 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 recent -steps are included.</p> - -<p>The <tt>first_time=</tt> argument provides the lower bound. No events will -be 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 the -given interval, where each timestamp on the left hand edge counts as a single -event. You can add a <tt>num_events=</tt> argument to override this this.</p> - -<h2>Showing non-Build events</h2> - -<p>By passing <tt>show_events=true</tt>, you can add the "buildslave -attached", "buildslave detached", and "builder reconfigured" events that -appear in-between the actual builds.</p> - -<p> -<input type="checkbox" name="show_events" value="true" -{% if show_events_checked %} checked="checked" {% endif %}/> -Show non-Build events -</p> - -<h2>Showing only Certain Branches</h2> - -<p>If you provide one or more <tt>branch=</tt> arguments, the display will be -limited 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. - -{% if branches %} -<table> - {% for b in branches %} - <tr> - <td>Show Branch: - <input type="text" name="branch" value="{{ b|e }}"/> - </td> - </tr> - {% endfor %} -</table> -{% endif %} - -<h2>Limiting the Builders that are Displayed</h2> - -<p>By adding one or more <tt>builder=</tt> arguments, the display will be -limited to showing builds that ran on the given builders. This serves to -limit 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, select -the Builders you are interested in here.</p> - -<table> -{% for bn in all_builders %} - <tr><td><input type="checkbox" name="builder" value="{{ bn }}" - {% if bn in show_builders %}checked="checked"{% endif %} /> - </td><td>{{bn}}</td></tr> -{% endfor %} -</table> - -<h2>Limiting the Builds that are Displayed</h2> - -<p>By adding one or more <tt>committer=</tt> arguments, the display will be -limited to showing builds that were started by the given committer. If no -<tt>committer=</tt> arguments are provided, all builds will be displayed.</p> - -<p>To view a Waterfall page with only a subset of Builds displayed, select -the committers your are interested in here.</p> - -Erase the text from these "Show Committer:" boxes to remove that filter. - -{% if committers %} - <table> - {% for cn in committers %} - <tr> - <td> - Show Committer: <input type="text" name="committer" value="{{ cn }}"> - </td> - </tr> - {% endfor %} - </table> -{% endif %} - -<h2>Showing only the Builders with failures</h2> - -<p>By adding the <tt>failures_only=true</tt> argument, the display will be limited -to showing builders that are currently failing. A builder is considered -failing if the last finished build was not successful, a step in the current -build(s) failed, or if the builder is offline.</p> - -<p> - <input type="checkbox" name="failures_only" value="true" - {% if failures_only %}checked="checked"{% endif %}/> - Show failures only -</p> - -<h2>Auto-reloading the Page</h2> - -<p>Adding a <tt>reload=</tt> argument will cause the page to automatically -reload itself after that many seconds.</p> - -<table> -{% for value, name in times %} - <tr><td><input type="radio" name="reload" value="{{ value|e }}" - {% if value == current_reload_time %}checked="checked"{% endif %}/> - </td><td>{{ name|e }}</td></tr> -{% endfor %} -</table> - - -<h2>Reload Waterfall Page</h2> - -<input type="submit" value="View Waterfall" /> -</form> -{% endblock %} diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/tests.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/tests.py deleted file mode 100644 index 52622982..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/tests.py +++ /dev/null @@ -1,84 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -import urllib -from buildbot.status.web.base import HtmlResource, path_to_builder, \ - path_to_build, css_classes -from buildbot.status.builder import Results - -# /builders/$builder/builds/$buildnum/steps/$stepname -class StatusResourceBuildTest(HtmlResource): - pageTitle = "Test Result" - addSlash = True - - def __init__(self, build_status, test_result): - HtmlResource.__init__(self) - self.status = build_status - self.test_result = test_result - - def content(self, req, cxt): - tr = self.test_result - b = self.status - - cxt['b'] = self.status - logs = cxt['logs'] = [] - for lname, log in tr.getLogs().items(): - if isinstance(log, str): - log = log.decode('utf-8') - logs.append({'name': lname, - 'log': log, - 'link': req.childLink("logs/%s" % urllib.quote(lname)) }) - - cxt['text'] = tr.text - cxt['result_word'] = Results[tr.getResults()] - cxt.update(dict(builder_link = path_to_builder(req, b.getBuilder()), - build_link = path_to_build(req, b), - result_css = css_classes[tr.getResults()], - b = b, - tr = tr)) - - template = req.site.buildbot_service.templates.get_template("testresult.html") - return template.render(**cxt) - - def getChild(self, path, req): - # if path == "logs": - # return LogsResource(self.step_status) #TODO we need another class - return HtmlResource.getChild(self, path, req) - - - -# /builders/$builder/builds/$buildnum/steps -class TestsResource(HtmlResource): - addSlash = True - nameDelim = '.' # Test result have names like a.b.c - - def __init__(self, build_status): - HtmlResource.__init__(self) - self.build_status = build_status - - def content(self, req, ctx): - # TODO list the tests - return "subpages show data for each test" - - def getChild(self, path, req): - tpath = None - if path: - tpath = tuple(path.split(self.nameDelim)) - if tpath: - tr = self.build_status.getTestResults().get(tpath) - if tr: - return StatusResourceBuildTest(self.build_status, tr) - return HtmlResource.getChild(self, path, req) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/users.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/users.py deleted file mode 100644 index 953ae68f..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/users.py +++ /dev/null @@ -1,90 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -import urllib -from twisted.internet import defer -from twisted.web.util import redirectTo -from buildbot.status.web.base import HtmlResource, path_to_authzfail, \ - path_to_root, ActionResource - -class UsersActionResource(ActionResource): - - def __init__(self): - self.action = "showUsersPage" - - @defer.inlineCallbacks - def performAction(self, req): - res = yield self.getAuthz(req).actionAllowed('showUsersPage', req) - if not res: - defer.returnValue(path_to_authzfail(req)) - return - # show the table - defer.returnValue(path_to_root(req) + "users") - -# /users/$uid -class OneUserResource(HtmlResource): - addSlash = False - def __init__(self, uid): - HtmlResource.__init__(self) - self.uid = int(uid) - - def getPageTitle (self, req): - return "Buildbot User: %s" % self.uid - - def content(self, request, ctx): - status = self.getStatus(request) - - d = status.master.db.users.getUser(self.uid) - def cb(usdict): - ctx['user_identifier'] = usdict['identifier'] - user = ctx['user'] = {} - for attr in usdict: - if attr not in ['uid', 'identifier', 'bb_password']: - user[attr] = usdict[attr] - - template = request.site.buildbot_service.templates.get_template("user.html") - data = template.render(**ctx) - return data - d.addCallback(cb) - return d - -# /users -class UsersResource(HtmlResource): - pageTitle = "Users" - addSlash = True - - def __init__(self): - HtmlResource.__init__(self) - - def getChild(self, path, req): - return OneUserResource(path) - - @defer.inlineCallbacks - def content(self, req, ctx): - res = yield self.getAuthz(req).actionAllowed('showUsersPage', req) - if not res: - defer.returnValue(redirectTo(path_to_authzfail(req), req)) - return - - s = self.getStatus(req) - - usdicts = yield s.master.db.users.getUsers() - users = ctx['users'] = usdicts - - for user in users: - user['user_link'] = req.childLink(urllib.quote(str(user['uid']), '')) - template = req.site.buildbot_service.templates.get_template( - "users.html") - defer.returnValue(template.render(**ctx)) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/waterfall.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/waterfall.py deleted file mode 100644 index 681c5768..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/waterfall.py +++ /dev/null @@ -1,813 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - - -from zope.interface import implements -from twisted.python import log, components -from twisted.internet import defer -import urllib - -import time, locale -import operator - -from buildbot import interfaces, util -from buildbot.status import builder, buildstep, build -from buildbot.changes import changes - -from buildbot.status.web.base import Box, HtmlResource, IBox, ICurrentBox, \ - ITopBox, build_get_class, path_to_build, path_to_step, path_to_root, \ - map_branches - - -def earlier(old, new): - # minimum of two things, but "None" counts as +infinity - if old: - if new < old: - return new - return old - return new - -def later(old, new): - # maximum of two things, but "None" counts as -infinity - if old: - if new > old: - return new - return old - return new - - -class 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, brcounts): - # 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) - brcount = brcounts[builderName] - if brcount: - text.append("%d pending" % brcount) - for t in sorted(upcoming): - if t is not None: - 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] - 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? - class_ = build_get_class(b) - return Box(text, urlbase=url, 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() - template = req.site.buildbot_service.templates.get_template("box_macros.html") - text = template.module.build_box(reason=reason,url=url,number=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, build.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() - - cxt = dict(text=text, logs=[], urls=[], stepinfo=self) - - for num in range(len(logs)): - name = logs[num].getName() - if logs[num].hasContents(): - url = urlbase + "/logs/%s" % urllib.quote(name) - else: - url = None - cxt['logs'].append(dict(name=name, url=url)) - - for name, target in self.original.getURLs().items(): - cxt['urls'].append(dict(link=target,name=name)) - - template = req.site.buildbot_service.templates.get_template("box_macros.html") - text = template.module.step_box(**cxt) - - class_ = "BuildStep " + build_get_class(self.original) - return Box(text, class_=class_) -components.registerAdapter(StepBox, buildstep.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 b -components.registerAdapter(SpacerBox, Spacer, IBox) - -def insertGaps(g, showEvents, 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() - if not showEvents and isinstance(e, builder.Event): - continue - 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) - - -class WaterfallHelp(HtmlResource): - pageTitle = "Waterfall Help" - - def __init__(self, categories=None): - HtmlResource.__init__(self) - self.categories = categories - - def content(self, request, cxt): - status = self.getStatus(request) - - cxt['show_events_checked'] = request.args.get("show_events", ["false"])[0].lower() == "true" - cxt['branches'] = [b for b in request.args.get("branch", []) if b] - cxt['failures_only'] = request.args.get("failures_only", ["false"])[0].lower() == "true" - cxt['committers'] = [c for c in request.args.get("committer", []) if c] - - # this has a set of toggle-buttons to let the user choose the - # builders - show_builders = request.args.get("show", []) - show_builders.extend(request.args.get("builder", [])) - cxt['show_builders'] = show_builders - cxt['all_builders'] = status.getBuilderNames(categories=self.categories) - - # a couple of radio-button selectors for refresh time will appear - # just after that text - times = [("none", "None"), - ("60", "60 seconds"), - ("300", "5 minutes"), - ("600", "10 minutes"), - ] - current_reload_time = request.args.get("reload", ["none"]) - if current_reload_time: - current_reload_time = current_reload_time[0] - if current_reload_time not in [t[0] for t in times]: - times.insert(0, (current_reload_time, current_reload_time) ) - - cxt['times'] = times - cxt['current_reload_time'] = current_reload_time - - template = request.site.buildbot_service.templates.get_template("waterfallhelp.html") - return template.render(**cxt) - - -class ChangeEventSource(object): - "A wrapper around a list of changes to supply the IEventSource interface" - def __init__(self, changes): - self.changes = changes - # we want them in newest-to-oldest order - self.changes.reverse() - - def eventGenerator(self, branches, categories, committers, minTime): - for change in self.changes: - if branches and change.branch not in branches: - continue - if categories and change.category not in categories: - continue - if committers and change.author not in committers: - continue - if minTime and change.when < minTime: - continue - yield change - -class WaterfallStatusResource(HtmlResource): - """This builds the main status page, with the waterfall display, and - all child pages.""" - - def __init__(self, categories=None, num_events=200, num_events_max=None): - HtmlResource.__init__(self) - self.categories = categories - self.num_events=num_events - self.num_events_max=num_events_max - self.putChild("help", WaterfallHelp(categories)) - - def getPageTitle(self, request): - status = self.getStatus(request) - p = status.getTitle() - if p: - return "BuildBot: %s" % p - else: - return "BuildBot" - - def getChangeManager(self, request): - # TODO: this wants to go away, access it through IStatus - return request.site.buildbot_service.getChangeSvc() - - 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 isSuccess(self, builderStatus): - # Helper function to return True if the builder is not failing. - # The function will return false if the current state is "offline", - # the last build was not successful, or if a step from the current - # build(s) failed. - - # Make sure the builder is online. - if builderStatus.getState()[0] == 'offline': - return False - - # Look at the last finished build to see if it was success or not. - lastBuild = builderStatus.getLastFinishedBuild() - if lastBuild and lastBuild.getResults() != builder.SUCCESS: - return False - - # Check all the current builds to see if one step is already - # failing. - currentBuilds = builderStatus.getCurrentBuilds() - if currentBuilds: - for build in currentBuilds: - for step in build.getSteps(): - if step.getResults()[0] == builder.FAILURE: - return False - - # The last finished build was successful, and all the current builds - # don't have any failed steps. - return True - - def content(self, request, ctx): - status = self.getStatus(request) - master = request.site.buildbot_service.master - - # before calling content_with_db_data, make a bunch of database - # queries. This is a sick hack, but beats rewriting the entire - # waterfall around asynchronous calls - - results = {} - - # recent changes - changes_d = master.db.changes.getRecentChanges(40) - def to_changes(chdicts): - return defer.gatherResults([ - changes.Change.fromChdict(master, chdict) - for chdict in chdicts ]) - changes_d.addCallback(to_changes) - def keep_changes(changes): - results['changes'] = changes - changes_d.addCallback(keep_changes) - - # build request counts for each builder - allBuilderNames = status.getBuilderNames(categories=self.categories) - brstatus_ds = [] - brcounts = {} - def keep_count(statuses, builderName): - brcounts[builderName] = len(statuses) - for builderName in allBuilderNames: - builder_status = status.getBuilder(builderName) - d = builder_status.getPendingBuildRequestStatuses() - d.addCallback(keep_count, builderName) - brstatus_ds.append(d) - - # wait for it all to finish - d = defer.gatherResults([ changes_d ] + brstatus_ds) - def call_content(_): - return self.content_with_db_data(results['changes'], - brcounts, request, ctx) - d.addCallback(call_content) - return d - - def content_with_db_data(self, changes, brcounts, request, ctx): - status = self.getStatus(request) - ctx['refresh'] = self.get_reload_time(request) - - # we start with all Builders available to this Waterfall: this is - # limited by the config-file -time categories= argument, and defaults - # to all defined Builders. - allBuilderNames = status.getBuilderNames(categories=self.categories) - builders = [status.getBuilder(name) for name in allBuilderNames] - - # but if the URL has one or more builder= arguments (or the old show= - # argument, which is still accepted for backwards compatibility), we - # use that set of builders instead. We still don't show anything - # outside the config-file time set limited by categories=. - showBuilders = request.args.get("show", []) - showBuilders.extend(request.args.get("builder", [])) - if showBuilders: - builders = [b for b in builders if b.name in showBuilders] - - # now, if the URL has one or category= arguments, use them as a - # filter: only show those builders which belong to one of the given - # categories. - showCategories = request.args.get("category", []) - if showCategories: - builders = [b for b in builders if b.category in showCategories] - - # If the URL has the failures_only=true argument, we remove all the - # builders that are not currently red or won't be turning red at the end - # of their current run. - failuresOnly = request.args.get("failures_only", ["false"])[0] - if failuresOnly.lower() == "true": - builders = [b for b in builders if not self.isSuccess(b)] - - (changeNames, builderNames, timestamps, eventGrid, sourceEvents) = \ - self.buildGrid(request, builders, changes) - - # start the table: top-header material - locale_enc = locale.getdefaultlocale()[1] - if locale_enc is not None: - locale_tz = unicode(time.tzname[time.localtime()[-1]], locale_enc) - else: - locale_tz = unicode(time.tzname[time.localtime()[-1]]) - ctx['tz'] = locale_tz - ctx['changes_url'] = request.childLink("../changes") - - bn = ctx['builders'] = [] - - for name in builderNames: - builder = status.getBuilder(name) - top_box = ITopBox(builder).getBox(request) - current_box = ICurrentBox(builder).getBox(status, brcounts) - bn.append({'name': name, - 'url': request.childLink("../builders/%s" % urllib.quote(name, safe='')), - 'top': top_box.text, - 'top_class': top_box.class_, - 'status': current_box.text, - 'status_class': current_box.class_, - }) - - ctx.update(self.phase2(request, changeNames + builderNames, timestamps, eventGrid, - sourceEvents)) - - def with_args(req, remove_args=[], new_args=[], new_path=None): - # sigh, nevow makes this sort of manipulation easier - newargs = req.args.copy() - for argname in remove_args: - newargs[argname] = [] - if "branch" in newargs: - newargs["branch"] = [b for b in newargs["branch"] if b] - for k,v in new_args: - if k in newargs: - newargs[k].append(v) - else: - newargs[k] = [v] - newquery = "&".join(["%s=%s" % (urllib.quote(k), urllib.quote(v)) - for k in newargs - for v in newargs[k] - ]) - if new_path: - new_url = new_path - elif req.prepath: - new_url = req.prepath[-1] - else: - new_url = '' - if newquery: - new_url += "?" + newquery - return new_url - - if timestamps: - bottom = timestamps[-1] - ctx['nextpage'] = with_args(request, ["last_time"], - [("last_time", str(int(bottom)))]) - - - helpurl = path_to_root(request) + "waterfall/help" - ctx['help_url'] = with_args(request, new_path=helpurl) - - if self.get_reload_time(request) is not None: - ctx['no_reload_page'] = with_args(request, remove_args=["reload"]) - - template = request.site.buildbot_service.templates.get_template("waterfall.html") - data = template.render(**ctx) - return data - - def buildGrid(self, request, builders, changes): - debug = False - # TODO: see if we can use a cached copy - - showEvents = False - if request.args.get("show_events", ["false"])[0].lower() == "true": - showEvents = True - filterCategories = request.args.get('category', []) - filterBranches = [b for b in request.args.get("branch", []) if b] - filterBranches = map_branches(filterBranches) - filterCommitters = [c for c in request.args.get("committer", []) if c] - maxTime = int(request.args.get("last_time", [util.now()])[0]) - if "show_time" in request.args: - minTime = maxTime - int(request.args["show_time"][0]) - elif "first_time" in request.args: - minTime = int(request.args["first_time"][0]) - elif filterBranches or filterCommitters: - minTime = util.now() - 24 * 60 * 60 - else: - minTime = 0 - spanLength = 10 # ten-second chunks - req_events=int(request.args.get("num_events", [self.num_events])[0]) - if self.num_events_max and req_events > self.num_events_max: - maxPageLen = self.num_events_max - else: - maxPageLen = req_events - - # first step is to walk backwards in time, asking each column - # (commit, all builders) if they have any events there. Build up the - # array of events, and stop when we have a reasonable number. - - commit_source = ChangeEventSource(changes) - - lastEventTime = util.now() - sources = [commit_source] + builders - changeNames = ["changes"] - builderNames = map(lambda builder: builder.getName(), builders) - sourceNames = changeNames + builderNames - sourceEvents = [] - sourceGenerators = [] - - def get_event_from(g): - try: - while True: - e = g.next() - # e might be buildstep.BuildStepStatus, - # builder.BuildStatus, builder.Event, - # waterfall.Spacer(builder.Event), or changes.Change . - # The showEvents=False flag means we should hide - # builder.Event . - if not showEvents and isinstance(e, builder.Event): - continue - - if isinstance(e, buildstep.BuildStepStatus): - # unfinished steps are always shown - if e.isFinished() and e.isHidden(): - continue - - break - event = interfaces.IStatusEvent(e) - if debug: - log.msg("gen %s gave1 %s" % (g, event.getText())) - except StopIteration: - event = None - return event - - for s in sources: - gen = insertGaps(s.eventGenerator(filterBranches, - filterCategories, - filterCommitters, - minTime), - showEvents, - lastEventTime) - sourceGenerators.append(gen) - # get the first event - sourceEvents.append(get_event_from(gen)) - eventGrid = [] - timestamps = [] - - lastEventTime = 0 - for e in sourceEvents: - if e and e.getTimes()[0] > lastEventTime: - lastEventTime = e.getTimes()[0] - if lastEventTime == 0: - lastEventTime = util.now() - - spanStart = lastEventTime - spanLength - debugGather = 0 - - while 1: - if debugGather: log.msg("checking (%s,]" % spanStart) - # the tableau of potential events is in sourceEvents[]. The - # window crawls backwards, and we examine one source at a time. - # If the source's top-most event is in the window, is it pushed - # onto the events[] array and the tableau is refilled. This - # continues until the tableau event is not in the window (or is - # missing). - - spanEvents = [] # for all sources, in this span. row of eventGrid - firstTimestamp = None # timestamp of first event in the span - lastTimestamp = None # last pre-span event, for next span - - for c in range(len(sourceGenerators)): - events = [] # for this source, in this span. cell of eventGrid - event = sourceEvents[c] - while event and spanStart < event.getTimes()[0]: - # to look at windows that don't end with the present, - # condition the .append on event.time <= spanFinish - if not IBox(event, None): - log.msg("BAD EVENT", event, event.getText()) - assert 0 - if debug: - log.msg("pushing", event.getText(), event) - events.append(event) - starts, finishes = event.getTimes() - firstTimestamp = earlier(firstTimestamp, starts) - event = get_event_from(sourceGenerators[c]) - if debug: - log.msg("finished span") - - if event: - # this is the last pre-span event for this source - lastTimestamp = later(lastTimestamp, - event.getTimes()[0]) - if debugGather: - log.msg(" got %s from %s" % (events, sourceNames[c])) - sourceEvents[c] = event # refill the tableau - spanEvents.append(events) - - # only show events older than maxTime. This makes it possible to - # visit a page that shows what it would be like to scroll off the - # bottom of this one. - if firstTimestamp is not None and firstTimestamp <= maxTime: - eventGrid.append(spanEvents) - timestamps.append(firstTimestamp) - - if lastTimestamp: - spanStart = lastTimestamp - spanLength - else: - # no more events - break - if minTime is not None and lastTimestamp < minTime: - break - - if len(timestamps) > maxPageLen: - break - - - # now loop - - # loop is finished. now we have eventGrid[] and timestamps[] - if debugGather: log.msg("finished loop") - assert(len(timestamps) == len(eventGrid)) - return (changeNames, builderNames, timestamps, eventGrid, sourceEvents) - - def phase2(self, request, sourceNames, timestamps, eventGrid, - sourceEvents): - - if not timestamps: - return dict(grid=[], gridlen=0) - - # first pass: figure out the height of the chunks, populate grid - grid = [] - for i in range(1+len(sourceNames)): - grid.append([]) - # grid is a list of columns, one for the timestamps, and one per - # event source. Each column is exactly the same height. Each element - # of the list is a single <td> box. - lastDate = time.strftime("%d %b %Y", - time.localtime(util.now())) - for r in range(0, len(timestamps)): - chunkstrip = eventGrid[r] - # chunkstrip is a horizontal strip of event blocks. Each block - # is a vertical list of events, all for the same source. - assert(len(chunkstrip) == len(sourceNames)) - maxRows = reduce(lambda x,y: max(x,y), - map(lambda x: len(x), chunkstrip)) - for i in range(maxRows): - if i != maxRows-1: - grid[0].append(None) - else: - # timestamp goes at the bottom of the chunk - stuff = [] - # add the date at the beginning (if it is not the same as - # today's date), and each time it changes - todayday = time.strftime("%a", - time.localtime(timestamps[r])) - today = time.strftime("%d %b %Y", - time.localtime(timestamps[r])) - if today != lastDate: - stuff.append(todayday) - stuff.append(today) - lastDate = today - stuff.append( - time.strftime("%H:%M:%S", - time.localtime(timestamps[r]))) - grid[0].append(Box(text=stuff, class_="Time", - valign="bottom", align="center")) - - # at this point the timestamp column has been populated with - # maxRows boxes, most None but the last one has the time string - for c in range(0, len(chunkstrip)): - block = chunkstrip[c] - assert(block != None) # should be [] instead - for i in range(maxRows - len(block)): - # fill top of chunk with blank space - grid[c+1].append(None) - for i in range(len(block)): - # so the events are bottom-justified - b = IBox(block[i]).getBox(request) - b.parms['valign'] = "top" - b.parms['align'] = "center" - grid[c+1].append(b) - # now all the other columns have maxRows new boxes too - # populate the last row, if empty - gridlen = len(grid[0]) - for i in range(len(grid)): - strip = grid[i] - assert(len(strip) == gridlen) - if strip[-1] == None: - if sourceEvents[i-1]: - filler = IBox(sourceEvents[i-1]).getBox(request) - else: - # this can happen if you delete part of the build history - filler = Box(text=["?"], align="center") - strip[-1] = filler - strip[-1].parms['rowspan'] = 1 - # second pass: bubble the events upwards to un-occupied locations - # Every square of the grid that has a None in it needs to have - # something else take its place. - noBubble = request.args.get("nobubble",['0']) - noBubble = int(noBubble[0]) - if not noBubble: - for col in range(len(grid)): - strip = grid[col] - if col == 1: # changes are handled differently - for i in range(2, len(strip)+1): - # only merge empty boxes. Don't bubble commit boxes. - if strip[-i] == None: - next = strip[-i+1] - assert(next) - if next: - #if not next.event: - if next.spacer: - # bubble the empty box up - strip[-i] = next - strip[-i].parms['rowspan'] += 1 - strip[-i+1] = None - else: - # we are above a commit box. Leave it - # be, and turn the current box into an - # empty one - strip[-i] = Box([], rowspan=1, - comment="commit bubble") - strip[-i].spacer = True - else: - # we are above another empty box, which - # somehow wasn't already converted. - # Shouldn't happen - pass - else: - for i in range(2, len(strip)+1): - # strip[-i] will go from next-to-last back to first - if strip[-i] == None: - # bubble previous item up - assert(strip[-i+1] != None) - strip[-i] = strip[-i+1] - strip[-i].parms['rowspan'] += 1 - strip[-i+1] = None - else: - strip[-i].parms['rowspan'] = 1 - - # convert to dicts - for i in range(gridlen): - for strip in grid: - if strip[i]: - strip[i] = strip[i].td() - - return dict(grid=grid, gridlen=gridlen, no_bubble=noBubble, time=lastDate) - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/words.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/words.py deleted file mode 100644 index 32565ac6..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/words.py +++ /dev/null @@ -1,1097 +0,0 @@ -# This file is part of Buildbot. Buildbot is free software: you can -# redistribute it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -# details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., 51 -# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Copyright Buildbot Team Members - -import re, shlex, random -from string import join, capitalize, lower - -from zope.interface import implements -from twisted.internet import protocol, reactor -from twisted.words.protocols import irc -from twisted.python import usage, log -from twisted.application import internet -from twisted.internet import defer, task - -from buildbot import config, interfaces, util -from buildbot import version -from buildbot.interfaces import IStatusReceiver -from buildbot.sourcestamp import SourceStamp -from buildbot.status import base -from buildbot.status.results import SUCCESS, WARNINGS, FAILURE, EXCEPTION, RETRY -from buildbot.process.properties import Properties - -# twisted.internet.ssl requires PyOpenSSL, so be resilient if it's missing -try: - from twisted.internet import ssl - have_ssl = True -except ImportError: - have_ssl = False - -def maybeColorize(text, color, useColors): - irc_colors = [ - 'WHITE', - 'BLACK', - 'NAVY_BLUE', - 'GREEN', - 'RED', - 'BROWN', - 'PURPLE', - 'OLIVE', - 'YELLOW', - 'LIME_GREEN', - 'TEAL', - 'AQUA_LIGHT', - 'ROYAL_BLUE', - 'HOT_PINK', - 'DARK_GRAY', - 'LIGHT_GRAY' - ] - - if useColors: - return "%c%d%s%c" % (3, irc_colors.index(color), text, 3) - else: - return text - -class UsageError(ValueError): - def __init__(self, string = "Invalid usage", *more): - ValueError.__init__(self, string, *more) - -class ForceOptions(usage.Options): - optParameters = [ - ["builder", None, None, "which Builder to start"], - ["branch", None, None, "which branch to build"], - ["revision", None, None, "which revision to build"], - ["reason", None, None, "the reason for starting the build"], - ["props", None, None, - "A set of properties made available in the build environment, " - "format is --properties=prop1=value1,prop2=value2,.. " - "option can be specified multiple times."], - ] - - def parseArgs(self, *args): - args = list(args) - if len(args) > 0: - if self['builder'] is not None: - raise UsageError("--builder provided in two ways") - self['builder'] = args.pop(0) - if len(args) > 0: - if self['reason'] is not None: - raise UsageError("--reason provided in two ways") - self['reason'] = " ".join(args) - - -class IrcBuildRequest: - hasStarted = False - timer = None - - def __init__(self, parent, useRevisions=False, useColors=True): - self.parent = parent - self.useRevisions = useRevisions - self.useColors = useColors - self.timer = reactor.callLater(5, self.soon) - - def soon(self): - del self.timer - if not self.hasStarted: - self.parent.send("The build has been queued, I'll give a shout" - " when it starts") - - def started(self, s): - self.hasStarted = True - if self.timer: - self.timer.cancel() - del self.timer - eta = s.getETA() - if self.useRevisions: - response = "build containing revision(s) [%s] forced" % s.getRevisions() - else: - response = "build #%d forced" % s.getNumber() - if eta is not None: - response = "build forced [ETA %s]" % self.parent.convertTime(eta) - self.parent.send(response) - self.parent.send("I'll give a shout when the build finishes") - d = s.waitUntilFinished() - d.addCallback(self.parent.watchedBuildFinished) - -class IRCContact(base.StatusReceiver): - implements(IStatusReceiver) - """I hold the state for a single user's interaction with the buildbot. - - There will be one instance of me for each user who interacts personally - with the buildbot. There will be an additional instance for each - 'broadcast contact' (chat rooms, IRC channels as a whole). - """ - - def __init__(self, bot, dest): - self.bot = bot - self.master = bot.master - self.notify_events = {} - self.subscribed = 0 - self.muted = False - self.useRevisions = bot.useRevisions - self.useColors = bot.useColors - self.reported_builds = [] # tuples (when, buildername, buildnum) - self.add_notification_events(bot.notify_events) - - # when people send us public messages ("buildbot: command"), - # self.dest is the name of the channel ("#twisted"). When they send - # us private messages (/msg buildbot command), self.dest is their - # username. - self.dest = dest - - # silliness - - silly = { - "What happen ?": [ "Somebody set up us the bomb." ], - "It's You !!": ["How are you gentlemen !!", - "All your base are belong to us.", - "You are on the way to destruction."], - "What you say !!": ["You have no chance to survive make your time.", - "HA HA HA HA ...."], - } - - def doSilly(self, message): - response = self.silly[message] - when = 0.5 - for r in response: - reactor.callLater(when, self.send, r) - when += 2.5 - - def getBuilder(self, which): - try: - b = self.bot.status.getBuilder(which) - except KeyError: - raise UsageError, "no such builder '%s'" % which - return b - - def getControl(self, which): - if not self.bot.control: - raise UsageError("builder control is not enabled") - try: - bc = self.bot.control.getBuilder(which) - except KeyError: - raise UsageError("no such builder '%s'" % which) - return bc - - def getAllBuilders(self): - """ - @rtype: list of L{buildbot.process.builder.Builder} - """ - names = self.bot.status.getBuilderNames(categories=self.bot.categories) - names.sort() - builders = [self.bot.status.getBuilder(n) for n in names] - return builders - - def convertTime(self, seconds): - if seconds < 60: - return "%d seconds" % seconds - minutes = int(seconds / 60) - seconds = seconds - 60*minutes - if minutes < 60: - return "%dm%02ds" % (minutes, seconds) - hours = int(minutes / 60) - minutes = minutes - 60*hours - return "%dh%02dm%02ds" % (hours, minutes, seconds) - - def reportBuild(self, builder, buildnum): - """Returns True if this build should be reported for this contact - (eliminating duplicates), and also records the report for later""" - for w, b, n in self.reported_builds: - if b == builder and n == buildnum: - return False - self.reported_builds.append([util.now(), builder, buildnum]) - - # clean the reported builds - horizon = util.now() - 60 - while self.reported_builds and self.reported_builds[0][0] < horizon: - self.reported_builds.pop(0) - - # and return True, since this is a new one - return True - - def splitArgs(self, args): - """Returns list of arguments parsed by shlex.split() or - raise UsageError if failed""" - try: - return shlex.split(args) - except ValueError, e: - raise UsageError(e) - - def command_HELLO(self, args, who): - self.send("yes?") - - def command_VERSION(self, args, who): - self.send("buildbot-%s at your service" % version) - - def command_LIST(self, args, who): - args = self.splitArgs(args) - if len(args) == 0: - raise UsageError, "try 'list builders'" - if args[0] == 'builders': - builders = self.getAllBuilders() - str = "Configured builders: " - for b in builders: - str += b.name - state = b.getState()[0] - if state == 'offline': - str += "[offline]" - str += " " - str.rstrip() - self.send(str) - return - command_LIST.usage = "list builders - List configured builders" - - def command_STATUS(self, args, who): - args = self.splitArgs(args) - if len(args) == 0: - which = "all" - elif len(args) == 1: - which = args[0] - else: - raise UsageError, "try 'status <builder>'" - if which == "all": - builders = self.getAllBuilders() - for b in builders: - self.emit_status(b.name) - return - self.emit_status(which) - command_STATUS.usage = "status [<which>] - List status of a builder (or all builders)" - - def validate_notification_event(self, event): - if not re.compile("^(started|finished|success|failure|exception|warnings|(success|warnings|exception|failure)To(Failure|Success|Warnings|Exception))$").match(event): - raise UsageError("try 'notify on|off <EVENT>'") - - def list_notified_events(self): - self.send( "The following events are being notified: %r" % self.notify_events.keys() ) - - def notify_for(self, *events): - for event in events: - if self.notify_events.has_key(event): - return 1 - return 0 - - def subscribe_to_build_events(self): - self.bot.status.subscribe(self) - self.subscribed = 1 - - def unsubscribe_from_build_events(self): - self.bot.status.unsubscribe(self) - self.subscribed = 0 - - def add_notification_events(self, events): - for event in events: - self.validate_notification_event(event) - self.notify_events[event] = 1 - - if not self.subscribed: - self.subscribe_to_build_events() - - def remove_notification_events(self, events): - for event in events: - self.validate_notification_event(event) - del self.notify_events[event] - - if len(self.notify_events) == 0 and self.subscribed: - self.unsubscribe_from_build_events() - - def remove_all_notification_events(self): - self.notify_events = {} - - if self.subscribed: - self.unsubscribe_from_build_events() - - def command_NOTIFY(self, args, who): - args = self.splitArgs(args) - - if not args: - raise UsageError("try 'notify on|off|list <EVENT>'") - action = args.pop(0) - events = args - - if action == "on": - if not events: events = ('started','finished') - self.add_notification_events(events) - - self.list_notified_events() - - elif action == "off": - if events: - self.remove_notification_events(events) - else: - self.remove_all_notification_events() - - self.list_notified_events() - - elif action == "list": - self.list_notified_events() - return - - else: - raise UsageError("try 'notify on|off <EVENT>'") - - command_NOTIFY.usage = "notify on|off|list [<EVENT>] ... - Notify me about build events. event should be one or more of: 'started', 'finished', 'failure', 'success', 'exception' or 'xToY' (where x and Y are one of success, warnings, failure, exception, but Y is capitalized)" - - def command_WATCH(self, args, who): - args = self.splitArgs(args) - if len(args) != 1: - raise UsageError("try 'watch <builder>'") - which = args[0] - b = self.getBuilder(which) - builds = b.getCurrentBuilds() - if not builds: - self.send("there are no builds currently running") - return - for build in builds: - assert not build.isFinished() - d = build.waitUntilFinished() - d.addCallback(self.watchedBuildFinished) - if self.useRevisions: - r = "watching build %s containing revision(s) [%s] until it finishes" \ - % (which, build.getRevisions()) - else: - r = "watching build %s #%d until it finishes" \ - % (which, build.getNumber()) - eta = build.getETA() - if eta is not None: - r += " [%s]" % self.convertTime(eta) - r += ".." - self.send(r) - command_WATCH.usage = "watch <which> - announce the completion of an active build" - - def builderAdded(self, builderName, builder): - if (self.bot.categories != None and - builder.category not in self.bot.categories): - return - - log.msg('[Contact] Builder %s added' % (builder)) - builder.subscribe(self) - - def builderRemoved(self, builderName): - log.msg('[Contact] Builder %s removed' % (builderName)) - - def buildStarted(self, builderName, build): - builder = build.getBuilder() - log.msg('[Contact] Builder %r in category %s started' % (builder, builder.category)) - - # only notify about builders we are interested in - - if (self.bot.categories != None and - builder.category not in self.bot.categories): - log.msg('Not notifying for a build in the wrong category') - return - - if not self.notify_for('started'): - return - - if self.useRevisions: - r = "build containing revision(s) [%s] on %s started" % \ - (build.getRevisions(), builder.getName()) - else: - r = "build #%d of %s started, including [%s]" % \ - (build.getNumber(), - builder.getName(), - ", ".join([str(c.revision) for c in build.getChanges()]) - ) - - self.send(r) - - results_descriptions = { - SUCCESS: ("Success", 'GREEN'), - WARNINGS: ("Warnings", 'YELLOW'), - FAILURE: ("Failure", 'RED'), - EXCEPTION: ("Exception", 'PURPLE'), - RETRY: ("Retry", 'AQUA_LIGHT'), - } - - def getResultsDescriptionAndColor(self, results): - return self.results_descriptions.get(results, ("??",'RED')) - - def buildFinished(self, builderName, build, results): - builder = build.getBuilder() - - if (self.bot.categories != None and - builder.category not in self.bot.categories): - return - - if not self.notify_for_finished(build): - return - - builder_name = builder.getName() - buildnum = build.getNumber() - buildrevs = build.getRevisions() - - results = self.getResultsDescriptionAndColor(build.getResults()) - if self.reportBuild(builder_name, buildnum): - if self.useRevisions: - r = "build containing revision(s) [%s] on %s is complete: %s" % \ - (buildrevs, builder_name, results[0]) - else: - r = "build #%d of %s is complete: %s" % \ - (buildnum, builder_name, results[0]) - - r += ' [%s]' % maybeColorize(" ".join(build.getText()), results[1], self.useColors) - buildurl = self.bot.status.getURLForThing(build) - if buildurl: - r += " Build details are at %s" % buildurl - - if self.bot.showBlameList and build.getResults() != SUCCESS and len(build.changes) != 0: - r += ' blamelist: ' + ', '.join(list(set([c.who for c in build.changes]))) - - self.send(r) - - def notify_for_finished(self, build): - results = build.getResults() - - if self.notify_for('finished'): - return True - - if self.notify_for(lower(self.results_descriptions.get(results)[0])): - return True - - prevBuild = build.getPreviousBuild() - if prevBuild: - prevResult = prevBuild.getResults() - - required_notification_control_string = join((lower(self.results_descriptions.get(prevResult)[0]), \ - 'To', \ - capitalize(self.results_descriptions.get(results)[0])), \ - '') - - if (self.notify_for(required_notification_control_string)): - return True - - return False - - def watchedBuildFinished(self, b): - - # only notify about builders we are interested in - builder = b.getBuilder() - if (self.bot.categories != None and - builder.category not in self.bot.categories): - return - - builder_name = builder.getName() - buildnum = b.getNumber() - buildrevs = b.getRevisions() - - results = self.getResultsDescriptionAndColor(b.getResults()) - if self.reportBuild(builder_name, buildnum): - if self.useRevisions: - r = "Hey! build %s containing revision(s) [%s] is complete: %s" % \ - (builder_name, buildrevs, results[0]) - else: - r = "Hey! build %s #%d is complete: %s" % \ - (builder_name, buildnum, results[0]) - - r += ' [%s]' % maybeColorize(" ".join(b.getText()), results[1], self.useColors) - self.send(r) - buildurl = self.bot.status.getURLForThing(b) - if buildurl: - self.send("Build details are at %s" % buildurl) - - def command_FORCE(self, args, who): - errReply = "try 'force build [--branch=BRANCH] [--revision=REVISION] [--props=PROP1=VAL1,PROP2=VAL2...] <WHICH> <REASON>'" - args = self.splitArgs(args) - if not args: - raise UsageError(errReply) - what = args.pop(0) - if what != "build": - raise UsageError(errReply) - opts = ForceOptions() - opts.parseOptions(args) - - which = opts['builder'] - branch = opts['branch'] - revision = opts['revision'] - reason = opts['reason'] - props = opts['props'] - - if which is None: - raise UsageError("you must provide a Builder, " + errReply) - - # keep weird stuff out of the branch, revision, and properties args. - branch_validate = self.master.config.validation['branch'] - revision_validate = self.master.config.validation['revision'] - pname_validate = self.master.config.validation['property_name'] - pval_validate = self.master.config.validation['property_value'] - if branch and not branch_validate.match(branch): - log.msg("bad branch '%s'" % branch) - self.send("sorry, bad branch '%s'" % branch) - return - if revision and not revision_validate.match(revision): - log.msg("bad revision '%s'" % revision) - self.send("sorry, bad revision '%s'" % revision) - return - - properties = Properties() - if props: - # split props into name:value dict - pdict = {} - propertylist = props.split(",") - for i in range(0,len(propertylist)): - splitproperty = propertylist[i].split("=", 1) - pdict[splitproperty[0]] = splitproperty[1] - - # set properties - for prop in pdict: - pname = prop - pvalue = pdict[prop] - if not pname_validate.match(pname) \ - or not pval_validate.match(pvalue): - log.msg("bad property name='%s', value='%s'" % (pname, pvalue)) - self.send("sorry, bad property name='%s', value='%s'" % - (pname, pvalue)) - return - properties.setProperty(pname, pvalue, "Force Build IRC") - - bc = self.getControl(which) - - reason = "forced: by %s: %s" % (self.describeUser(who), reason) - ss = SourceStamp(branch=branch, revision=revision) - d = bc.submitBuildRequest(ss, reason, props=properties.asDict()) - def subscribe(buildreq): - ireq = IrcBuildRequest(self, self.useRevisions) - buildreq.subscribe(ireq.started) - d.addCallback(subscribe) - d.addErrback(log.err, "while forcing a build") - - - command_FORCE.usage = "force build [--branch=branch] [--revision=revision] [--props=prop1=val1,prop2=val2...] <which> <reason> - Force a build" - - def command_STOP(self, args, who): - args = self.splitArgs(args) - if len(args) < 3 or args[0] != 'build': - raise UsageError, "try 'stop build WHICH <REASON>'" - which = args[1] - reason = args[2] - - buildercontrol = self.getControl(which) - - r = "stopped: by %s: %s" % (self.describeUser(who), reason) - - # find an in-progress build - builderstatus = self.getBuilder(which) - builds = builderstatus.getCurrentBuilds() - if not builds: - self.send("sorry, no build is currently running") - return - for build in builds: - num = build.getNumber() - revs = build.getRevisions() - - # obtain the BuildControl object - buildcontrol = buildercontrol.getBuild(num) - - # make it stop - buildcontrol.stopBuild(r) - - if self.useRevisions: - response = "build containing revision(s) [%s] interrupted" % revs - else: - response = "build %d interrupted" % num - self.send(response) - - command_STOP.usage = "stop build <which> <reason> - Stop a running build" - - def emit_status(self, which): - b = self.getBuilder(which) - str = "%s: " % which - state, builds = b.getState() - str += state - if state == "idle": - last = b.getLastFinishedBuild() - if last: - start,finished = last.getTimes() - str += ", last build %s ago: %s" % \ - (self.convertTime(int(util.now() - finished)), " ".join(last.getText())) - if state == "building": - t = [] - for build in builds: - step = build.getCurrentStep() - if step: - s = "(%s)" % " ".join(step.getText()) - else: - s = "(no current step)" - ETA = build.getETA() - if ETA is not None: - s += " [ETA %s]" % self.convertTime(ETA) - t.append(s) - str += ", ".join(t) - self.send(str) - - def command_LAST(self, args, who): - args = self.splitArgs(args) - - if len(args) == 0: - which = "all" - elif len(args) == 1: - which = args[0] - else: - raise UsageError, "try 'last <builder>'" - - def emit_last(which): - last = self.getBuilder(which).getLastFinishedBuild() - if not last: - str = "(no builds run since last restart)" - else: - start,finish = last.getTimes() - str = "%s ago: " % (self.convertTime(int(util.now() - finish))) - str += " ".join(last.getText()) - self.send("last build [%s]: %s" % (which, str)) - - if which == "all": - builders = self.getAllBuilders() - for b in builders: - emit_last(b.name) - return - emit_last(which) - command_LAST.usage = "last <which> - list last build status for builder <which>" - - def build_commands(self): - commands = [] - for k in dir(self): - if k.startswith('command_'): - commands.append(k[8:].lower()) - commands.sort() - return commands - - def describeUser(self, user): - if self.dest[0] == '#': - return "IRC user <%s> on channel %s" % (user, self.dest) - return "IRC user <%s> (privmsg)" % user - - # commands - - def command_MUTE(self, args, who): - # The order of these is important! ;) - self.send("Shutting up for now.") - self.muted = True - command_MUTE.usage = "mute - suppress all messages until a corresponding 'unmute' is issued" - - def command_UNMUTE(self, args, who): - if self.muted: - # The order of these is important! ;) - self.muted = False - self.send("I'm baaaaaaaaaaack!") - else: - self.send("You hadn't told me to be quiet, but it's the thought that counts, right?") - command_UNMUTE.usage = "unmute - disable a previous 'mute'" - - def command_HELP(self, args, who): - args = self.splitArgs(args) - if len(args) == 0: - self.send("Get help on what? (try 'help <foo>', 'help <foo> <bar>, " - "or 'commands' for a command list)") - return - command = args[0] - meth = self.getCommandMethod(command) - if not meth: - raise UsageError, "no such command '%s'" % command - usage = getattr(meth, 'usage', None) - if isinstance(usage, dict): - if len(args) == 1: - k = None # command - elif len(args) == 2: - k = args[1] # command arg - else: - k = tuple(args[1:]) # command arg subarg ... - usage = usage.get(k, None) - if usage: - self.send("Usage: %s" % usage) - else: - self.send("No usage info for " + ' '.join(["'%s'" % arg for arg in args])) - command_HELP.usage = "help <command> [<arg> [<subarg> ...]] - Give help for <command> or one of it's arguments" - - def command_SOURCE(self, args, who): - self.send("My source can be found at " - "https://github.com/buildbot/buildbot") - command_SOURCE.usage = "source - the source code for Buildbot" - - def command_COMMANDS(self, args, who): - commands = self.build_commands() - str = "buildbot commands: " + ", ".join(commands) - self.send(str) - command_COMMANDS.usage = "commands - List available commands" - - def command_DESTROY(self, args, who): - if self.bot.nickname not in args: - self.act("readies phasers") - - def command_DANCE(self, args, who): - reactor.callLater(1.0, self.send, "<(^.^<)") - reactor.callLater(2.0, self.send, "<(^.^)>") - reactor.callLater(3.0, self.send, "(>^.^)>") - reactor.callLater(3.5, self.send, "(7^.^)7") - reactor.callLater(5.0, self.send, "(>^.^<)") - - def command_SHUTDOWN(self, args, who): - if args not in ('check','start','stop','now'): - raise UsageError("try 'shutdown check|start|stop|now'") - - if not self.bot.factory.allowShutdown: - raise UsageError("shutdown control is not enabled") - - botmaster = self.master.botmaster - shuttingDown = botmaster.shuttingDown - - if args == 'check': - if shuttingDown: - self.send("Status: buildbot is shutting down") - else: - self.send("Status: buildbot is running") - elif args == 'start': - if shuttingDown: - self.send("Already started") - else: - self.send("Starting clean shutdown") - botmaster.cleanShutdown() - elif args == 'stop': - if not shuttingDown: - self.send("Nothing to stop") - else: - self.send("Stopping clean shutdown") - botmaster.cancelCleanShutdown() - elif args == 'now': - self.send("Stopping buildbot") - reactor.stop() - command_SHUTDOWN.usage = { - None: "shutdown check|start|stop|now - shutdown the buildbot master", - "check": "shutdown check - check if the buildbot master is running or shutting down", - "start": "shutdown start - start a clean shutdown", - "stop": "shutdown cancel - stop the clean shutdown", - "now": "shutdown now - shutdown immediately without waiting for the builders to finish"} - - # communication with the user - - def send(self, message): - if not self.muted: - self.bot.msgOrNotice(self.dest, message.encode("ascii", "replace")) - - def act(self, action): - if not self.muted: - self.bot.describe(self.dest, action.encode("ascii", "replace")) - - # main dispatchers for incoming messages - - def getCommandMethod(self, command): - return getattr(self, 'command_' + command.upper(), None) - - def handleMessage(self, message, who): - # a message has arrived from 'who'. For broadcast contacts (i.e. when - # people do an irc 'buildbot: command'), this will be a string - # describing the sender of the message in some useful-to-log way, and - # a single Contact may see messages from a variety of users. For - # unicast contacts (i.e. when people do an irc '/msg buildbot - # command'), a single Contact will only ever see messages from a - # single user. - message = message.lstrip() - if self.silly.has_key(message): - self.doSilly(message) - return defer.succeed(None) - - parts = message.split(' ', 1) - if len(parts) == 1: - parts = parts + [''] - cmd, args = parts - log.msg("irc command", cmd) - - meth = self.getCommandMethod(cmd) - if not meth and message[-1] == '!': - self.send("What you say!") - return defer.succeed(None) - - if meth: - d = defer.maybeDeferred(meth, args.strip(), who) - @d.addErrback - def usageError(f): - f.trap(UsageError) - self.send(str(f.value)) - @d.addErrback - def logErr(f): - log.err(f) - self.send("Something bad happened (see logs)") - d.addErrback(log.err) - return d - return defer.succeed(None) - - def handleAction(self, data, user): - # this is sent when somebody performs an action that mentions the - # buildbot (like '/me kicks buildbot'). 'user' is the name/nick/id of - # the person who performed the action, so if their action provokes a - # response, they can be named. This is 100% silly. - if not data.endswith("s "+ self.bot.nickname): - return - words = data.split() - verb = words[-2] - if verb == "kicks": - response = "%s back" % verb - else: - response = "%s %s too" % (verb, user) - self.act(response) - - -class IrcStatusBot(irc.IRCClient): - """I represent the buildbot to an IRC server. - """ - contactClass = IRCContact - - def __init__(self, nickname, password, channels, pm_to_nicks, status, - categories, notify_events, noticeOnChannel=False, - useRevisions=False, showBlameList=False, useColors=True): - self.nickname = nickname - self.channels = channels - self.pm_to_nicks = pm_to_nicks - self.password = password - self.status = status - self.master = status.master - self.categories = categories - self.notify_events = notify_events - self.hasQuit = 0 - self.contacts = {} - self.noticeOnChannel = noticeOnChannel - self.useColors = useColors - self.useRevisions = useRevisions - self.showBlameList = showBlameList - self._keepAliveCall = task.LoopingCall(lambda: self.ping(self.nickname)) - - def connectionMade(self): - irc.IRCClient.connectionMade(self) - self._keepAliveCall.start(60) - - def connectionLost(self, reason): - if self._keepAliveCall.running: - self._keepAliveCall.stop() - irc.IRCClient.connectionLost(self, reason) - - def msgOrNotice(self, dest, message): - if self.noticeOnChannel and dest[0] == '#': - self.notice(dest, message) - else: - self.msg(dest, message) - - def getContact(self, name): - name = name.lower() # nicknames and channel names are case insensitive - if name in self.contacts: - return self.contacts[name] - new_contact = self.contactClass(self, name) - self.contacts[name] = new_contact - return new_contact - - def log(self, msg): - log.msg("%s: %s" % (self, msg)) - - - # the following irc.IRCClient methods are called when we have input - - def privmsg(self, user, channel, message): - user = user.split('!', 1)[0] # rest is ~user@hostname - # channel is '#twisted' or 'buildbot' (for private messages) - if channel == self.nickname: - # private message - contact = self.getContact(user) - contact.handleMessage(message, user) - return - # else it's a broadcast message, maybe for us, maybe not. 'channel' - # is '#twisted' or the like. - contact = self.getContact(channel) - if message.startswith("%s:" % self.nickname) or message.startswith("%s," % self.nickname): - message = message[len("%s:" % self.nickname):] - contact.handleMessage(message, user) - - def action(self, user, channel, data): - user = user.split('!', 1)[0] # rest is ~user@hostname - # somebody did an action (/me actions) in the broadcast channel - contact = self.getContact(channel) - if self.nickname in data: - contact.handleAction(data, user) - - def signedOn(self): - if self.password: - self.msg("Nickserv", "IDENTIFY " + self.password) - for c in self.channels: - if isinstance(c, dict): - channel = c.get('channel', None) - password = c.get('password', None) - else: - channel = c - password = None - self.join(channel=channel, key=password) - for c in self.pm_to_nicks: - self.getContact(c) - - def joined(self, channel): - self.log("I have joined %s" % (channel,)) - # trigger contact contructor, which in turn subscribes to notify events - self.getContact(channel) - - def left(self, channel): - self.log("I have left %s" % (channel,)) - - def kickedFrom(self, channel, kicker, message): - self.log("I have been kicked from %s by %s: %s" % (channel, - kicker, - message)) - - -class ThrottledClientFactory(protocol.ClientFactory): - lostDelay = random.randint(1, 5) - failedDelay = random.randint(45, 60) - - def __init__(self, lostDelay=None, failedDelay=None): - if lostDelay is not None: - self.lostDelay = lostDelay - if failedDelay is not None: - self.failedDelay = failedDelay - - def clientConnectionLost(self, connector, reason): - reactor.callLater(self.lostDelay, connector.connect) - - def clientConnectionFailed(self, connector, reason): - reactor.callLater(self.failedDelay, connector.connect) - - -class IrcStatusFactory(ThrottledClientFactory): - protocol = IrcStatusBot - - status = None - control = None - shuttingDown = False - p = None - - def __init__(self, nickname, password, channels, pm_to_nicks, categories, notify_events, - noticeOnChannel=False, useRevisions=False, showBlameList=False, - lostDelay=None, failedDelay=None, useColors=True, allowShutdown=False): - ThrottledClientFactory.__init__(self, lostDelay=lostDelay, - failedDelay=failedDelay) - self.status = None - self.nickname = nickname - self.password = password - self.channels = channels - self.pm_to_nicks = pm_to_nicks - self.categories = categories - self.notify_events = notify_events - self.noticeOnChannel = noticeOnChannel - self.useRevisions = useRevisions - self.showBlameList = showBlameList - self.useColors = useColors - self.allowShutdown = allowShutdown - - def __getstate__(self): - d = self.__dict__.copy() - del d['p'] - return d - - def shutdown(self): - self.shuttingDown = True - if self.p: - self.p.quit("buildmaster reconfigured: bot disconnecting") - - def buildProtocol(self, address): - p = self.protocol(self.nickname, self.password, - self.channels, self.pm_to_nicks, self.status, - self.categories, self.notify_events, - noticeOnChannel = self.noticeOnChannel, - useColors = self.useColors, - useRevisions = self.useRevisions, - showBlameList = self.showBlameList) - p.factory = self - p.status = self.status - p.control = self.control - self.p = p - return p - - # TODO: I think a shutdown that occurs while the connection is being - # established will make this explode - - def clientConnectionLost(self, connector, reason): - if self.shuttingDown: - log.msg("not scheduling reconnection attempt") - return - ThrottledClientFactory.clientConnectionLost(self, connector, reason) - - def clientConnectionFailed(self, connector, reason): - if self.shuttingDown: - log.msg("not scheduling reconnection attempt") - return - ThrottledClientFactory.clientConnectionFailed(self, connector, reason) - - -class IRC(base.StatusReceiverMultiService): - implements(IStatusReceiver) - - in_test_harness = False - - compare_attrs = ["host", "port", "nick", "password", - "channels", "pm_to_nicks", "allowForce", "useSSL", - "useRevisions", "categories", "useColors", - "lostDelay", "failedDelay", "allowShutdown"] - - def __init__(self, host, nick, channels, pm_to_nicks=[], port=6667, - allowForce=False, categories=None, password=None, notify_events={}, - noticeOnChannel = False, showBlameList = True, useRevisions=False, - useSSL=False, lostDelay=None, failedDelay=None, useColors=True, - allowShutdown=False): - base.StatusReceiverMultiService.__init__(self) - - if allowForce not in (True, False): - config.error("allowForce must be boolean, not %r" % (allowForce,)) - if allowShutdown not in (True, False): - config.error("allowShutdown must be boolean, not %r" % (allowShutdown,)) - - # need to stash these so we can detect changes later - self.host = host - self.port = port - self.nick = nick - self.channels = channels - self.pm_to_nicks = pm_to_nicks - self.password = password - self.allowForce = allowForce - self.useRevisions = useRevisions - self.categories = categories - self.notify_events = notify_events - self.allowShutdown = allowShutdown - - self.f = IrcStatusFactory(self.nick, self.password, - self.channels, self.pm_to_nicks, - self.categories, self.notify_events, - noticeOnChannel = noticeOnChannel, - useRevisions = useRevisions, - showBlameList = showBlameList, - lostDelay = lostDelay, - failedDelay = failedDelay, - useColors = useColors, - allowShutdown = allowShutdown) - - if useSSL: - # SSL client needs a ClientContextFactory for some SSL mumbo-jumbo - if not have_ssl: - raise RuntimeError("useSSL requires PyOpenSSL") - cf = ssl.ClientContextFactory() - c = internet.SSLClient(self.host, self.port, self.f, cf) - else: - c = internet.TCPClient(self.host, self.port, self.f) - - c.setServiceParent(self) - - def setServiceParent(self, parent): - base.StatusReceiverMultiService.setServiceParent(self, parent) - self.f.status = parent - if self.allowForce: - self.f.control = interfaces.IControl(self.master) - - def stopService(self): - # make sure the factory will stop reconnecting - self.f.shutdown() - return base.StatusReceiverMultiService.stopService(self) - |