diff options
Diffstat (limited to 'lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers')
10 files changed, 0 insertions, 2444 deletions
diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/__init__.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/__init__.py +++ /dev/null diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/base.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/base.py deleted file mode 100644 index 4b18f935..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/base.py +++ /dev/null @@ -1,453 +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 failure, log -from twisted.application import service -from twisted.internet import defer -from buildbot.process.properties import Properties -from buildbot.util import ComparableMixin -from buildbot import config, interfaces -from buildbot.util.state import StateMixin - -class BaseScheduler(service.MultiService, ComparableMixin, StateMixin): - """ - Base class for all schedulers; this provides the equipment to manage - reconfigurations and to handle basic scheduler state. It also provides - utility methods to begin various sorts of builds. - - Subclasses should add any configuration-derived attributes to - C{base.Scheduler.compare_attrs}. - """ - - implements(interfaces.IScheduler) - - DefaultCodebases = {'':{}} - - compare_attrs = ('name', 'builderNames', 'properties', 'codebases') - - def __init__(self, name, builderNames, properties, - codebases = DefaultCodebases): - """ - Initialize a Scheduler. - - @param name: name of this scheduler (used as a key for state) - @type name: unicode - - @param builderNames: list of builders this scheduler may start - @type builderNames: list of unicode - - @param properties: properties to add to builds triggered by this - scheduler - @type properties: dictionary - - @param codebases: codebases that are necessary to process the changes - @type codebases: dict with following struct: - key: '<codebase>' - value: {'repository':'<repo>', 'branch':'<br>', 'revision:'<rev>'} - - @param consumeChanges: true if this scheduler wishes to be informed - about the addition of new changes. Defaults to False. This should - be passed explicitly from subclasses to indicate their interest in - consuming changes. - @type consumeChanges: boolean - """ - service.MultiService.__init__(self) - self.name = name - "name of this scheduler; used to identify replacements on reconfig" - - ok = True - if not isinstance(builderNames, (list, tuple)): - ok = False - else: - for b in builderNames: - if not isinstance(b, basestring): - ok = False - if not ok: - config.error( - "The builderNames argument to a scheduler must be a list " - "of Builder names.") - - self.builderNames = builderNames - "list of builder names to start in each buildset" - - self.properties = Properties() - "properties that are contributed to each buildset" - self.properties.update(properties, "Scheduler") - self.properties.setProperty("scheduler", name, "Scheduler") - - self.objectid = None - - self.master = None - - # Set the codebases that are necessary to process the changes - # These codebases will always result in a sourcestamp with or without changes - if codebases is not None: - if not isinstance(codebases, dict): - config.error("Codebases must be a dict of dicts") - for codebase, codebase_attrs in codebases.iteritems(): - if not isinstance(codebase_attrs, dict): - config.error("Codebases must be a dict of dicts") - if (codebases != BaseScheduler.DefaultCodebases and - 'repository' not in codebase_attrs): - config.error("The key 'repository' is mandatory in codebases") - else: - config.error("Codebases cannot be None") - - self.codebases = codebases - - # internal variables - self._change_subscription = None - self._change_consumption_lock = defer.DeferredLock() - - ## service handling - - def startService(self): - service.MultiService.startService(self) - - def findNewSchedulerInstance(self, new_config): - return new_config.schedulers[self.name] # should exist! - - def stopService(self): - d = defer.maybeDeferred(self._stopConsumingChanges) - d.addCallback(lambda _ : service.MultiService.stopService(self)) - return d - - - ## status queries - - # TODO: these aren't compatible with distributed schedulers - - def listBuilderNames(self): - "Returns the list of builder names" - return self.builderNames - - def getPendingBuildTimes(self): - "Returns a list of the next times that builds are scheduled, if known." - return [] - - ## change handling - - def startConsumingChanges(self, fileIsImportant=None, change_filter=None, - onlyImportant=False): - """ - Subclasses should call this method from startService to register to - receive changes. The BaseScheduler class will take care of filtering - the changes (using change_filter) and (if fileIsImportant is not None) - classifying them. See L{gotChange}. Returns a Deferred. - - @param fileIsImportant: a callable provided by the user to distinguish - important and unimportant changes - @type fileIsImportant: callable - - @param change_filter: a filter to determine which changes are even - considered by this scheduler, or C{None} to consider all changes - @type change_filter: L{buildbot.changes.filter.ChangeFilter} instance - - @param onlyImportant: If True, only important changes, as specified by - fileIsImportant, will be added to the buildset. - @type onlyImportant: boolean - - """ - assert fileIsImportant is None or callable(fileIsImportant) - - # register for changes with master - assert not self._change_subscription - def changeCallback(change): - # ignore changes delivered while we're not running - if not self._change_subscription: - return - - if change_filter and not change_filter.filter_change(change): - return - if change.codebase not in self.codebases: - log.msg(format='change contains codebase %(codebase)s that is' - 'not processed by scheduler %(scheduler)s', - codebase=change.codebase, name=self.name) - return - if fileIsImportant: - try: - important = fileIsImportant(change) - if not important and onlyImportant: - return - except: - log.err(failure.Failure(), - 'in fileIsImportant check for %s' % change) - return - else: - important = True - - # use change_consumption_lock to ensure the service does not stop - # while this change is being processed - d = self._change_consumption_lock.run(self.gotChange, change, important) - d.addErrback(log.err, 'while processing change') - self._change_subscription = self.master.subscribeToChanges(changeCallback) - - return defer.succeed(None) - - def _stopConsumingChanges(self): - # (note: called automatically in stopService) - - # acquire the lock change consumption lock to ensure that any change - # consumption is complete before we are done stopping consumption - def stop(): - if self._change_subscription: - self._change_subscription.unsubscribe() - self._change_subscription = None - return self._change_consumption_lock.run(stop) - - def gotChange(self, change, important): - """ - Called when a change is received; returns a Deferred. If the - C{fileIsImportant} parameter to C{startConsumingChanges} was C{None}, - then all changes are considered important. - The C{codebase} of the change has always an entry in the C{codebases} - dictionary of the scheduler. - - @param change: the new change object - @type change: L{buildbot.changes.changes.Change} instance - @param important: true if this is an important change, according to - C{fileIsImportant}. - @type important: boolean - @returns: Deferred - """ - raise NotImplementedError - - ## starting bulids - - @defer.inlineCallbacks - def addBuildsetForLatest(self, reason='', external_idstring=None, - branch=None, repository='', project='', - builderNames=None, properties=None): - """ - Add a buildset for the 'latest' source in the given branch, - repository, and project. This will create a relative sourcestamp for - the buildset. - - This method will add any properties provided to the scheduler - constructor to the buildset, and will call the master's addBuildset - method with the appropriate parameters. - - @param reason: reason for this buildset - @type reason: unicode string - @param external_idstring: external identifier for this buildset, or None - @param branch: branch to build (note that None often has a special meaning) - @param repository: repository name for sourcestamp - @param project: project name for sourcestamp - @param builderNames: builders to name in the buildset (defaults to - C{self.builderNames}) - @param properties: a properties object containing initial properties for - the buildset - @type properties: L{buildbot.process.properties.Properties} - @returns: (buildset ID, buildrequest IDs) via Deferred - """ - # Define setid for this set of changed repositories - setid = yield self.master.db.sourcestampsets.addSourceStampSet() - - # add a sourcestamp for each codebase - for codebase, cb_info in self.codebases.iteritems(): - ss_repository = cb_info.get('repository', repository) - ss_branch = cb_info.get('branch', branch) - ss_revision = cb_info.get('revision', None) - yield self.master.db.sourcestamps.addSourceStamp( - codebase=codebase, - repository=ss_repository, - branch=ss_branch, - revision=ss_revision, - project=project, - changeids=set(), - sourcestampsetid=setid) - - bsid,brids = yield self.addBuildsetForSourceStamp( - setid=setid, reason=reason, - external_idstring=external_idstring, - builderNames=builderNames, - properties=properties) - - defer.returnValue((bsid,brids)) - - - @defer.inlineCallbacks - def addBuildsetForSourceStampDetails(self, reason='', external_idstring=None, - branch=None, repository='', project='', revision=None, - builderNames=None, properties=None): - """ - Given details about the source code to build, create a source stamp and - then add a buildset for it. - - @param reason: reason for this buildset - @type reason: unicode string - @param external_idstring: external identifier for this buildset, or None - @param branch: branch to build (note that None often has a special meaning) - @param repository: repository name for sourcestamp - @param project: project name for sourcestamp - @param revision: revision to build - default is latest - @param builderNames: builders to name in the buildset (defaults to - C{self.builderNames}) - @param properties: a properties object containing initial properties for - the buildset - @type properties: L{buildbot.process.properties.Properties} - @returns: (buildset ID, buildrequest IDs) via Deferred - """ - # Define setid for this set of changed repositories - setid = yield self.master.db.sourcestampsets.addSourceStampSet() - - yield self.master.db.sourcestamps.addSourceStamp( - branch=branch, revision=revision, repository=repository, - project=project, sourcestampsetid=setid) - - rv = yield self.addBuildsetForSourceStamp( - setid=setid, reason=reason, - external_idstring=external_idstring, - builderNames=builderNames, - properties=properties) - defer.returnValue(rv) - - - @defer.inlineCallbacks - def addBuildsetForSourceStampSetDetails(self, reason, sourcestamps, - properties, builderNames=None): - if sourcestamps is None: - sourcestamps = {} - - # Define new setid for this set of sourcestamps - new_setid = yield self.master.db.sourcestampsets.addSourceStampSet() - - # Merge codebases with the passed list of sourcestamps - # This results in a new sourcestamp for each codebase - for codebase in self.codebases: - ss = self.codebases[codebase].copy() - # apply info from passed sourcestamps onto the configured default - # sourcestamp attributes for this codebase. - ss.update(sourcestamps.get(codebase,{})) - - # add sourcestamp to the new setid - yield self.master.db.sourcestamps.addSourceStamp( - codebase=codebase, - repository=ss.get('repository', ''), - branch=ss.get('branch', None), - revision=ss.get('revision', None), - project=ss.get('project', ''), - changeids=[c['number'] for c in ss.get('changes', [])], - patch_body=ss.get('patch_body', None), - patch_level=ss.get('patch_level', None), - patch_author=ss.get('patch_author', None), - patch_comment=ss.get('patch_comment', None), - sourcestampsetid=new_setid) - - rv = yield self.addBuildsetForSourceStamp( - setid=new_setid, reason=reason, - properties=properties, - builderNames=builderNames) - - defer.returnValue(rv) - - - @defer.inlineCallbacks - def addBuildsetForChanges(self, reason='', external_idstring=None, - changeids=[], builderNames=None, properties=None): - changesByCodebase = {} - - def get_last_change_for_codebase(codebase): - return max(changesByCodebase[codebase],key = lambda change: change["changeid"]) - - # Define setid for this set of changed repositories - setid = yield self.master.db.sourcestampsets.addSourceStampSet() - - # Changes are retrieved from database and grouped by their codebase - for changeid in changeids: - chdict = yield self.master.db.changes.getChange(changeid) - # group change by codebase - changesByCodebase.setdefault(chdict["codebase"], []).append(chdict) - - for codebase in self.codebases: - args = {'codebase': codebase, 'sourcestampsetid': setid } - if codebase not in changesByCodebase: - # codebase has no changes - # create a sourcestamp that has no changes - args['repository'] = self.codebases[codebase]['repository'] - args['branch'] = self.codebases[codebase].get('branch', None) - args['revision'] = self.codebases[codebase].get('revision', None) - args['changeids'] = set() - args['project'] = '' - else: - #codebase has changes - args['changeids'] = [c["changeid"] for c in changesByCodebase[codebase]] - lastChange = get_last_change_for_codebase(codebase) - for key in ['repository', 'branch', 'revision', 'project']: - args[key] = lastChange[key] - - yield self.master.db.sourcestamps.addSourceStamp(**args) - - # add one buildset, this buildset is connected to the sourcestamps by the setid - bsid,brids = yield self.addBuildsetForSourceStamp( setid=setid, - reason=reason, external_idstring=external_idstring, - builderNames=builderNames, properties=properties) - - defer.returnValue((bsid,brids)) - - @defer.inlineCallbacks - def addBuildsetForSourceStamp(self, ssid=None, setid=None, reason='', external_idstring=None, - properties=None, builderNames=None): - """ - Add a buildset for the given, already-existing sourcestamp. - - This method will add any properties provided to the scheduler - constructor to the buildset, and will call the master's - L{BuildMaster.addBuildset} method with the appropriate parameters, and - return the same result. - - @param reason: reason for this buildset - @type reason: unicode string - @param external_idstring: external identifier for this buildset, or None - @param properties: a properties object containing initial properties for - the buildset - @type properties: L{buildbot.process.properties.Properties} - @param builderNames: builders to name in the buildset (defaults to - C{self.builderNames}) - @param setid: idenitification of a set of sourcestamps - @returns: (buildset ID, buildrequest IDs) via Deferred - """ - assert (ssid is None and setid is not None) \ - or (ssid is not None and setid is None), "pass a single sourcestamp OR set not both" - - # combine properties - if properties: - properties.updateFromProperties(self.properties) - else: - properties = self.properties - - # apply the default builderNames - if not builderNames: - builderNames = self.builderNames - - # translate properties object into a dict as required by the - # addBuildset method - properties_dict = properties.asDict() - - if setid == None: - if ssid is not None: - ssdict = yield self.master.db.sourcestamps.getSourceStamp(ssid) - setid = ssdict['sourcestampsetid'] - else: - # no sourcestamp and no sets - yield None - - rv = yield self.master.addBuildset(sourcestampsetid=setid, - reason=reason, properties=properties_dict, - builderNames=builderNames, - external_idstring=external_idstring) - defer.returnValue(rv) - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/basic.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/basic.py deleted file mode 100644 index a2b9f19c..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/basic.py +++ /dev/null @@ -1,256 +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, reactor -from twisted.python import log -from buildbot import util, config -from buildbot.util import NotABranch -from collections import defaultdict -from buildbot.changes import filter, changes -from buildbot.schedulers import base, dependent - -class BaseBasicScheduler(base.BaseScheduler): - """ - @param onlyImportant: If True, only important changes will be added to the - buildset. - @type onlyImportant: boolean - - """ - - compare_attrs = (base.BaseScheduler.compare_attrs + - ('treeStableTimer', 'change_filter', 'fileIsImportant', - 'onlyImportant') ) - - _reactor = reactor # for tests - - fileIsImportant = None - - class NotSet: pass - def __init__(self, name, shouldntBeSet=NotSet, treeStableTimer=None, - builderNames=None, branch=NotABranch, branches=NotABranch, - fileIsImportant=None, properties={}, categories=None, - change_filter=None, onlyImportant=False, **kwargs): - if shouldntBeSet is not self.NotSet: - config.error( - "pass arguments to schedulers using keyword arguments") - if fileIsImportant and not callable(fileIsImportant): - config.error( - "fileIsImportant must be a callable") - - # initialize parent classes - base.BaseScheduler.__init__(self, name, builderNames, properties, **kwargs) - - self.treeStableTimer = treeStableTimer - if fileIsImportant is not None: - self.fileIsImportant = fileIsImportant - self.onlyImportant = onlyImportant - self.change_filter = self.getChangeFilter(branch=branch, - branches=branches, change_filter=change_filter, - categories=categories) - - # the IDelayedCall used to wake up when this scheduler's - # treeStableTimer expires. - self._stable_timers = defaultdict(lambda : None) - self._stable_timers_lock = defer.DeferredLock() - - def getChangeFilter(self, branch, branches, change_filter, categories): - raise NotImplementedError - - def startService(self, _returnDeferred=False): - base.BaseScheduler.startService(self) - - d = self.startConsumingChanges(fileIsImportant=self.fileIsImportant, - change_filter=self.change_filter, - onlyImportant=self.onlyImportant) - - # if treeStableTimer is False, then we don't care about classified - # changes, so get rid of any hanging around from previous - # configurations - if not self.treeStableTimer: - d.addCallback(lambda _ : - self.master.db.schedulers.flushChangeClassifications( - self.objectid)) - - # otherwise, if there are classified changes out there, start their - # treeStableTimers again - else: - d.addCallback(lambda _ : - self.scanExistingClassifiedChanges()) - - # handle Deferred errors, since startService does not return a Deferred - d.addErrback(log.err, "while starting SingleBranchScheduler '%s'" - % self.name) - - if _returnDeferred: - return d # only used in tests - - def stopService(self): - # the base stopService will unsubscribe from new changes - d = base.BaseScheduler.stopService(self) - @util.deferredLocked(self._stable_timers_lock) - def cancel_timers(_): - for timer in self._stable_timers.values(): - if timer: - timer.cancel() - self._stable_timers.clear() - d.addCallback(cancel_timers) - return d - - @util.deferredLocked('_stable_timers_lock') - def gotChange(self, change, important): - if not self.treeStableTimer: - # if there's no treeStableTimer, we can completely ignore - # unimportant changes - if not important: - return defer.succeed(None) - # otherwise, we'll build it right away - return self.addBuildsetForChanges(reason='scheduler', - changeids=[ change.number ]) - - timer_name = self.getTimerNameForChange(change) - - # if we have a treeStableTimer, then record the change's importance - # and: - # - for an important change, start the timer - # - for an unimportant change, reset the timer if it is running - d = self.master.db.schedulers.classifyChanges( - self.objectid, { change.number : important }) - def fix_timer(_): - if not important and not self._stable_timers[timer_name]: - return - if self._stable_timers[timer_name]: - self._stable_timers[timer_name].cancel() - def fire_timer(): - d = self.stableTimerFired(timer_name) - d.addErrback(log.err, "while firing stable timer") - self._stable_timers[timer_name] = self._reactor.callLater( - self.treeStableTimer, fire_timer) - d.addCallback(fix_timer) - return d - - @defer.inlineCallbacks - def scanExistingClassifiedChanges(self): - # call gotChange for each classified change. This is called at startup - # and is intended to re-start the treeStableTimer for any changes that - # had not yet been built when the scheduler was stopped. - - # NOTE: this may double-call gotChange for changes that arrive just as - # the scheduler starts up. In practice, this doesn't hurt anything. - classifications = \ - yield self.master.db.schedulers.getChangeClassifications( - self.objectid) - - # call gotChange for each change, after first fetching it from the db - for changeid, important in classifications.iteritems(): - chdict = yield self.master.db.changes.getChange(changeid) - - if not chdict: - continue - - change = yield changes.Change.fromChdict(self.master, chdict) - yield self.gotChange(change, important) - - def getTimerNameForChange(self, change): - raise NotImplementedError # see subclasses - - def getChangeClassificationsForTimer(self, objectid, timer_name): - """similar to db.schedulers.getChangeClassifications, but given timer - name""" - raise NotImplementedError # see subclasses - - @util.deferredLocked('_stable_timers_lock') - @defer.inlineCallbacks - def stableTimerFired(self, timer_name): - # if the service has already been stoppd then just bail out - if not self._stable_timers[timer_name]: - return - - # delete this now-fired timer - del self._stable_timers[timer_name] - - classifications = \ - yield self.getChangeClassificationsForTimer(self.objectid, - timer_name) - - # just in case: databases do weird things sometimes! - if not classifications: # pragma: no cover - return - - changeids = sorted(classifications.keys()) - yield self.addBuildsetForChanges(reason='scheduler', - changeids=changeids) - - max_changeid = changeids[-1] # (changeids are sorted) - yield self.master.db.schedulers.flushChangeClassifications( - self.objectid, less_than=max_changeid+1) - - def getPendingBuildTimes(self): - # This isn't locked, since the caller expects and immediate value, - # and in any case, this is only an estimate. - return [timer.getTime() for timer in self._stable_timers.values() if timer and timer.active()] - -class SingleBranchScheduler(BaseBasicScheduler): - def getChangeFilter(self, branch, branches, change_filter, categories): - if branch is NotABranch and not change_filter: - config.error( - "the 'branch' argument to SingleBranchScheduler is " + - "mandatory unless change_filter is provided") - elif branches is not NotABranch: - config.error( - "the 'branches' argument is not allowed for " + - "SingleBranchScheduler") - - - return filter.ChangeFilter.fromSchedulerConstructorArgs( - change_filter=change_filter, branch=branch, - categories=categories) - - def getTimerNameForChange(self, change): - return "only" # this class only uses one timer - - def getChangeClassificationsForTimer(self, objectid, timer_name): - return self.master.db.schedulers.getChangeClassifications( - self.objectid) - - -class Scheduler(SingleBranchScheduler): - "alias for SingleBranchScheduler" - def __init__(self, *args, **kwargs): - log.msg("WARNING: the name 'Scheduler' is deprecated; use " + - "buildbot.schedulers.basic.SingleBranchScheduler instead " + - "(note that this may require you to change your import " + - "statement)") - SingleBranchScheduler.__init__(self, *args, **kwargs) - - -class AnyBranchScheduler(BaseBasicScheduler): - def getChangeFilter(self, branch, branches, change_filter, categories): - assert branch is NotABranch - return filter.ChangeFilter.fromSchedulerConstructorArgs( - change_filter=change_filter, branch=branches, - categories=categories) - - def getTimerNameForChange(self, change): - # Py2.6+: could be a namedtuple - return (change.codebase, change.project, change.repository, change.branch) - - def getChangeClassificationsForTimer(self, objectid, timer_name): - codebase, project, repository, branch = timer_name # set in getTimerNameForChange - return self.master.db.schedulers.getChangeClassifications( - self.objectid, branch=branch, repository=repository, - codebase=codebase, project=project) - -# now at buildbot.schedulers.dependent, but keep the old name alive -Dependent = dependent.Dependent diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/dependent.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/dependent.py deleted file mode 100644 index f28df579..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/dependent.py +++ /dev/null @@ -1,148 +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 twisted.python import log -from buildbot import util, interfaces, config -from buildbot.status.results import SUCCESS, WARNINGS -from buildbot.schedulers import base - -class Dependent(base.BaseScheduler): - - compare_attrs = base.BaseScheduler.compare_attrs + ('upstream_name',) - - def __init__(self, name, upstream, builderNames, properties={}, **kwargs): - base.BaseScheduler.__init__(self, name, builderNames, properties, - **kwargs) - if not interfaces.IScheduler.providedBy(upstream): - config.error( - "upstream must be another Scheduler instance") - self.upstream_name = upstream.name - self._buildset_addition_subscr = None - self._buildset_completion_subscr = None - self._cached_upstream_bsids = None - - # the subscription lock makes sure that we're done inserting a - # subcription into the DB before registering that the buildset is - # complete. - self._subscription_lock = defer.DeferredLock() - - def startService(self): - self._buildset_addition_subscr = \ - self.master.subscribeToBuildsets(self._buildsetAdded) - self._buildset_completion_subscr = \ - self.master.subscribeToBuildsetCompletions(self._buildsetCompleted) - - # check for any buildsets completed before we started - d = self._checkCompletedBuildsets(None, None) - d.addErrback(log.err, 'while checking for completed buildsets in start') - - def stopService(self): - if self._buildset_addition_subscr: - self._buildset_addition_subscr.unsubscribe() - if self._buildset_completion_subscr: - self._buildset_completion_subscr.unsubscribe() - self._cached_upstream_bsids = None - return defer.succeed(None) - - @util.deferredLocked('_subscription_lock') - def _buildsetAdded(self, bsid=None, properties=None, **kwargs): - # check if this was submitetted by our upstream by checking the - # scheduler property - submitter = properties.get('scheduler', (None, None))[0] - if submitter != self.upstream_name: - return - - # record our interest in this buildset - d = self._addUpstreamBuildset(bsid) - d.addErrback(log.err, 'while subscribing to buildset %d' % bsid) - - def _buildsetCompleted(self, bsid, result): - d = self._checkCompletedBuildsets(bsid, result) - d.addErrback(log.err, 'while checking for completed buildsets') - - @util.deferredLocked('_subscription_lock') - @defer.inlineCallbacks - def _checkCompletedBuildsets(self, bsid, result): - subs = yield self._getUpstreamBuildsets() - - sub_bsids = [] - for (sub_bsid, sub_sssetid, sub_complete, sub_results) in subs: - # skip incomplete builds, handling the case where the 'complete' - # column has not been updated yet - if not sub_complete and sub_bsid != bsid: - continue - - # build a dependent build if the status is appropriate - if sub_results in (SUCCESS, WARNINGS): - yield self.addBuildsetForSourceStamp(setid=sub_sssetid, - reason='downstream') - - sub_bsids.append(sub_bsid) - - # and regardless of status, remove the subscriptions - yield self._removeUpstreamBuildsets(sub_bsids) - - @defer.inlineCallbacks - def _updateCachedUpstreamBuilds(self): - if self._cached_upstream_bsids is None: - bsids = yield self.master.db.state.getState(self.objectid, - 'upstream_bsids', []) - self._cached_upstream_bsids = bsids - - @defer.inlineCallbacks - def _getUpstreamBuildsets(self): - # get a list of (bsid, sssid, complete, results) for all - # upstream buildsets - yield self._updateCachedUpstreamBuilds() - - changed = False - rv = [] - for bsid in self._cached_upstream_bsids[:]: - bsdict = yield self.master.db.buildsets.getBuildset(bsid) - if not bsdict: - self._cached_upstream_bsids.remove(bsid) - changed = True - continue - - rv.append((bsid, bsdict['sourcestampsetid'], bsdict['complete'], - bsdict['results'])) - - if changed: - yield self.master.db.state.setState(self.objectid, - 'upstream_bsids', self._cached_upstream_bsids) - - defer.returnValue(rv) - - @defer.inlineCallbacks - def _addUpstreamBuildset(self, bsid): - yield self._updateCachedUpstreamBuilds() - - if bsid not in self._cached_upstream_bsids: - self._cached_upstream_bsids.append(bsid) - - yield self.master.db.state.setState(self.objectid, - 'upstream_bsids', self._cached_upstream_bsids) - - @defer.inlineCallbacks - def _removeUpstreamBuildsets(self, bsids): - yield self._updateCachedUpstreamBuilds() - - old = set(self._cached_upstream_bsids) - self._cached_upstream_bsids = list(old - set(bsids)) - - yield self.master.db.state.setState(self.objectid, - 'upstream_bsids', self._cached_upstream_bsids) - diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/filter.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/filter.py deleted file mode 100644 index c06e2315..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/filter.py +++ /dev/null @@ -1,18 +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 - -# old (pre-0.8.4) location for ChangeFilter -from buildbot.changes.filter import ChangeFilter -_hush_pyflakes = ChangeFilter # keep pyflakes happy diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/forcesched.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/forcesched.py deleted file mode 100644 index 90ff950f..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/forcesched.py +++ /dev/null @@ -1,666 +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 traceback -import re -from twisted.internet import defer -import email.utils as email_utils - -from buildbot.process.properties import Properties -from buildbot.schedulers import base -from buildbot import config - -class ValidationError(ValueError): - pass - -DefaultField = object() # sentinel object to signal default behavior - -class BaseParameter(object): - """ - BaseParameter provides a base implementation for property customization - """ - name = "" - parentName = None - label = "" - type = [] - default = "" - required = False - multiple = False - regex = None - debug=True - hide = False - - @property - def fullName(self): - """A full name, intended to uniquely identify a parameter""" - # join with '_' if both are set - if self.parentName and self.name: - return self.parentName+'_'+self.name - # otherwise just use the one that is set - # (this allows empty name for "anonymous nests") - return self.name or self.parentName - - def setParent(self, parent): - self.parentName = parent.fullName if parent else None - - def __init__(self, name, label=None, regex=None, **kw): - """ - @param name: the name of the field, used during posting values - back to the scheduler. This is not necessarily a UI value, - and there may be restrictions on the characters allowed for - this value. For example, HTML would require this field to - avoid spaces and other punctuation ('-', '.', and '_' allowed) - @type name: unicode - - @param label: (optional) the name of the field, used for UI display. - @type label: unicode or None (to use 'name') - - @param regex: (optional) regex to validate the value with. Not used by - all subclasses - @type regex: unicode or regex - """ - - self.name = name - self.label = name if label is None else label - if regex: - self.regex = re.compile(regex) - # all other properties are generically passed via **kw - self.__dict__.update(kw) - - def getFromKwargs(self, kwargs): - """Simple customization point for child classes that do not need the other - parameters supplied to updateFromKwargs. Return the value for the property - named 'self.name'. - - The default implementation converts from a list of items, validates using - the optional regex field and calls 'parse_from_args' for the final conversion. - """ - args = kwargs.get(self.fullName, []) - if len(args) == 0: - if self.required: - raise ValidationError("'%s' needs to be specified" % (self.label)) - if self.multiple: - args = self.default - else: - args = [self.default] - - if self.regex: - for arg in args: - if not self.regex.match(arg): - raise ValidationError("%s:'%s' does not match pattern '%s'" - % (self.label, arg, self.regex.pattern)) - - try: - arg = self.parse_from_args(args) - except Exception, e: - # an exception will just display an alert in the web UI - # also log the exception - if self.debug: - traceback.print_exc() - raise e - if arg is None: - raise ValidationError("need %s: no default provided by config" - % (self.fullName,)) - return arg - - def updateFromKwargs(self, properties, kwargs, **unused): - """Primary entry point to turn 'kwargs' into 'properties'""" - properties[self.name] = self.getFromKwargs(kwargs) - - def parse_from_args(self, l): - """Secondary customization point, called from getFromKwargs to turn - a validated value into a single property value""" - if self.multiple: - return map(self.parse_from_arg, l) - else: - return self.parse_from_arg(l[0]) - - def parse_from_arg(self, s): - return s - - -class FixedParameter(BaseParameter): - """A fixed parameter that cannot be modified by the user.""" - type = ["fixed"] - hide = True - default = "" - - def parse_from_args(self, l): - return self.default - - -class StringParameter(BaseParameter): - """A simple string parameter""" - type = ["text"] - size = 10 - - def parse_from_arg(self, s): - return s - - -class TextParameter(StringParameter): - """A generic string parameter that may span multiple lines""" - type = ["textarea"] - cols = 80 - rows = 20 - - def value_to_text(self, value): - return str(value) - - -class IntParameter(StringParameter): - """An integer parameter""" - type = ["int"] - - parse_from_arg = int # will throw an exception if parse fail - - -class BooleanParameter(BaseParameter): - """A boolean parameter""" - type = ["bool"] - - def getFromKwargs(self, kwargs): - return kwargs.get(self.fullName, None) == [True] - - -class UserNameParameter(StringParameter): - """A username parameter to supply the 'owner' of a build""" - type = ["text"] - default = "" - size = 30 - need_email = True - - def __init__(self, name="username", label="Your name:", **kw): - BaseParameter.__init__(self, name, label, **kw) - - def parse_from_arg(self, s): - if not s and not self.required: - return s - if self.need_email: - e = email_utils.parseaddr(s) - if e[0]=='' or e[1] == '': - raise ValidationError("%s: please fill in email address in the " - "form 'User <email@email.com>'" % (self.name,)) - return s - - -class ChoiceStringParameter(BaseParameter): - """A list of strings, allowing the selection of one of the predefined values. - The 'strict' parameter controls whether values outside the predefined list - of choices are allowed""" - type = ["list"] - choices = [] - strict = True - - def parse_from_arg(self, s): - if self.strict and not s in self.choices: - raise ValidationError("'%s' does not belongs to list of available choices '%s'"%(s, self.choices)) - return s - - def getChoices(self, master, scheduler, buildername): - return self.choices - -class InheritBuildParameter(ChoiceStringParameter): - """A parameter that takes its values from another build""" - type = ChoiceStringParameter.type + ["inherit"] - name = "inherit" - compatible_builds = None - - def getChoices(self, master, scheduler, buildername): - return self.compatible_builds(master.status, buildername) - - def getFromKwargs(self, kwargs): - raise ValidationError("InheritBuildParameter can only be used by properties") - - def updateFromKwargs(self, master, properties, changes, kwargs, **unused): - arg = kwargs.get(self.fullName, [""])[0] - splitted_arg = arg.split(" ")[0].split("/") - if len(splitted_arg) != 2: - raise ValidationError("bad build: %s"%(arg)) - builder, num = splitted_arg - builder_status = master.status.getBuilder(builder) - if not builder_status: - raise ValidationError("unknown builder: %s in %s"%(builder, arg)) - b = builder_status.getBuild(int(num)) - if not b: - raise ValidationError("unknown build: %d in %s"%(num, arg)) - props = {self.name:(arg.split(" ")[0])} - for name, value, source in b.getProperties().asList(): - if source == "Force Build Form": - if name == "owner": - name = "orig_owner" - props[name] = value - properties.update(props) - changes.extend(b.changes) - - -class BuildslaveChoiceParameter(ChoiceStringParameter): - """A parameter that lets the buildslave name be explicitly chosen. - - This parameter works in conjunction with 'buildbot.process.builder.enforceChosenSlave', - which should be added as the 'canStartBuild' parameter to the Builder. - - The "anySentinel" parameter represents the sentinel value to specify that - there is no buildslave preference. - """ - anySentinel = '-any-' - label = 'Build slave' - required = False - strict = False - - def __init__(self, name='slavename', **kwargs): - ChoiceStringParameter.__init__(self, name, **kwargs) - - def updateFromKwargs(self, kwargs, **unused): - slavename = self.getFromKwargs(kwargs) - if slavename==self.anySentinel: - # no preference, so dont set a parameter at all - return - ChoiceStringParameter.updateFromKwargs(self, kwargs=kwargs, **unused) - - def getChoices(self, master, scheduler, buildername): - if buildername is None: - # this is the "Force All Builds" page - slavenames = master.status.getSlaveNames() - else: - builderStatus = master.status.getBuilder(buildername) - slavenames = [slave.getName() for slave in builderStatus.getSlaves()] - slavenames.sort() - slavenames.insert(0, self.anySentinel) - return slavenames - - -class NestedParameter(BaseParameter): - """A 'parent' parameter for a set of related parameters. This provices a - logical grouping for the child parameters. - - Typically, the 'fullName' of the child parameters mix in the parent's - 'fullName'. This allows for a field to appear multiple times in a form - (for example, two codebases each have a 'branch' field). - - If the 'name' of the parent is the empty string, then the parent's name - does not mix in with the child 'fullName'. This is useful when a field - will not appear multiple time in a scheduler but the logical grouping is - helpful. - - The result of a NestedParameter is typically a dictionary, with the key/value - being the name/value of the children. - """ - type = ['nested'] - fields = None - - def __init__(self, name, fields, **kwargs): - BaseParameter.__init__(self, fields=fields, name=name, **kwargs) - - # fix up the child nodes with the parent (use None for now): - self.setParent(None) - - def setParent(self, parent): - BaseParameter.setParent(self, parent) - for field in self.fields: - field.setParent(self) - - def collectChildProperties(self, kwargs, properties, **kw): - """Collapse the child values into a dictionary. This is intended to be - called by child classes to fix up the fullName->name conversions.""" - - childProperties = {} - for field in self.fields: - field.updateFromKwargs(kwargs=kwargs, - properties=childProperties, - **kw) - - kwargs[self.fullName] = childProperties - - def updateFromKwargs(self, kwargs, properties, **kw): - """By default, the child values will be collapsed into a dictionary. If - the parent is anonymous, this dictionary is the top-level properties.""" - self.collectChildProperties(kwargs=kwargs, properties=properties, **kw) - - # default behavior is to set a property - # -- use setdefault+update in order to collapse 'anonymous' nested - # parameters correctly - if self.name: - d = properties.setdefault(self.name, {}) - else: - # if there's no name, collapse this nest all the way - d = properties - d.update(kwargs[self.fullName]) - -class AnyPropertyParameter(NestedParameter): - """A generic property parameter, where both the name and value of the property - must be given.""" - type = NestedParameter.type + ["any"] - - def __init__(self, name, **kw): - fields = [ - StringParameter(name='name', label="Name:"), - StringParameter(name='value', label="Value:"), - ] - NestedParameter.__init__(self, name, label='', fields=fields, **kw) - - def getFromKwargs(self, kwargs): - raise ValidationError("AnyPropertyParameter can only be used by properties") - - def updateFromKwargs(self, master, properties, kwargs, **kw): - self.collectChildProperties(master=master, - properties=properties, - kwargs=kwargs, - **kw) - - pname = kwargs[self.fullName].get("name", "") - pvalue = kwargs[self.fullName].get("value", "") - if not pname: - return - - validation = master.config.validation - pname_validate = validation['property_name'] - pval_validate = validation['property_value'] - - if not pname_validate.match(pname) \ - or not pval_validate.match(pvalue): - raise ValidationError("bad property name='%s', value='%s'" % (pname, pvalue)) - properties[pname] = pvalue - - -class CodebaseParameter(NestedParameter): - """A parameter whose result is a codebase specification instead of a property""" - type = NestedParameter.type + ["codebase"] - codebase = '' - - def __init__(self, - codebase, - name=None, - label=None, - - branch=DefaultField, - revision=DefaultField, - repository=DefaultField, - project=DefaultField, - - **kwargs): - """ - A set of properties that will be used to generate a codebase dictionary. - - The branch/revision/repository/project should each be a parameter that - will map to the corresponding value in the sourcestamp. Use None to disable - the field. - - @param codebase: name of the codebase; used as key for the sourcestamp set - @type codebase: unicode - - @param name: optional override for the name-currying for the subfields - @type codebase: unicode - - @param label: optional override for the label for this set of parameters - @type codebase: unicode - """ - - name = name or codebase - if label is None and codebase: - label = "Codebase: " + codebase - - if branch is DefaultField: - branch = StringParameter(name='branch', label="Branch:") - if revision is DefaultField: - revision = StringParameter(name='revision', label="Revision:") - if repository is DefaultField: - repository = StringParameter(name='repository', label="Repository:") - if project is DefaultField: - project = StringParameter(name='project', label="Project:") - - fields = filter(None, [branch, revision, repository, project]) - - NestedParameter.__init__(self, name=name, label=label, - codebase=codebase, - fields=fields, **kwargs) - - def createSourcestamp(self, properties, kwargs): - # default, just return the things we put together - return kwargs.get(self.fullName, {}) - - def updateFromKwargs(self, sourcestamps, kwargs, properties, **kw): - self.collectChildProperties(sourcestamps=sourcestamps, - properties=properties, - kwargs=kwargs, - **kw) - - # convert the "property" to a sourcestamp - ss = self.createSourcestamp(properties, kwargs) - if ss is not None: - sourcestamps[self.codebase] = ss - - -class ForceScheduler(base.BaseScheduler): - """ - ForceScheduler implements the backend for a UI to allow customization of - builds. For example, a web form be populated to trigger a build. - """ - compare_attrs = ( 'name', 'builderNames', - 'reason', 'username', - 'forcedProperties' ) - - def __init__(self, name, builderNames, - username=UserNameParameter(), - reason=StringParameter(name="reason", default="force build", length=20), - - codebases=None, - - properties=[ - NestedParameter(name='', fields=[ - AnyPropertyParameter("property1"), - AnyPropertyParameter("property2"), - AnyPropertyParameter("property3"), - AnyPropertyParameter("property4"), - ]) - ], - - # deprecated; use 'codebase' instead - branch=None, - revision=None, - repository=None, - project=None - ): - """ - Initialize a ForceScheduler. - - The UI will provide a set of fields to the user; these fields are - driven by a corresponding child class of BaseParameter. - - Use NestedParameter to provide logical groupings for parameters. - - The branch/revision/repository/project fields are deprecated and - provided only for backwards compatibility. Using a Codebase(name='') - will give the equivalent behavior. - - @param name: name of this scheduler (used as a key for state) - @type name: unicode - - @param builderNames: list of builders this scheduler may start - @type builderNames: list of unicode - - @param username: the "owner" for a build (may not be shown depending - on the Auth configuration for the master) - @type username: BaseParameter - - @param reason: the "reason" for a build - @type reason: BaseParameter - - @param codebases: the codebases for a build - @type codebases: list of string's or CodebaseParameter's; - None will generate a default, but [] will - remove all codebases - - @param properties: extra properties to configure the build - @type properties: list of BaseParameter's - """ - - if not self.checkIfType(name, str): - config.error("ForceScheduler name must be a unicode string: %r" % - name) - - if not name: - config.error("ForceScheduler name must not be empty: %r " % - name) - - if not self.checkIfListOfType(builderNames, str): - config.error("ForceScheduler builderNames must be a list of strings: %r" % - builderNames) - - if self.checkIfType(reason, BaseParameter): - self.reason = reason - else: - config.error("ForceScheduler reason must be a StringParameter: %r" % - reason) - - if not self.checkIfListOfType(properties, BaseParameter): - config.error("ForceScheduler properties must be a list of BaseParameters: %r" % - properties) - - if self.checkIfType(username, BaseParameter): - self.username = username - else: - config.error("ForceScheduler username must be a StringParameter: %r" % - username) - - self.forcedProperties = [] - - if any((branch, revision, repository, project)): - if codebases: - config.error("ForceScheduler: Must either specify 'codebases' or the 'branch/revision/repository/project' parameters: %r " % (codebases,)) - - codebases = [ - CodebaseParameter(codebase='', - branch=branch or DefaultField, - revision=revision or DefaultField, - repository=repository or DefaultField, - project=project or DefaultField, - ) - ] - - # Use the default single codebase form if none are provided - if codebases is None: - codebases =[CodebaseParameter(codebase='')] - elif not codebases: - config.error("ForceScheduler: 'codebases' cannot be empty; use CodebaseParameter(codebase='', hide=True) if needed: %r " % (codebases,)) - - codebase_dict = {} - for codebase in codebases: - if isinstance(codebase, basestring): - codebase = CodebaseParameter(codebase=codebase) - elif not isinstance(codebase, CodebaseParameter): - config.error("ForceScheduler: 'codebases' must be a list of strings or CodebaseParameter objects: %r" % (codebases,)) - - self.forcedProperties.append(codebase) - codebase_dict[codebase.codebase] = dict(branch='',repository='',revision='') - - base.BaseScheduler.__init__(self, - name=name, - builderNames=builderNames, - properties={}, - codebases=codebase_dict) - - if properties: - self.forcedProperties.extend(properties) - - # this is used to simplify the template - self.all_fields = [ NestedParameter(name='', fields=[username, reason]) ] - self.all_fields.extend(self.forcedProperties) - - def checkIfType(self, obj, chkType): - return isinstance(obj, chkType) - - def checkIfListOfType(self, obj, chkType): - isListOfType = True - - if self.checkIfType(obj, list): - for item in obj: - if not self.checkIfType(item, chkType): - isListOfType = False - break - else: - isListOfType = False - - return isListOfType - - def startService(self): - pass - - def stopService(self): - pass - - @defer.inlineCallbacks - def gatherPropertiesAndChanges(self, **kwargs): - properties = {} - changeids = [] - sourcestamps = {} - - for param in self.forcedProperties: - yield defer.maybeDeferred(param.updateFromKwargs, - master=self.master, - properties=properties, - changes=changeids, - sourcestamps=sourcestamps, - kwargs=kwargs) - - changeids = map(lambda a: type(a)==int and a or a.number, changeids) - - real_properties = Properties() - for pname, pvalue in properties.items(): - real_properties.setProperty(pname, pvalue, "Force Build Form") - - defer.returnValue((real_properties, changeids, sourcestamps)) - - @defer.inlineCallbacks - def force(self, owner, builderNames=None, **kwargs): - """ - We check the parameters, and launch the build, if everything is correct - """ - if builderNames is None: - builderNames = self.builderNames - else: - builderNames = set(builderNames).intersection(self.builderNames) - - if not builderNames: - defer.returnValue(None) - return - - # Currently the validation code expects all kwargs to be lists - # I don't want to refactor that now so much sure we comply... - kwargs = dict((k, [v]) if not isinstance(v, list) else (k,v) for k,v in kwargs.items()) - - # probably need to clean that out later as the IProperty is already a - # validation mechanism - - reason = self.reason.getFromKwargs(kwargs) - if owner is None: - owner = self.username.getFromKwargs(kwargs) - - properties, changeids, sourcestamps = yield self.gatherPropertiesAndChanges(**kwargs) - - properties.setProperty("reason", reason, "Force Build Form") - properties.setProperty("owner", owner, "Force Build Form") - - r = ("A build was forced by '%s': %s" % (owner, reason)) - - # everything is validated, we can create our source stamp, and buildrequest - res = yield self.addBuildsetForSourceStampSetDetails( - reason = r, - sourcestamps = sourcestamps, - properties = properties, - builderNames = builderNames, - ) - - defer.returnValue(res) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/manager.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/manager.py deleted file mode 100644 index d4c8b795..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/manager.py +++ /dev/null @@ -1,95 +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 twisted.application import service -from twisted.python import log, reflect -from buildbot.process import metrics -from buildbot import config, util - -class SchedulerManager(config.ReconfigurableServiceMixin, - service.MultiService): - def __init__(self, master): - service.MultiService.__init__(self) - self.setName('scheduler_manager') - self.master = master - - @defer.inlineCallbacks - def reconfigService(self, new_config): - timer = metrics.Timer("SchedulerManager.reconfigService") - timer.start() - - old_by_name = dict((sch.name, sch) for sch in self) - old_set = set(old_by_name.iterkeys()) - new_by_name = new_config.schedulers - new_set = set(new_by_name.iterkeys()) - - removed_names, added_names = util.diffSets(old_set, new_set) - - # find any schedulers that don't know how to reconfig, and, if they - # have changed, add them to both removed and added, so that we - # run the new version. While we're at it, find any schedulers whose - # fully qualified class name has changed, and consider those a removal - # and re-add as well. - for n in old_set & new_set: - old = old_by_name[n] - new = new_by_name[n] - # detect changed class name - if reflect.qual(old.__class__) != reflect.qual(new.__class__): - removed_names.add(n) - added_names.add(n) - - # compare using ComparableMixin if they don't support reconfig - elif not hasattr(old, 'reconfigService'): - if old != new: - removed_names.add(n) - added_names.add(n) - - # removals first - - for sch_name in removed_names: - log.msg("removing scheduler '%s'" % (sch_name,)) - sch = old_by_name[sch_name] - yield defer.maybeDeferred(lambda : - sch.disownServiceParent()) - sch.master = None - - # .. then additions - - for sch_name in added_names: - log.msg("adding scheduler '%s'" % (sch_name,)) - sch = new_by_name[sch_name] - - # get the scheduler's objectid - class_name = '%s.%s' % (sch.__class__.__module__, - sch.__class__.__name__) - objectid = yield self.master.db.state.getObjectId( - sch.name, class_name) - - # set up the scheduler - sch.objectid = objectid - sch.master = self.master - - # *then* attacah and start it - sch.setServiceParent(self) - - metrics.MetricCountEvent.log("num_schedulers", len(list(self)), - absolute=True) - - # reconfig any newly-added schedulers, as well as existing - yield config.ReconfigurableServiceMixin.reconfigService(self, - new_config) - - timer.stop() diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/timed.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/timed.py deleted file mode 100644 index e735ca93..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/timed.py +++ /dev/null @@ -1,410 +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 util -from buildbot.interfaces import ITriggerableScheduler -from buildbot.process import buildstep, properties -from buildbot.schedulers import base -from twisted.internet import defer, reactor -from twisted.python import log -from buildbot import config -from buildbot.changes import filter -# Import croniter if available. -# This is only required for Nightly schedulers, -# so fail gracefully if it isn't present. -try: - from buildbot.util import croniter -except ImportError: - # Pyflakes doesn't like a redefinition here - # Instead, we check if croniter is defined when we need it - pass - -class Timed(base.BaseScheduler): - """ - Parent class for timed schedulers. This takes care of the (surprisingly - subtle) mechanics of ensuring that each timed actuation runs to completion - before the service stops. - """ - - compare_attrs = base.BaseScheduler.compare_attrs - - def __init__(self, name, builderNames, properties={}, **kwargs): - base.BaseScheduler.__init__(self, name, builderNames, properties, - **kwargs) - - # tracking for when to start the next build - self.lastActuated = None - - # A lock to make sure that each actuation occurs without interruption. - # This lock governs actuateAt, actuateAtTimer, and actuateOk - self.actuationLock = defer.DeferredLock() - self.actuateOk = False - self.actuateAt = None - self.actuateAtTimer = None - - self._reactor = reactor # patched by tests - - def startService(self): - base.BaseScheduler.startService(self) - - # no need to lock this; nothing else can run before the service is started - self.actuateOk = True - - # get the scheduler's last_build time (note: only done at startup) - d = self.getState('last_build', None) - def set_last(lastActuated): - self.lastActuated = lastActuated - d.addCallback(set_last) - - # schedule the next build - d.addCallback(lambda _ : self.scheduleNextBuild()) - - # give subclasses a chance to start up - d.addCallback(lambda _ : self.startTimedSchedulerService()) - - # startService does not return a Deferred, so handle errors with a traceback - d.addErrback(log.err, "while initializing %s '%s'" % - (self.__class__.__name__, self.name)) - - def startTimedSchedulerService(self): - """Hook for subclasses to participate in the L{startService} process; - can return a Deferred""" - - def stopService(self): - # shut down any pending actuation, and ensure that we wait for any - # current actuation to complete by acquiring the lock. This ensures - # that no build will be scheduled after stopService is complete. - def stop_actuating(): - self.actuateOk = False - self.actuateAt = None - if self.actuateAtTimer: - self.actuateAtTimer.cancel() - self.actuateAtTimer = None - d = self.actuationLock.run(stop_actuating) - - # and chain to the parent class - d.addCallback(lambda _ : base.BaseScheduler.stopService(self)) - return d - - ## Scheduler methods - - def getPendingBuildTimes(self): - # take the latest-calculated value of actuateAt as a reasonable - # estimate - return [ self.actuateAt ] - - ## Timed methods - - def startBuild(self): - """The time has come to start a new build. Returns a Deferred. - Override in subclasses.""" - raise NotImplementedError - - def getNextBuildTime(self, lastActuation): - """ - Called by to calculate the next time to actuate a BuildSet. Override - in subclasses. To trigger a fresh call to this method, use - L{rescheduleNextBuild}. - - @param lastActuation: the time of the last actuation, or None for never - - @returns: a Deferred firing with the next time a build should occur (in - the future), or None for never. - """ - raise NotImplementedError - - def scheduleNextBuild(self): - """ - Schedule the next build, re-invoking L{getNextBuildTime}. This can be - called at any time, and it will avoid contention with builds being - started concurrently. - - @returns: Deferred - """ - return self.actuationLock.run(self._scheduleNextBuild_locked) - - ## utilities - - def now(self): - "Similar to util.now, but patchable by tests" - return util.now(self._reactor) - - def _scheduleNextBuild_locked(self): - # clear out the existing timer - if self.actuateAtTimer: - self.actuateAtTimer.cancel() - self.actuateAtTimer = None - - # calculate the new time - d = self.getNextBuildTime(self.lastActuated) - - # set up the new timer - def set_timer(actuateAt): - now = self.now() - self.actuateAt = max(actuateAt, now) - if actuateAt is not None: - untilNext = self.actuateAt - now - if untilNext == 0: - log.msg(("%s: missed scheduled build time, so building " - "immediately") % self.name) - self.actuateAtTimer = self._reactor.callLater(untilNext, - self._actuate) - d.addCallback(set_timer) - - return d - - def _actuate(self): - # called from the timer when it's time to start a build - self.actuateAtTimer = None - self.lastActuated = self.actuateAt - - @defer.inlineCallbacks - def set_state_and_start(): - # bail out if we shouldn't be actuating anymore - if not self.actuateOk: - return - - # mark the last build time - self.actuateAt = None - yield self.setState('last_build', self.lastActuated) - - # start the build - yield self.startBuild() - - # schedule the next build (noting the lock is already held) - yield self._scheduleNextBuild_locked() - d = self.actuationLock.run(set_state_and_start) - - # this function can't return a deferred, so handle any failures via - # log.err - d.addErrback(log.err, 'while actuating') - - -class Periodic(Timed): - compare_attrs = Timed.compare_attrs + ('periodicBuildTimer', 'branch',) - - def __init__(self, name, builderNames, periodicBuildTimer, - branch=None, properties={}, onlyImportant=False): - Timed.__init__(self, name=name, builderNames=builderNames, - properties=properties) - if periodicBuildTimer <= 0: - config.error( - "periodicBuildTimer must be positive") - self.periodicBuildTimer = periodicBuildTimer - self.branch = branch - self.reason = "The Periodic scheduler named '%s' triggered this build" % self.name - - def getNextBuildTime(self, lastActuated): - if lastActuated is None: - return defer.succeed(self.now()) # meaning "ASAP" - else: - return defer.succeed(lastActuated + self.periodicBuildTimer) - - def startBuild(self): - return self.addBuildsetForLatest(reason=self.reason, branch=self.branch) - -class NightlyBase(Timed): - compare_attrs = (Timed.compare_attrs - + ('minute', 'hour', 'dayOfMonth', 'month', 'dayOfWeek')) - - def __init__(self, name, builderNames, minute=0, hour='*', - dayOfMonth='*', month='*', dayOfWeek='*', - properties={}, codebases=base.BaseScheduler.DefaultCodebases): - Timed.__init__(self, name=name, builderNames=builderNames, - properties=properties, codebases=codebases) - - self.minute = minute - self.hour = hour - self.dayOfMonth = dayOfMonth - self.month = month - self.dayOfWeek = dayOfWeek - - try: - croniter - except NameError: - config.error("python-dateutil required for scheduler %s '%s'." % - (self.__class__.__name__, self.name)) - - def _timeToCron(self, time, isDayOfWeek = False): - if isinstance(time, int): - if isDayOfWeek: - time = (time + 1) % 7 # Convert from Mon = 0 format to Sun = 0 format for use in croniter - return time - - if isinstance(time, basestring): - return time - - if isDayOfWeek: - time = [ (t + 1) % 7 for t in time ] # Conversion for croniter (see above) - - return ','.join([ str(s) for s in time ]) # Convert the list to a string - - def getNextBuildTime(self, lastActuated): - dateTime = lastActuated or self.now() - sched = '%s %s %s %s %s' % (self._timeToCron(self.minute), - self._timeToCron(self.hour), - self._timeToCron(self.dayOfMonth), - self._timeToCron(self.month), - self._timeToCron(self.dayOfWeek, True)) - cron = croniter.croniter(sched, dateTime) - nextdate = cron.get_next(float) - return defer.succeed(nextdate) - -class Nightly(NightlyBase): - compare_attrs = (NightlyBase.compare_attrs - + ('branch', 'onlyIfChanged', 'fileIsImportant', - 'change_filter', 'onlyImportant',)) - - class NoBranch: pass - def __init__(self, name, builderNames, minute=0, hour='*', - dayOfMonth='*', month='*', dayOfWeek='*', - branch=NoBranch, fileIsImportant=None, onlyIfChanged=False, - properties={}, change_filter=None, onlyImportant=False, - codebases = base.BaseScheduler.DefaultCodebases): - NightlyBase.__init__(self, name=name, builderNames=builderNames, - minute=minute, hour=hour, dayOfWeek=dayOfWeek, dayOfMonth=dayOfMonth, - properties=properties, codebases=codebases) - - # If True, only important changes will be added to the buildset. - self.onlyImportant = onlyImportant - - if fileIsImportant and not callable(fileIsImportant): - config.error( - "fileIsImportant must be a callable") - - if branch is Nightly.NoBranch: - config.error( - "Nightly parameter 'branch' is required") - - self.branch = branch - self.onlyIfChanged = onlyIfChanged - self.fileIsImportant = fileIsImportant - self.change_filter = filter.ChangeFilter.fromSchedulerConstructorArgs( - change_filter=change_filter) - self.reason = "The Nightly scheduler named '%s' triggered this build" % self.name - - def startTimedSchedulerService(self): - if self.onlyIfChanged: - return self.startConsumingChanges(fileIsImportant=self.fileIsImportant, - change_filter=self.change_filter, - onlyImportant=self.onlyImportant) - else: - return self.master.db.schedulers.flushChangeClassifications(self.objectid) - - def gotChange(self, change, important): - # both important and unimportant changes on our branch are recorded, as - # we will include all such changes in any buildsets we start. Note - # that we must check the branch here because it is not included in the - # change filter. - if change.branch != self.branch: - return defer.succeed(None) # don't care about this change - return self.master.db.schedulers.classifyChanges( - self.objectid, { change.number : important }) - - @defer.inlineCallbacks - def startBuild(self): - scheds = self.master.db.schedulers - # if onlyIfChanged is True, then we will skip this build if no - # important changes have occurred since the last invocation - if self.onlyIfChanged: - classifications = \ - yield scheds.getChangeClassifications(self.objectid) - - # see if we have any important changes - for imp in classifications.itervalues(): - if imp: - break - else: - log.msg(("Nightly Scheduler <%s>: skipping build " + - "- No important changes on configured branch") % self.name) - return - - changeids = sorted(classifications.keys()) - yield self.addBuildsetForChanges(reason=self.reason, - changeids=changeids) - - max_changeid = changeids[-1] # (changeids are sorted) - yield scheds.flushChangeClassifications(self.objectid, - less_than=max_changeid+1) - else: - # start a build of the latest revision, whatever that is - yield self.addBuildsetForLatest(reason=self.reason, - branch=self.branch) - -class NightlyTriggerable(NightlyBase): - implements(ITriggerableScheduler) - def __init__(self, name, builderNames, minute=0, hour='*', - dayOfMonth='*', month='*', dayOfWeek='*', - properties={}, codebases=base.BaseScheduler.DefaultCodebases): - NightlyBase.__init__(self, name=name, builderNames=builderNames, minute=minute, hour=hour, - dayOfWeek=dayOfWeek, dayOfMonth=dayOfMonth, properties=properties, codebases=codebases) - - self._lastTrigger = None - self.reason = "The NightlyTriggerable scheduler named '%s' triggered this build" % self.name - - def startService(self): - NightlyBase.startService(self) - - # get the scheduler's lastTrigger time (note: only done at startup) - d = self.getState('lastTrigger', None) - def setLast(lastTrigger): - try: - if lastTrigger: - assert isinstance(lastTrigger[0], dict) - self._lastTrigger = (lastTrigger[0], properties.Properties.fromDict(lastTrigger[1])) - except: - # If the lastTrigger isn't of the right format, ignore it - log.msg("NightlyTriggerable Scheduler <%s>: bad lastTrigger: %r" % (self.name, lastTrigger)) - d.addCallback(setLast) - - def trigger(self, sourcestamps, set_props=None): - """Trigger this scheduler with the given sourcestamp ID. Returns a - deferred that will fire when the buildset is finished.""" - self._lastTrigger = (sourcestamps, set_props) - - # record the trigger in the db - if set_props: - propsDict = set_props.asDict() - else: - propsDict = {} - d = self.setState('lastTrigger', - (sourcestamps, propsDict)) - - ## Trigger expects a callback with the success of the triggered - ## build, if waitForFinish is True. - ## Just return SUCCESS, to indicate that the trigger was succesful, - ## don't want for the nightly to run. - return d.addCallback(lambda _: buildstep.SUCCESS) - - @defer.inlineCallbacks - def startBuild(self): - if self._lastTrigger is None: - defer.returnValue(None) - - (sourcestamps, set_props) = self._lastTrigger - self._lastTrigger = None - yield self.setState('lastTrigger', None) - - # properties for this buildset are composed of our own properties, - # potentially overridden by anything from the triggering build - props = properties.Properties() - props.updateFromProperties(self.properties) - if set_props: - props.updateFromProperties(set_props) - - yield self.addBuildsetForSourceStampSetDetails(reason=self.reason, sourcestamps=sourcestamps, - properties=props) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/triggerable.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/triggerable.py deleted file mode 100644 index a50e120e..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/triggerable.py +++ /dev/null @@ -1,94 +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 failure -from twisted.internet import defer -from buildbot.interfaces import ITriggerableScheduler -from buildbot.schedulers import base -from buildbot.process.properties import Properties - -class Triggerable(base.BaseScheduler): - implements(ITriggerableScheduler) - - compare_attrs = base.BaseScheduler.compare_attrs - - def __init__(self, name, builderNames, properties={}, **kwargs): - base.BaseScheduler.__init__(self, name, builderNames, properties, - **kwargs) - self._waiters = {} - self._bsc_subscription = None - self.reason = "Triggerable(%s)" % name - - def trigger(self, sourcestamps = None, set_props=None): - """Trigger this scheduler with the optional given list of sourcestamps - Returns a deferred that will fire when the buildset is finished.""" - # properties for this buildset are composed of our own properties, - # potentially overridden by anything from the triggering build - props = Properties() - props.updateFromProperties(self.properties) - if set_props: - props.updateFromProperties(set_props) - - # note that this does not use the buildset subscriptions mechanism, as - # the duration of interest to the caller is bounded by the lifetime of - # this process. - d = self.addBuildsetForSourceStampSetDetails(self.reason, - sourcestamps, props) - def setup_waiter((bsid,brids)): - d = defer.Deferred() - self._waiters[bsid] = (d, brids) - self._updateWaiters() - return d - d.addCallback(setup_waiter) - return d - - def stopService(self): - # cancel any outstanding subscription - if self._bsc_subscription: - self._bsc_subscription.unsubscribe() - self._bsc_subscription = None - - # and errback any outstanding deferreds - if self._waiters: - msg = 'Triggerable scheduler stopped before build was complete' - for d, brids in self._waiters.values(): - d.errback(failure.Failure(RuntimeError(msg))) - self._waiters = {} - - return base.BaseScheduler.stopService(self) - - - def _updateWaiters(self): - if self._waiters and not self._bsc_subscription: - self._bsc_subscription = \ - self.master.subscribeToBuildsetCompletions( - self._buildsetComplete) - elif not self._waiters and self._bsc_subscription: - self._bsc_subscription.unsubscribe() - self._bsc_subscription = None - - def _buildsetComplete(self, bsid, result): - if bsid not in self._waiters: - return - - # pop this bsid from the waiters list, and potentially unsubscribe - # from completion notifications - d, brids = self._waiters.pop(bsid) - self._updateWaiters() - - # fire the callback to indicate that the triggered build is complete - d.callback((result, brids)) diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/trysched.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/trysched.py deleted file mode 100644 index 79179ba8..00000000 --- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/schedulers/trysched.py +++ /dev/null @@ -1,304 +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 twisted.internet import defer -from twisted.python import log -from twisted.protocols import basic - -from buildbot import pbutil -from buildbot.util.maildir import MaildirService -from buildbot.util import json -from buildbot.util import netstrings -from buildbot.process.properties import Properties -from buildbot.schedulers import base -from buildbot.status.buildset import BuildSetStatus - - -class TryBase(base.BaseScheduler): - - def filterBuilderList(self, builderNames): - """ - Make sure that C{builderNames} is a subset of the configured - C{self.builderNames}, returning an empty list if not. If - C{builderNames} is empty, use C{self.builderNames}. - - @returns: list of builder names to build on - """ - - # self.builderNames is the configured list of builders - # available for try. If the user supplies a list of builders, - # it must be restricted to the configured list. If not, build - # on all of the configured builders. - if builderNames: - for b in builderNames: - if not b in self.builderNames: - log.msg("%s got with builder %s" % (self, b)) - log.msg(" but that wasn't in our list: %s" - % (self.builderNames,)) - return [] - else: - builderNames = self.builderNames - return builderNames - - -class BadJobfile(Exception): - pass - - -class JobdirService(MaildirService): - # NOTE: tightly coupled with Try_Jobdir, below - - def messageReceived(self, filename): - f = self.moveToCurDir(filename) - return self.parent.handleJobFile(filename, f) - - -class Try_Jobdir(TryBase): - - compare_attrs = TryBase.compare_attrs + ('jobdir',) - - def __init__(self, name, builderNames, jobdir, - properties={}): - TryBase.__init__(self, name=name, builderNames=builderNames, - properties=properties) - self.jobdir = jobdir - self.watcher = JobdirService() - self.watcher.setServiceParent(self) - - def startService(self): - # set the watcher's basedir now that we have a master - jobdir = os.path.join(self.master.basedir, self.jobdir) - self.watcher.setBasedir(jobdir) - for subdir in "cur new tmp".split(): - if not os.path.exists(os.path.join(jobdir, subdir)): - os.mkdir(os.path.join(jobdir, subdir)) - TryBase.startService(self) - - def parseJob(self, f): - # jobfiles are serialized build requests. Each is a list of - # serialized netstrings, in the following order: - # format version number: - # "1" the original - # "2" introduces project and repository - # "3" introduces who - # "4" introduces comment - # "5" introduces properties and JSON serialization of values after - # version - # jobid: arbitrary string, used to find the buildSet later - # branch: branch name, "" for default-branch - # baserev: revision, "" for HEAD - # patch_level: usually "1" - # patch_body: patch to be applied for build - # repository - # project - # who: user requesting build - # comment: comment from user about diff and/or build - # builderNames: list of builder names - # properties: dict of build properties - p = netstrings.NetstringParser() - f.seek(0,2) - if f.tell() > basic.NetstringReceiver.MAX_LENGTH: - raise BadJobfile("The patch size is greater that NetStringReceiver.MAX_LENGTH. Please Set this higher in the master.cfg") - f.seek(0,0) - try: - p.feed(f.read()) - except basic.NetstringParseError: - raise BadJobfile("unable to parse netstrings") - if not p.strings: - raise BadJobfile("could not find any complete netstrings") - ver = p.strings.pop(0) - - v1_keys = ['jobid', 'branch', 'baserev', 'patch_level', 'patch_body'] - v2_keys = v1_keys + ['repository', 'project'] - v3_keys = v2_keys + ['who'] - v4_keys = v3_keys + ['comment'] - keys = [v1_keys, v2_keys, v3_keys, v4_keys] - # v5 introduces properties and uses JSON serialization - - parsed_job = {} - - def extract_netstrings(p, keys): - for i, key in enumerate(keys): - parsed_job[key] = p.strings[i] - - def postprocess_parsed_job(): - # apply defaults and handle type casting - parsed_job['branch'] = parsed_job['branch'] or None - parsed_job['baserev'] = parsed_job['baserev'] or None - parsed_job['patch_level'] = int(parsed_job['patch_level']) - for key in 'repository project who comment'.split(): - parsed_job[key] = parsed_job.get(key, '') - parsed_job['properties'] = parsed_job.get('properties', {}) - - if ver <= "4": - i = int(ver) - 1 - extract_netstrings(p, keys[i]) - parsed_job['builderNames'] = p.strings[len(keys[i]):] - postprocess_parsed_job() - elif ver == "5": - try: - parsed_job = json.loads(p.strings[0]) - except ValueError: - raise BadJobfile("unable to parse JSON") - postprocess_parsed_job() - else: - raise BadJobfile("unknown version '%s'" % ver) - return parsed_job - - def handleJobFile(self, filename, f): - try: - parsed_job = self.parseJob(f) - builderNames = parsed_job['builderNames'] - except BadJobfile: - log.msg("%s reports a bad jobfile in %s" % (self, filename)) - log.err() - return defer.succeed(None) - - # Validate/fixup the builder names. - builderNames = self.filterBuilderList(builderNames) - if not builderNames: - log.msg( - "incoming Try job did not specify any allowed builder names") - return defer.succeed(None) - - who = "" - if parsed_job['who']: - who = parsed_job['who'] - - comment = "" - if parsed_job['comment']: - comment = parsed_job['comment'] - - d = self.master.db.sourcestampsets.addSourceStampSet() - - def addsourcestamp(setid): - self.master.db.sourcestamps.addSourceStamp( - sourcestampsetid=setid, - branch=parsed_job['branch'], - revision=parsed_job['baserev'], - patch_body=parsed_job['patch_body'], - patch_level=parsed_job['patch_level'], - patch_author=who, - patch_comment=comment, - patch_subdir='', # TODO: can't set this remotely - #1769 - project=parsed_job['project'], - repository=parsed_job['repository']) - return setid - - d.addCallback(addsourcestamp) - - def create_buildset(setid): - reason = "'try' job" - if parsed_job['who']: - reason += " by user %s" % parsed_job['who'] - properties = parsed_job['properties'] - requested_props = Properties() - requested_props.update(properties, "try build") - return self.addBuildsetForSourceStamp( - ssid=None, setid=setid, - reason=reason, external_idstring=parsed_job['jobid'], - builderNames=builderNames, properties=requested_props) - d.addCallback(create_buildset) - return d - - -class Try_Userpass_Perspective(pbutil.NewCredPerspective): - def __init__(self, scheduler, username): - self.scheduler = scheduler - self.username = username - - @defer.inlineCallbacks - def perspective_try(self, branch, revision, patch, repository, project, - builderNames, who="", comment="", properties={}): - db = self.scheduler.master.db - log.msg("user %s requesting build on builders %s" % (self.username, - builderNames)) - - # build the intersection of the request and our configured list - builderNames = self.scheduler.filterBuilderList(builderNames) - if not builderNames: - return - - reason = "'try' job" - - if who: - reason += " by user %s" % who - - if comment: - reason += " (%s)" % comment - - sourcestampsetid = yield db.sourcestampsets.addSourceStampSet() - - yield db.sourcestamps.addSourceStamp( - branch=branch, revision=revision, repository=repository, - project=project, patch_level=patch[0], patch_body=patch[1], - patch_subdir='', patch_author=who or '', - patch_comment=comment or '', - sourcestampsetid=sourcestampsetid) - # note: no way to specify patch subdir - #1769 - - requested_props = Properties() - requested_props.update(properties, "try build") - (bsid, brids) = yield self.scheduler.addBuildsetForSourceStamp( - setid=sourcestampsetid, reason=reason, - properties=requested_props, builderNames=builderNames) - - # return a remotely-usable BuildSetStatus object - bsdict = yield db.buildsets.getBuildset(bsid) - - bss = BuildSetStatus(bsdict, self.scheduler.master.status) - from buildbot.status.client import makeRemote - defer.returnValue(makeRemote(bss)) - - def perspective_getAvailableBuilderNames(self): - # Return a list of builder names that are configured - # for the try service - # This is mostly intended for integrating try services - # into other applications - return self.scheduler.listBuilderNames() - - -class Try_Userpass(TryBase): - compare_attrs = ('name', 'builderNames', 'port', 'userpass', 'properties') - - def __init__(self, name, builderNames, port, userpass, - properties={}): - TryBase.__init__(self, name=name, builderNames=builderNames, - properties=properties) - self.port = port - self.userpass = userpass - - def startService(self): - TryBase.startService(self) - - # register each user/passwd with the pbmanager - def factory(mind, username): - return Try_Userpass_Perspective(self, username) - self.registrations = [] - for user, passwd in self.userpass: - self.registrations.append( - self.master.pbmanager.register( - self.port, user, passwd, factory)) - - def stopService(self): - d = defer.maybeDeferred(TryBase.stopService, self) - - def unreg(_): - return defer.gatherResults( - [reg.unregister() for reg in self.registrations]) - d.addCallback(unreg) - return d |