| Index: third_party/buildbot_7_12/buildbot/scheduler.py
|
| diff --git a/third_party/buildbot_7_12/buildbot/scheduler.py b/third_party/buildbot_7_12/buildbot/scheduler.py
|
| deleted file mode 100644
|
| index e2277e1b23802908749e5a86fab4a2454234ae87..0000000000000000000000000000000000000000
|
| --- a/third_party/buildbot_7_12/buildbot/scheduler.py
|
| +++ /dev/null
|
| @@ -1,854 +0,0 @@
|
| -# -*- test-case-name: buildbot.test.test_dependencies -*-
|
| -
|
| -import time, os.path
|
| -
|
| -from zope.interface import implements
|
| -from twisted.internet import reactor
|
| -from twisted.application import service, internet, strports
|
| -from twisted.python import log, runtime
|
| -from twisted.protocols import basic
|
| -from twisted.cred import portal, checkers
|
| -from twisted.spread import pb
|
| -
|
| -from buildbot import interfaces, buildset, util, pbutil
|
| -from buildbot.status import builder
|
| -from buildbot.sourcestamp import SourceStamp
|
| -from buildbot.changes.maildir import MaildirService
|
| -from buildbot.process.properties import Properties
|
| -
|
| -
|
| -class BaseScheduler(service.MultiService, util.ComparableMixin):
|
| - """
|
| - A Scheduler creates BuildSets and submits them to the BuildMaster.
|
| -
|
| - @ivar name: name of the scheduler
|
| -
|
| - @ivar properties: additional properties specified in this
|
| - scheduler's configuration
|
| - @type properties: Properties object
|
| - """
|
| - implements(interfaces.IScheduler)
|
| -
|
| - def __init__(self, name, properties={}):
|
| - """
|
| - @param name: name for this scheduler
|
| -
|
| - @param properties: properties to be propagated from this scheduler
|
| - @type properties: dict
|
| - """
|
| - service.MultiService.__init__(self)
|
| - self.name = name
|
| - self.properties = Properties()
|
| - self.properties.update(properties, "Scheduler")
|
| - self.properties.setProperty("scheduler", name, "Scheduler")
|
| -
|
| - def __repr__(self):
|
| - # TODO: why can't id() return a positive number? %d is ugly.
|
| - return "<Scheduler '%s' at %d>" % (self.name, id(self))
|
| -
|
| - def submitBuildSet(self, bs):
|
| - self.parent.submitBuildSet(bs)
|
| -
|
| - def addChange(self, change):
|
| - pass
|
| -
|
| -class BaseUpstreamScheduler(BaseScheduler):
|
| - implements(interfaces.IUpstreamScheduler)
|
| -
|
| - def __init__(self, name, properties={}):
|
| - BaseScheduler.__init__(self, name, properties)
|
| - self.successWatchers = []
|
| -
|
| - def subscribeToSuccessfulBuilds(self, watcher):
|
| - self.successWatchers.append(watcher)
|
| - def unsubscribeToSuccessfulBuilds(self, watcher):
|
| - self.successWatchers.remove(watcher)
|
| -
|
| - def submitBuildSet(self, bs):
|
| - d = bs.waitUntilFinished()
|
| - d.addCallback(self.buildSetFinished)
|
| - BaseScheduler.submitBuildSet(self, bs)
|
| -
|
| - def buildSetFinished(self, bss):
|
| - if not self.running:
|
| - return
|
| - if bss.getResults() == builder.SUCCESS:
|
| - ss = bss.getSourceStamp()
|
| - for w in self.successWatchers:
|
| - w(ss)
|
| -
|
| -
|
| -class Scheduler(BaseUpstreamScheduler):
|
| - """The default Scheduler class will run a build after some period of time
|
| - called the C{treeStableTimer}, on a given set of Builders. It only pays
|
| - attention to a single branch. You can provide a C{fileIsImportant}
|
| - function which will evaluate each Change to decide whether or not it
|
| - should trigger a new build.
|
| - """
|
| -
|
| - fileIsImportant = None
|
| - compare_attrs = ('name', 'treeStableTimer', 'builderNames', 'branch',
|
| - 'fileIsImportant', 'properties', 'categories')
|
| -
|
| - def __init__(self, name, branch, treeStableTimer, builderNames,
|
| - fileIsImportant=None, properties={}, categories=None):
|
| - """
|
| - @param name: the name of this Scheduler
|
| - @param branch: The branch name that the Scheduler should pay
|
| - attention to. Any Change that is not in this branch
|
| - will be ignored. It can be set to None to only pay
|
| - attention to the default branch.
|
| - @param treeStableTimer: the duration, in seconds, for which the tree
|
| - must remain unchanged before a build is
|
| - triggered. This is intended to avoid builds
|
| - of partially-committed fixes.
|
| - @param builderNames: a list of Builder names. When this Scheduler
|
| - decides to start a set of builds, they will be
|
| - run on the Builders named by this list.
|
| -
|
| - @param fileIsImportant: A callable which takes one argument (a Change
|
| - instance) and returns True if the change is
|
| - worth building, and False if it is not.
|
| - Unimportant Changes are accumulated until the
|
| - build is triggered by an important change.
|
| - The default value of None means that all
|
| - Changes are important.
|
| -
|
| - @param properties: properties to apply to all builds started from this
|
| - scheduler
|
| - @param categories: A list of categories of changes to accept
|
| - """
|
| -
|
| - BaseUpstreamScheduler.__init__(self, name, properties)
|
| - self.treeStableTimer = treeStableTimer
|
| - errmsg = ("The builderNames= argument to Scheduler must be a list "
|
| - "of Builder description names (i.e. the 'name' key of the "
|
| - "Builder specification dictionary)")
|
| - assert isinstance(builderNames, (list, tuple)), errmsg
|
| - for b in builderNames:
|
| - assert isinstance(b, str), errmsg
|
| - self.builderNames = builderNames
|
| - self.branch = branch
|
| - if fileIsImportant:
|
| - assert callable(fileIsImportant)
|
| - self.fileIsImportant = fileIsImportant
|
| -
|
| - self.importantChanges = []
|
| - self.allChanges = []
|
| - self.nextBuildTime = None
|
| - self.timer = None
|
| - self.categories = categories
|
| -
|
| - def listBuilderNames(self):
|
| - return self.builderNames
|
| -
|
| - def getPendingBuildTimes(self):
|
| - if self.nextBuildTime is not None:
|
| - return [self.nextBuildTime]
|
| - return []
|
| -
|
| - def addChange(self, change):
|
| - if change.branch != self.branch:
|
| - log.msg("%s ignoring off-branch %s" % (self, change))
|
| - return
|
| - if self.categories is not None and change.category not in self.categories:
|
| - log.msg("%s ignoring non-matching categories %s" % (self, change))
|
| - return
|
| - if not self.fileIsImportant:
|
| - self.addImportantChange(change)
|
| - elif self.fileIsImportant(change):
|
| - self.addImportantChange(change)
|
| - else:
|
| - self.addUnimportantChange(change)
|
| -
|
| - def addImportantChange(self, change):
|
| - log.msg("%s: change is important, adding %s" % (self, change))
|
| - self.importantChanges.append(change)
|
| - self.allChanges.append(change)
|
| - self.nextBuildTime = max(self.nextBuildTime,
|
| - change.when + self.treeStableTimer)
|
| - self.setTimer(self.nextBuildTime)
|
| -
|
| - def addUnimportantChange(self, change):
|
| - log.msg("%s: change is not important, adding %s" % (self, change))
|
| - self.allChanges.append(change)
|
| -
|
| - def setTimer(self, when):
|
| - log.msg("%s: setting timer to %s" %
|
| - (self, time.strftime("%H:%M:%S", time.localtime(when))))
|
| - now = util.now()
|
| - if when < now:
|
| - when = now
|
| - if self.timer:
|
| - self.timer.cancel()
|
| - self.timer = reactor.callLater(when - now, self.fireTimer)
|
| -
|
| - def stopTimer(self):
|
| - if self.timer:
|
| - self.timer.cancel()
|
| - self.timer = None
|
| -
|
| - def fireTimer(self):
|
| - # clear out our state
|
| - self.timer = None
|
| - self.nextBuildTime = None
|
| - changes = self.allChanges
|
| - self.importantChanges = []
|
| - self.allChanges = []
|
| -
|
| - # create a BuildSet, submit it to the BuildMaster
|
| - bs = buildset.BuildSet(self.builderNames,
|
| - SourceStamp(changes=changes),
|
| - properties=self.properties)
|
| - self.submitBuildSet(bs)
|
| -
|
| - def stopService(self):
|
| - self.stopTimer()
|
| - return service.MultiService.stopService(self)
|
| -
|
| -
|
| -class AnyBranchScheduler(BaseUpstreamScheduler):
|
| - """This Scheduler will handle changes on a variety of branches. It will
|
| - accumulate Changes for each branch separately. It works by creating a
|
| - separate Scheduler for each new branch it sees."""
|
| -
|
| - schedulerFactory = Scheduler
|
| - fileIsImportant = None
|
| -
|
| - compare_attrs = ('name', 'branches', 'treeStableTimer', 'builderNames',
|
| - 'fileIsImportant', 'properties', 'categories')
|
| -
|
| - def __init__(self, name, branches, treeStableTimer, builderNames,
|
| - fileIsImportant=None, properties={}, categories=None):
|
| - """
|
| - @param name: the name of this Scheduler
|
| - @param branches: The branch names that the Scheduler should pay
|
| - attention to. Any Change that is not in one of these
|
| - branches will be ignored. It can be set to None to
|
| - accept changes from any branch. Don't use [] (an
|
| - empty list), because that means we don't pay
|
| - attention to *any* branches, so we'll never build
|
| - anything.
|
| - @param treeStableTimer: the duration, in seconds, for which the tree
|
| - must remain unchanged before a build is
|
| - triggered. This is intended to avoid builds
|
| - of partially-committed fixes.
|
| - @param builderNames: a list of Builder names. When this Scheduler
|
| - decides to start a set of builds, they will be
|
| - run on the Builders named by this list.
|
| -
|
| - @param fileIsImportant: A callable which takes one argument (a Change
|
| - instance) and returns True if the change is
|
| - worth building, and False if it is not.
|
| - Unimportant Changes are accumulated until the
|
| - build is triggered by an important change.
|
| - The default value of None means that all
|
| - Changes are important.
|
| -
|
| - @param properties: properties to apply to all builds started from this
|
| - scheduler
|
| - @param categories: A list of categories of changes to accept
|
| - """
|
| -
|
| - BaseUpstreamScheduler.__init__(self, name, properties)
|
| - self.treeStableTimer = treeStableTimer
|
| - for b in builderNames:
|
| - assert isinstance(b, str)
|
| - self.builderNames = builderNames
|
| - self.branches = branches
|
| - if self.branches == []:
|
| - log.msg("AnyBranchScheduler %s: branches=[], so we will ignore "
|
| - "all branches, and never trigger any builds. Please set "
|
| - "branches=None to mean 'all branches'" % self)
|
| - # consider raising an exception here, to make this warning more
|
| - # prominent, but I can vaguely imagine situations where you might
|
| - # want to comment out branches temporarily and wouldn't
|
| - # appreciate it being treated as an error.
|
| - if fileIsImportant:
|
| - assert callable(fileIsImportant)
|
| - self.fileIsImportant = fileIsImportant
|
| - self.schedulers = {} # one per branch
|
| - self.categories = categories
|
| -
|
| - def __repr__(self):
|
| - return "<AnyBranchScheduler '%s'>" % self.name
|
| -
|
| - def listBuilderNames(self):
|
| - return self.builderNames
|
| -
|
| - def getPendingBuildTimes(self):
|
| - bts = []
|
| - for s in self.schedulers.values():
|
| - if s.nextBuildTime is not None:
|
| - bts.append(s.nextBuildTime)
|
| - return bts
|
| -
|
| - def buildSetFinished(self, bss):
|
| - # we don't care if a build has finished; one of the per-branch builders
|
| - # will take care of it, instead.
|
| - pass
|
| -
|
| - def addChange(self, change):
|
| - branch = change.branch
|
| - if self.branches is not None and branch not in self.branches:
|
| - log.msg("%s ignoring off-branch %s" % (self, change))
|
| - return
|
| - if self.categories is not None and change.category not in self.categories:
|
| - log.msg("%s ignoring non-matching categories %s" % (self, change))
|
| - return
|
| - s = self.schedulers.get(branch)
|
| - if not s:
|
| - if branch:
|
| - name = self.name + "." + branch
|
| - else:
|
| - name = self.name + ".<default>"
|
| - s = self.schedulerFactory(name, branch,
|
| - self.treeStableTimer,
|
| - self.builderNames,
|
| - self.fileIsImportant)
|
| - s.successWatchers = self.successWatchers
|
| - s.setServiceParent(self)
|
| - s.properties = self.properties
|
| - # TODO: does this result in schedulers that stack up forever?
|
| - # When I make the persistify-pass, think about this some more.
|
| - self.schedulers[branch] = s
|
| - s.addChange(change)
|
| -
|
| -
|
| -class Dependent(BaseUpstreamScheduler):
|
| - """This scheduler runs some set of 'downstream' builds when the
|
| - 'upstream' scheduler has completed successfully."""
|
| - implements(interfaces.IDownstreamScheduler)
|
| -
|
| - compare_attrs = ('name', 'upstream', 'builderNames', 'properties')
|
| -
|
| - def __init__(self, name, upstream, builderNames, properties={}):
|
| - assert interfaces.IUpstreamScheduler.providedBy(upstream)
|
| - BaseUpstreamScheduler.__init__(self, name, properties)
|
| - self.upstream_name = upstream.name
|
| - self.upstream = None
|
| - self.builderNames = builderNames
|
| -
|
| - def listBuilderNames(self):
|
| - return self.builderNames
|
| -
|
| - def getPendingBuildTimes(self):
|
| - # report the upstream's value
|
| - return self.findUpstreamScheduler().getPendingBuildTimes()
|
| -
|
| - def startService(self):
|
| - service.MultiService.startService(self)
|
| - self.upstream = self.findUpstreamScheduler()
|
| - self.upstream.subscribeToSuccessfulBuilds(self.upstreamBuilt)
|
| -
|
| - def stopService(self):
|
| - d = service.MultiService.stopService(self)
|
| - self.upstream.unsubscribeToSuccessfulBuilds(self.upstreamBuilt)
|
| - self.upstream = None
|
| - return d
|
| -
|
| - def upstreamBuilt(self, ss):
|
| - bs = buildset.BuildSet(self.builderNames, ss,
|
| - properties=self.properties)
|
| - self.submitBuildSet(bs)
|
| -
|
| - def findUpstreamScheduler(self):
|
| - # find our *active* upstream scheduler (which may not be self.upstream!) by name
|
| - upstream = None
|
| - for s in self.parent.allSchedulers():
|
| - if s.name == self.upstream_name and interfaces.IUpstreamScheduler.providedBy(s):
|
| - upstream = s
|
| - if not upstream:
|
| - log.msg("ERROR: Couldn't find upstream scheduler of name <%s>" %
|
| - self.upstream_name)
|
| - return upstream
|
| -
|
| - def checkUpstreamScheduler(self):
|
| - # if we don't already have an upstream, then there's nothing to worry about
|
| - if not self.upstream:
|
| - return
|
| -
|
| - upstream = self.findUpstreamScheduler()
|
| -
|
| - # if it's already correct, we're good to go
|
| - if upstream is self.upstream:
|
| - return
|
| -
|
| - # otherwise, associate with the new upstream. We also keep listening
|
| - # to the old upstream, in case it's in the middle of a build
|
| - upstream.subscribeToSuccessfulBuilds(self.upstreamBuilt)
|
| - self.upstream = upstream
|
| - log.msg("Dependent <%s> connected to new Upstream <%s>" %
|
| - (self.name, up_name))
|
| -
|
| -class Periodic(BaseUpstreamScheduler):
|
| - """Instead of watching for Changes, this Scheduler can just start a build
|
| - at fixed intervals. The C{periodicBuildTimer} parameter sets the number
|
| - of seconds to wait between such periodic builds. The first build will be
|
| - run immediately."""
|
| -
|
| - # TODO: consider having this watch another (changed-based) scheduler and
|
| - # merely enforce a minimum time between builds.
|
| -
|
| - compare_attrs = ('name', 'builderNames', 'periodicBuildTimer', 'branch', 'properties')
|
| -
|
| - def __init__(self, name, builderNames, periodicBuildTimer,
|
| - branch=None, properties={}):
|
| - BaseUpstreamScheduler.__init__(self, name, properties)
|
| - self.builderNames = builderNames
|
| - self.periodicBuildTimer = periodicBuildTimer
|
| - self.branch = branch
|
| - self.reason = ("The Periodic scheduler named '%s' triggered this build"
|
| - % name)
|
| - self.timer = internet.TimerService(self.periodicBuildTimer,
|
| - self.doPeriodicBuild)
|
| - self.timer.setServiceParent(self)
|
| -
|
| - def listBuilderNames(self):
|
| - return self.builderNames
|
| -
|
| - def getPendingBuildTimes(self):
|
| - # TODO: figure out when self.timer is going to fire next and report
|
| - # that
|
| - return []
|
| -
|
| - def doPeriodicBuild(self):
|
| - bs = buildset.BuildSet(self.builderNames,
|
| - SourceStamp(branch=self.branch),
|
| - self.reason,
|
| - properties=self.properties)
|
| - self.submitBuildSet(bs)
|
| -
|
| -
|
| -
|
| -class Nightly(BaseUpstreamScheduler):
|
| - """Imitate 'cron' scheduling. This can be used to schedule a nightly
|
| - build, or one which runs are certain times of the day, week, or month.
|
| -
|
| - Pass some subset of minute, hour, dayOfMonth, month, and dayOfWeek; each
|
| - may be a single number or a list of valid values. The builds will be
|
| - triggered whenever the current time matches these values. Wildcards are
|
| - represented by a '*' string. All fields default to a wildcard except
|
| - 'minute', so with no fields this defaults to a build every hour, on the
|
| - hour.
|
| -
|
| - For example, the following master.cfg clause will cause a build to be
|
| - started every night at 3:00am::
|
| -
|
| - s = Nightly('nightly', ['builder1', 'builder2'], hour=3, minute=0)
|
| - c['schedules'].append(s)
|
| -
|
| - This scheduler will perform a build each monday morning at 6:23am and
|
| - again at 8:23am::
|
| -
|
| - s = Nightly('BeforeWork', ['builder1'],
|
| - dayOfWeek=0, hour=[6,8], minute=23)
|
| -
|
| - The following runs a build every two hours::
|
| -
|
| - s = Nightly('every2hours', ['builder1'], hour=range(0, 24, 2))
|
| -
|
| - And this one will run only on December 24th::
|
| -
|
| - s = Nightly('SleighPreflightCheck', ['flying_circuits', 'radar'],
|
| - month=12, dayOfMonth=24, hour=12, minute=0)
|
| -
|
| - For dayOfWeek and dayOfMonth, builds are triggered if the date matches
|
| - either of them. All time values are compared against the tuple returned
|
| - by time.localtime(), so month and dayOfMonth numbers start at 1, not
|
| - zero. dayOfWeek=0 is Monday, dayOfWeek=6 is Sunday.
|
| -
|
| - onlyIfChanged functionality
|
| - s = Nightly('nightly', ['builder1', 'builder2'],
|
| - hour=3, minute=0, onlyIfChanged=True)
|
| - When the flag is True (False by default), the build is trigged if
|
| - the date matches and if the branch has changed
|
| -
|
| - fileIsImportant parameter is implemented as defined in class Scheduler
|
| - """
|
| -
|
| - compare_attrs = ('name', 'builderNames',
|
| - 'minute', 'hour', 'dayOfMonth', 'month',
|
| - 'dayOfWeek', 'branch', 'onlyIfChanged',
|
| - 'fileIsImportant', 'properties')
|
| -
|
| - def __init__(self, name, builderNames, minute=0, hour='*',
|
| - dayOfMonth='*', month='*', dayOfWeek='*',
|
| - branch=None, fileIsImportant=None, onlyIfChanged=False, properties={}):
|
| - # Setting minute=0 really makes this an 'Hourly' scheduler. This
|
| - # seemed like a better default than minute='*', which would result in
|
| - # a build every 60 seconds.
|
| - BaseUpstreamScheduler.__init__(self, name, properties)
|
| - self.builderNames = builderNames
|
| - self.minute = minute
|
| - self.hour = hour
|
| - self.dayOfMonth = dayOfMonth
|
| - self.month = month
|
| - self.dayOfWeek = dayOfWeek
|
| - self.branch = branch
|
| - self.onlyIfChanged = onlyIfChanged
|
| - self.delayedRun = None
|
| - self.nextRunTime = None
|
| - self.reason = ("The Nightly scheduler named '%s' triggered this build"
|
| - % name)
|
| -
|
| - self.importantChanges = []
|
| - self.allChanges = []
|
| - self.fileIsImportant = None
|
| - if fileIsImportant:
|
| - assert callable(fileIsImportant)
|
| - self.fileIsImportant = fileIsImportant
|
| -
|
| - def addTime(self, timetuple, secs):
|
| - return time.localtime(time.mktime(timetuple)+secs)
|
| - def findFirstValueAtLeast(self, values, value, default=None):
|
| - for v in values:
|
| - if v >= value: return v
|
| - return default
|
| -
|
| - def setTimer(self):
|
| - self.nextRunTime = self.calculateNextRunTime()
|
| - self.delayedRun = reactor.callLater(self.nextRunTime - time.time(),
|
| - self.doPeriodicBuild)
|
| -
|
| - def startService(self):
|
| - BaseUpstreamScheduler.startService(self)
|
| - self.setTimer()
|
| -
|
| - def stopService(self):
|
| - BaseUpstreamScheduler.stopService(self)
|
| - self.delayedRun.cancel()
|
| -
|
| - def isRunTime(self, timetuple):
|
| - def check(ourvalue, value):
|
| - if ourvalue == '*': return True
|
| - if isinstance(ourvalue, int): return value == ourvalue
|
| - return (value in ourvalue)
|
| -
|
| - if not check(self.minute, timetuple[4]):
|
| - #print 'bad minute', timetuple[4], self.minute
|
| - return False
|
| -
|
| - if not check(self.hour, timetuple[3]):
|
| - #print 'bad hour', timetuple[3], self.hour
|
| - return False
|
| -
|
| - if not check(self.month, timetuple[1]):
|
| - #print 'bad month', timetuple[1], self.month
|
| - return False
|
| -
|
| - if self.dayOfMonth != '*' and self.dayOfWeek != '*':
|
| - # They specified both day(s) of month AND day(s) of week.
|
| - # This means that we only have to match one of the two. If
|
| - # neither one matches, this time is not the right time.
|
| - if not (check(self.dayOfMonth, timetuple[2]) or
|
| - check(self.dayOfWeek, timetuple[6])):
|
| - #print 'bad day'
|
| - return False
|
| - else:
|
| - if not check(self.dayOfMonth, timetuple[2]):
|
| - #print 'bad day of month'
|
| - return False
|
| -
|
| - if not check(self.dayOfWeek, timetuple[6]):
|
| - #print 'bad day of week'
|
| - return False
|
| -
|
| - return True
|
| -
|
| - def calculateNextRunTime(self):
|
| - return self.calculateNextRunTimeFrom(time.time())
|
| -
|
| - def calculateNextRunTimeFrom(self, now):
|
| - dateTime = time.localtime(now)
|
| -
|
| - # Remove seconds by advancing to at least the next minue
|
| - dateTime = self.addTime(dateTime, 60-dateTime[5])
|
| -
|
| - # Now we just keep adding minutes until we find something that matches
|
| -
|
| - # It not an efficient algorithm, but it'll *work* for now
|
| - yearLimit = dateTime[0]+2
|
| - while not self.isRunTime(dateTime):
|
| - dateTime = self.addTime(dateTime, 60)
|
| - #print 'Trying', time.asctime(dateTime)
|
| - assert dateTime[0] < yearLimit, 'Something is wrong with this code'
|
| - return time.mktime(dateTime)
|
| -
|
| - def listBuilderNames(self):
|
| - return self.builderNames
|
| -
|
| - def getPendingBuildTimes(self):
|
| - # TODO: figure out when self.timer is going to fire next and report
|
| - # that
|
| - if self.nextRunTime is None: return []
|
| - return [self.nextRunTime]
|
| -
|
| - def doPeriodicBuild(self):
|
| - # Schedule the next run
|
| - self.setTimer()
|
| -
|
| - if self.onlyIfChanged:
|
| - if len(self.importantChanges) > 0:
|
| - changes = self.allChanges
|
| - # And trigger a build
|
| - log.msg("Nightly Scheduler <%s>: triggering build" % self.name)
|
| - bs = buildset.BuildSet(self.builderNames,
|
| - SourceStamp(changes=changes),
|
| - self.reason,
|
| - properties=self.properties)
|
| - self.submitBuildSet(bs)
|
| - # Reset the change lists
|
| - self.importantChanges = []
|
| - self.allChanges = []
|
| - else:
|
| - log.msg("Nightly Scheduler <%s>: skipping build - No important change" % self.name)
|
| - else:
|
| - # And trigger a build
|
| - bs = buildset.BuildSet(self.builderNames,
|
| - SourceStamp(branch=self.branch),
|
| - self.reason,
|
| - properties=self.properties)
|
| - self.submitBuildSet(bs)
|
| -
|
| - def addChange(self, change):
|
| - if self.onlyIfChanged:
|
| - if change.branch != self.branch:
|
| - log.msg("Nightly Scheduler <%s>: ignoring change %s on off-branch %s" % (self.name, change.revision, change.branch))
|
| - return
|
| - if not self.fileIsImportant:
|
| - self.addImportantChange(change)
|
| - elif self.fileIsImportant(change):
|
| - self.addImportantChange(change)
|
| - else:
|
| - self.addUnimportantChange(change)
|
| - else:
|
| - log.msg("Nightly Scheduler <%s>: no add change" % self.name)
|
| - pass
|
| -
|
| - def addImportantChange(self, change):
|
| - log.msg("Nightly Scheduler <%s>: change %s from %s is important, adding it" % (self.name, change.revision, change.who))
|
| - self.allChanges.append(change)
|
| - self.importantChanges.append(change)
|
| -
|
| - def addUnimportantChange(self, change):
|
| - log.msg("Nightly Scheduler <%s>: change %s from %s is not important, adding it" % (self.name, change.revision, change.who))
|
| - self.allChanges.append(change)
|
| -
|
| -
|
| -class TryBase(BaseScheduler):
|
| - def __init__(self, name, builderNames, properties={}):
|
| - BaseScheduler.__init__(self, name, properties)
|
| - self.builderNames = builderNames
|
| -
|
| - def listBuilderNames(self):
|
| - return self.builderNames
|
| -
|
| - def getPendingBuildTimes(self):
|
| - # we can't predict what the developers are going to do in the future
|
| - return []
|
| -
|
| - def addChange(self, change):
|
| - # Try schedulers ignore Changes
|
| - pass
|
| -
|
| - def processBuilderList(self, builderNames):
|
| - # 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 JobFileScanner(basic.NetstringReceiver):
|
| - def __init__(self):
|
| - self.strings = []
|
| - self.transport = self # so transport.loseConnection works
|
| - self.error = False
|
| -
|
| - def stringReceived(self, s):
|
| - self.strings.append(s)
|
| -
|
| - def loseConnection(self):
|
| - self.error = True
|
| -
|
| -class Try_Jobdir(TryBase):
|
| - compare_attrs = ( 'name', 'builderNames', 'jobdir', 'properties' )
|
| -
|
| - def __init__(self, name, builderNames, jobdir, properties={}):
|
| - TryBase.__init__(self, name, builderNames, properties)
|
| - self.jobdir = jobdir
|
| - self.watcher = MaildirService()
|
| - self.watcher.setServiceParent(self)
|
| -
|
| - def setServiceParent(self, parent):
|
| - self.watcher.setBasedir(os.path.join(parent.basedir, self.jobdir))
|
| - TryBase.setServiceParent(self, parent)
|
| -
|
| - def parseJob(self, f):
|
| - # jobfiles are serialized build requests. Each is a list of
|
| - # serialized netstrings, in the following order:
|
| - # "1", the version number of this format
|
| - # buildsetID, arbitrary string, used to find the buildSet later
|
| - # branch name, "" for default-branch
|
| - # base revision, "" for HEAD
|
| - # patchlevel, usually "1"
|
| - # patch
|
| - # builderNames...
|
| - p = JobFileScanner()
|
| - p.dataReceived(f.read())
|
| - if p.error:
|
| - raise BadJobfile("unable to parse netstrings")
|
| - s = p.strings
|
| - ver = s.pop(0)
|
| - if ver != "1":
|
| - raise BadJobfile("unknown version '%s'" % ver)
|
| - buildsetID, branch, baserev, patchlevel, diff = s[:5]
|
| - builderNames = s[5:]
|
| - if branch == "":
|
| - branch = None
|
| - if baserev == "":
|
| - baserev = None
|
| - patchlevel = int(patchlevel)
|
| - patch = (patchlevel, diff)
|
| - ss = SourceStamp(branch, baserev, patch)
|
| - return builderNames, ss, buildsetID
|
| -
|
| - def messageReceived(self, filename):
|
| - md = os.path.join(self.parent.basedir, self.jobdir)
|
| - if runtime.platformType == "posix":
|
| - # open the file before moving it, because I'm afraid that once
|
| - # it's in cur/, someone might delete it at any moment
|
| - path = os.path.join(md, "new", filename)
|
| - f = open(path, "r")
|
| - os.rename(os.path.join(md, "new", filename),
|
| - os.path.join(md, "cur", filename))
|
| - else:
|
| - # do this backwards under windows, because you can't move a file
|
| - # that somebody is holding open. This was causing a Permission
|
| - # Denied error on bear's win32-twisted1.3 buildslave.
|
| - os.rename(os.path.join(md, "new", filename),
|
| - os.path.join(md, "cur", filename))
|
| - path = os.path.join(md, "cur", filename)
|
| - f = open(path, "r")
|
| -
|
| - try:
|
| - builderNames, ss, bsid = self.parseJob(f)
|
| - except BadJobfile:
|
| - log.msg("%s reports a bad jobfile in %s" % (self, filename))
|
| - log.err()
|
| - return
|
| - # Validate/fixup the builder names.
|
| - builderNames = self.processBuilderList(builderNames)
|
| - if not builderNames:
|
| - return
|
| - reason = "'try' job"
|
| - bs = buildset.BuildSet(builderNames, ss, reason=reason,
|
| - bsid=bsid, properties=self.properties)
|
| - self.submitBuildSet(bs)
|
| -
|
| -class Try_Userpass(TryBase):
|
| - compare_attrs = ( 'name', 'builderNames', 'port', 'userpass', 'properties' )
|
| - implements(portal.IRealm)
|
| -
|
| - def __init__(self, name, builderNames, port, userpass, properties={}):
|
| - TryBase.__init__(self, name, builderNames, properties)
|
| - if type(port) is int:
|
| - port = "tcp:%d" % port
|
| - self.port = port
|
| - self.userpass = userpass
|
| - c = checkers.InMemoryUsernamePasswordDatabaseDontUse()
|
| - for user,passwd in self.userpass:
|
| - c.addUser(user, passwd)
|
| -
|
| - p = portal.Portal(self)
|
| - p.registerChecker(c)
|
| - f = pb.PBServerFactory(p)
|
| - s = strports.service(port, f)
|
| - s.setServiceParent(self)
|
| -
|
| - def getPort(self):
|
| - # utility method for tests: figure out which TCP port we just opened.
|
| - return self.services[0]._port.getHost().port
|
| -
|
| - def requestAvatar(self, avatarID, mind, interface):
|
| - log.msg("%s got connection from user %s" % (self, avatarID))
|
| - assert interface == pb.IPerspective
|
| - p = Try_Userpass_Perspective(self, avatarID)
|
| - return (pb.IPerspective, p, lambda: None)
|
| -
|
| -class Try_Userpass_Perspective(pbutil.NewCredPerspective):
|
| - def __init__(self, parent, username):
|
| - self.parent = parent
|
| - self.username = username
|
| -
|
| - def perspective_try(self, branch, revision, patch, builderNames, properties={}):
|
| - log.msg("user %s requesting build on builders %s" % (self.username,
|
| - builderNames))
|
| - # Validate/fixup the builder names.
|
| - builderNames = self.parent.processBuilderList(builderNames)
|
| - if not builderNames:
|
| - return
|
| - ss = SourceStamp(branch, revision, patch)
|
| - reason = "'try' job from user %s" % self.username
|
| -
|
| - # roll the specified props in with our inherited props
|
| - combined_props = Properties()
|
| - combined_props.updateFromProperties(self.parent.properties)
|
| - combined_props.update(properties, "try build")
|
| -
|
| - bs = buildset.BuildSet(builderNames,
|
| - ss,
|
| - reason=reason,
|
| - properties=combined_props)
|
| -
|
| - self.parent.submitBuildSet(bs)
|
| -
|
| - # return a remotely-usable BuildSetStatus object
|
| - from buildbot.status.client import makeRemote
|
| - return makeRemote(bs.status)
|
| -
|
| -class Triggerable(BaseUpstreamScheduler):
|
| - """This scheduler doesn't do anything until it is triggered by a Trigger
|
| - step in a factory. In general, that step will not complete until all of
|
| - the builds that I fire have finished.
|
| - """
|
| -
|
| - compare_attrs = ('name', 'builderNames', 'properties')
|
| -
|
| - def __init__(self, name, builderNames, properties={}):
|
| - BaseUpstreamScheduler.__init__(self, name, properties)
|
| - self.builderNames = builderNames
|
| -
|
| - def listBuilderNames(self):
|
| - return self.builderNames
|
| -
|
| - def getPendingBuildTimes(self):
|
| - return []
|
| -
|
| - def trigger(self, ss, set_props=None):
|
| - """Trigger this scheduler. 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)
|
| -
|
| - bs = buildset.BuildSet(self.builderNames, ss, properties=props)
|
| - d = bs.waitUntilFinished()
|
| - self.submitBuildSet(bs)
|
| - return d
|
|
|