Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/buildbot/buildbot/interfaces.py
diff options
context:
space:
mode:
Diffstat (limited to 'buildbot/buildbot/interfaces.py')
-rw-r--r--buildbot/buildbot/interfaces.py1123
1 files changed, 1123 insertions, 0 deletions
diff --git a/buildbot/buildbot/interfaces.py b/buildbot/buildbot/interfaces.py
new file mode 100644
index 0000000..e510d05
--- /dev/null
+++ b/buildbot/buildbot/interfaces.py
@@ -0,0 +1,1123 @@
+
+"""Interface documentation.
+
+Define the interfaces that are implemented by various buildbot classes.
+"""
+
+from zope.interface import Interface, Attribute
+
+# exceptions that can be raised while trying to start a build
+class NoSlaveError(Exception):
+ pass
+class BuilderInUseError(Exception):
+ pass
+class BuildSlaveTooOldError(Exception):
+ pass
+class LatentBuildSlaveFailedToSubstantiate(Exception):
+ pass
+
+# other exceptions
+class BuildbotNotRunningError(Exception):
+ pass
+
+class IChangeSource(Interface):
+ """Object which feeds Change objects to the changemaster. When files or
+ directories are changed and the version control system provides some
+ kind of notification, this object should turn it into a Change object
+ and pass it through::
+
+ self.changemaster.addChange(change)
+ """
+
+ def start():
+ """Called when the buildmaster starts. Can be used to establish
+ connections to VC daemons or begin polling."""
+
+ def stop():
+ """Called when the buildmaster shuts down. Connections should be
+ terminated, polling timers should be canceled."""
+
+ def describe():
+ """Should return a string which briefly describes this source. This
+ string will be displayed in an HTML status page."""
+
+class IScheduler(Interface):
+ """I watch for Changes in the source tree and decide when to trigger
+ Builds. I create BuildSet objects and submit them to the BuildMaster. I
+ am a service, and the BuildMaster is always my parent.
+
+ @ivar properties: properties to be applied to all builds started by this
+ scheduler
+ @type properties: L<buildbot.process.properties.Properties>
+ """
+
+ def addChange(change):
+ """A Change has just been dispatched by one of the ChangeSources.
+ Each Scheduler will receive this Change. I may decide to start a
+ build as a result, or I might choose to ignore it."""
+
+ def listBuilderNames():
+ """Return a list of strings indicating the Builders that this
+ Scheduler might feed."""
+
+ def getPendingBuildTimes():
+ """Return a list of timestamps for any builds that are waiting in the
+ tree-stable-timer queue. This is only relevant for Change-based
+ schedulers, all others can just return an empty list."""
+ # TODO: it might be nice to make this into getPendingBuildSets, which
+ # would let someone subscribe to the buildset being finished.
+ # However, the Scheduler doesn't actually create the buildset until
+ # it gets submitted, so doing this would require some major rework.
+
+class IUpstreamScheduler(Interface):
+ """This marks an IScheduler as being eligible for use as the 'upstream='
+ argument to a buildbot.scheduler.Dependent instance."""
+
+ def subscribeToSuccessfulBuilds(target):
+ """Request that the target callbable be invoked after every
+ successful buildset. The target will be called with a single
+ argument: the SourceStamp used by the successful builds."""
+
+ def listBuilderNames():
+ """Return a list of strings indicating the Builders that this
+ Scheduler might feed."""
+
+class IDownstreamScheduler(Interface):
+ """This marks an IScheduler to be listening to other schedulers.
+ On reconfigs, these might get notified to check if their upstream
+ scheduler are stil the same."""
+
+ def checkUpstreamScheduler():
+ """Check if the upstream scheduler is still alive, and if not,
+ get a new upstream object from the master."""
+
+
+class ISourceStamp(Interface):
+ """
+ @cvar branch: branch from which source was drawn
+ @type branch: string or None
+
+ @cvar revision: revision of the source, or None to use CHANGES
+ @type revision: varies depending on VC
+
+ @cvar patch: patch applied to the source, or None if no patch
+ @type patch: None or tuple (level diff)
+
+ @cvar changes: the source step should check out hte latest revision
+ in the given changes
+ @type changes: tuple of L{buildbot.changes.changes.Change} instances,
+ all of which are on the same branch
+ """
+
+ def canBeMergedWith(self, other):
+ """
+ Can this SourceStamp be merged with OTHER?
+ """
+
+ def mergeWith(self, others):
+ """Generate a SourceStamp for the merger of me and all the other
+ BuildRequests. This is called by a Build when it starts, to figure
+ out what its sourceStamp should be."""
+
+ def getAbsoluteSourceStamp(self, got_revision):
+ """Get a new SourceStamp object reflecting the actual revision found
+ by a Source step."""
+
+ def getText(self):
+ """Returns a list of strings to describe the stamp. These are
+ intended to be displayed in a narrow column. If more space is
+ available, the caller should join them together with spaces before
+ presenting them to the user."""
+
+class IEmailSender(Interface):
+ """I know how to send email, and can be used by other parts of the
+ Buildbot to contact developers."""
+ pass
+
+class IEmailLookup(Interface):
+ def getAddress(user):
+ """Turn a User-name string into a valid email address. Either return
+ a string (with an @ in it), None (to indicate that the user cannot
+ be reached by email), or a Deferred which will fire with the same."""
+
+class IStatus(Interface):
+ """I am an object, obtainable from the buildmaster, which can provide
+ status information."""
+
+ def getProjectName():
+ """Return the name of the project that this Buildbot is working
+ for."""
+ def getProjectURL():
+ """Return the URL of this Buildbot's project."""
+ def getBuildbotURL():
+ """Return the URL of the top-most Buildbot status page, or None if
+ this Buildbot does not provide a web status page."""
+ def getURLForThing(thing):
+ """Return the URL of a page which provides information on 'thing',
+ which should be an object that implements one of the status
+ interfaces defined in L{buildbot.interfaces}. Returns None if no
+ suitable page is available (or if no Waterfall is running)."""
+
+ def getChangeSources():
+ """Return a list of IChangeSource objects."""
+
+ def getChange(number):
+ """Return an IChange object."""
+
+ def getSchedulers():
+ """Return a list of ISchedulerStatus objects for all
+ currently-registered Schedulers."""
+
+ def getBuilderNames(categories=None):
+ """Return a list of the names of all current Builders."""
+ def getBuilder(name):
+ """Return the IBuilderStatus object for a given named Builder. Raises
+ KeyError if there is no Builder by that name."""
+
+ def getSlaveNames():
+ """Return a list of buildslave names, suitable for passing to
+ getSlave()."""
+ def getSlave(name):
+ """Return the ISlaveStatus object for a given named buildslave."""
+
+ def getBuildSets():
+ """Return a list of active (non-finished) IBuildSetStatus objects."""
+
+ def generateFinishedBuilds(builders=[], branches=[],
+ num_builds=None, finished_before=None,
+ max_search=200):
+ """Return a generator that will produce IBuildStatus objects each
+ time you invoke its .next() method, starting with the most recent
+ finished build and working backwards.
+
+ @param builders: this is a list of Builder names, and the generator
+ will only produce builds that ran on the given
+ Builders. If the list is empty, produce builds from
+ all Builders.
+
+ @param branches: this is a list of branch names, and the generator
+ will only produce builds that used the given
+ branches. If the list is empty, produce builds from
+ all branches.
+
+ @param num_builds: the generator will stop after providing this many
+ builds. The default of None means to produce as
+ many builds as possible.
+
+ @type finished_before: int: a timestamp, seconds since the epoch
+ @param finished_before: if provided, do not produce any builds that
+ finished after the given timestamp.
+
+ @type max_search: int
+ @param max_search: this method may have to examine a lot of builds
+ to find some that match the search parameters,
+ especially if there aren't any matching builds.
+ This argument imposes a hard limit on the number
+ of builds that will be examined within any given
+ Builder.
+ """
+
+ def subscribe(receiver):
+ """Register an IStatusReceiver to receive new status events. The
+ receiver will immediately be sent a set of 'builderAdded' messages
+ for all current builders. It will receive further 'builderAdded' and
+ 'builderRemoved' messages as the config file is reloaded and builders
+ come and go. It will also receive 'buildsetSubmitted' messages for
+ all outstanding BuildSets (and each new BuildSet that gets
+ submitted). No additional messages will be sent unless the receiver
+ asks for them by calling .subscribe on the IBuilderStatus objects
+ which accompany the addedBuilder message."""
+
+ def unsubscribe(receiver):
+ """Unregister an IStatusReceiver. No further status messgaes will be
+ delivered."""
+
+class IBuildSetStatus(Interface):
+ """I represent a set of Builds, each run on a separate Builder but all
+ using the same source tree."""
+
+ def getSourceStamp():
+ """Return a SourceStamp object which can be used to re-create
+ the source tree that this build used.
+
+ This method will return None if the source information is no longer
+ available."""
+ pass
+ def getReason():
+ pass
+ def getID():
+ """Return the BuildSet's ID string, if any. The 'try' feature uses a
+ random string as a BuildSetID to relate submitted jobs with the
+ resulting BuildSet."""
+ def getResponsibleUsers():
+ pass # not implemented
+ def getInterestedUsers():
+ pass # not implemented
+ def getBuilderNames():
+ """Return a list of the names of all Builders on which this set will
+ do builds."""
+ def getBuildRequests():
+ """Return a list of IBuildRequestStatus objects that represent my
+ component Builds. This list might correspond to the Builders named by
+ getBuilderNames(), but if builder categories are used, or 'Builder
+ Aliases' are implemented, then they may not."""
+ def isFinished():
+ pass
+ def waitUntilSuccess():
+ """Return a Deferred that fires (with this IBuildSetStatus object)
+ when the outcome of the BuildSet is known, i.e., upon the first
+ failure, or after all builds complete successfully."""
+ def waitUntilFinished():
+ """Return a Deferred that fires (with this IBuildSetStatus object)
+ when all builds have finished."""
+ def getResults():
+ pass
+
+class IBuildRequestStatus(Interface):
+ """I represent a request to build a particular set of source code on a
+ particular Builder. These requests may be merged by the time they are
+ finally turned into a Build."""
+
+ def getSourceStamp():
+ """Return a SourceStamp object which can be used to re-create
+ the source tree that this build used. This method will
+ return an absolute SourceStamp if possible, and its results
+ may change as the build progresses. Specifically, a "HEAD"
+ build may later be more accurately specified by an absolute
+ SourceStamp with the specific revision information.
+
+ This method will return None if the source information is no longer
+ available."""
+ pass
+ def getBuilderName():
+ pass
+ def getBuilds():
+ """Return a list of IBuildStatus objects for each Build that has been
+ started in an attempt to satify this BuildRequest."""
+
+ def subscribe(observer):
+ """Register a callable that will be invoked (with a single
+ IBuildStatus object) for each Build that is created to satisfy this
+ request. There may be multiple Builds created in an attempt to handle
+ the request: they may be interrupted by the user or abandoned due to
+ a lost slave. The last Build (the one which actually gets to run to
+ completion) is said to 'satisfy' the BuildRequest. The observer will
+ be called once for each of these Builds, both old and new."""
+ def unsubscribe(observer):
+ """Unregister the callable that was registered with subscribe()."""
+ def getSubmitTime():
+ """Return the time when this request was submitted"""
+ def setSubmitTime(t):
+ """Sets the time when this request was submitted"""
+
+
+class ISlaveStatus(Interface):
+ def getName():
+ """Return the name of the build slave."""
+
+ def getAdmin():
+ """Return a string with the slave admin's contact data."""
+
+ def getHost():
+ """Return a string with the slave host info."""
+
+ def isConnected():
+ """Return True if the slave is currently online, False if not."""
+
+ def lastMessageReceived():
+ """Return a timestamp (seconds since epoch) indicating when the most
+ recent message was received from the buildslave."""
+
+class ISchedulerStatus(Interface):
+ def getName():
+ """Return the name of this Scheduler (a string)."""
+
+ def getPendingBuildsets():
+ """Return an IBuildSet for all BuildSets that are pending. These
+ BuildSets are waiting for their tree-stable-timers to expire."""
+ # TODO: this is not implemented anywhere
+
+
+class IBuilderStatus(Interface):
+ def getName():
+ """Return the name of this Builder (a string)."""
+
+ def getState():
+ # TODO: this isn't nearly as meaningful as it used to be
+ """Return a tuple (state, builds) for this Builder. 'state' is the
+ so-called 'big-status', indicating overall status (as opposed to
+ which step is currently running). It is a string, one of 'offline',
+ 'idle', or 'building'. 'builds' is a list of IBuildStatus objects
+ (possibly empty) representing the currently active builds."""
+
+ def getSlaves():
+ """Return a list of ISlaveStatus objects for the buildslaves that are
+ used by this builder."""
+
+ def getPendingBuilds():
+ """Return an IBuildRequestStatus object for all upcoming builds
+ (those which are ready to go but which are waiting for a buildslave
+ to be available."""
+
+ def getCurrentBuilds():
+ """Return a list containing an IBuildStatus object for each build
+ currently in progress."""
+ # again, we could probably provide an object for 'waiting' and
+ # 'interlocked' too, but things like the Change list might still be
+ # subject to change
+
+ def getLastFinishedBuild():
+ """Return the IBuildStatus object representing the last finished
+ build, which may be None if the builder has not yet finished any
+ builds."""
+
+ def getBuild(number):
+ """Return an IBuildStatus object for a historical build. Each build
+ is numbered (starting at 0 when the Builder is first added),
+ getBuild(n) will retrieve the Nth such build. getBuild(-n) will
+ retrieve a recent build, with -1 being the most recent build
+ started. If the Builder is idle, this will be the same as
+ getLastFinishedBuild(). If the Builder is active, it will be an
+ unfinished build. This method will return None if the build is no
+ longer available. Older builds are likely to have less information
+ stored: Logs are the first to go, then Steps."""
+
+ def getEvent(number):
+ """Return an IStatusEvent object for a recent Event. Builders
+ connecting and disconnecting are events, as are ping attempts.
+ getEvent(-1) will return the most recent event. Events are numbered,
+ but it probably doesn't make sense to ever do getEvent(+n)."""
+
+ def generateFinishedBuilds(branches=[],
+ num_builds=None,
+ max_buildnum=None, finished_before=None,
+ max_search=200,
+ ):
+ """Return a generator that will produce IBuildStatus objects each
+ time you invoke its .next() method, starting with the most recent
+ finished build, then the previous build, and so on back to the oldest
+ build available.
+
+ @param branches: this is a list of branch names, and the generator
+ will only produce builds that involve the given
+ branches. If the list is empty, the generator will
+ produce all builds regardless of what branch they
+ used.
+
+ @param num_builds: if provided, the generator will stop after
+ providing this many builds. The default of None
+ means to produce as many builds as possible.
+
+ @param max_buildnum: if provided, the generator will start by
+ providing the build with this number, or the
+ highest-numbered preceding build (i.e. the
+ generator will not produce any build numbered
+ *higher* than max_buildnum). The default of None
+ means to start with the most recent finished
+ build. -1 means the same as None. -2 means to
+ start with the next-most-recent completed build,
+ etc.
+
+ @type finished_before: int: a timestamp, seconds since the epoch
+ @param finished_before: if provided, do not produce any builds that
+ finished after the given timestamp.
+
+ @type max_search: int
+ @param max_search: this method may have to examine a lot of builds
+ to find some that match the search parameters,
+ especially if there aren't any matching builds.
+ This argument imposes a hard limit on the number
+ of builds that will be examined.
+ """
+
+ def subscribe(receiver):
+ """Register an IStatusReceiver to receive new status events. The
+ receiver will be given builderChangedState, buildStarted, and
+ buildFinished messages."""
+
+ def unsubscribe(receiver):
+ """Unregister an IStatusReceiver. No further status messgaes will be
+ delivered."""
+
+class IEventSource(Interface):
+ def eventGenerator(branches=[]):
+ """This function creates a generator which will yield all of this
+ object's status events, starting with the most recent and progressing
+ backwards in time. These events provide the IStatusEvent interface.
+ At the moment they are all instances of buildbot.status.builder.Event
+ or buildbot.status.builder.BuildStepStatus .
+
+ @param branches: a list of branch names. The generator should only
+ return events that are associated with these branches. If the list is
+ empty, events for all branches should be returned (i.e. an empty list
+ means 'accept all' rather than 'accept none').
+ """
+
+class IBuildStatus(Interface):
+ """I represent the status of a single Build/BuildRequest. It could be
+ in-progress or finished."""
+
+ def getBuilder():
+ """
+ Return the BuilderStatus that owns this build.
+
+ @rtype: implementor of L{IBuilderStatus}
+ """
+
+ def isFinished():
+ """Return a boolean. True means the build has finished, False means
+ it is still running."""
+
+ def waitUntilFinished():
+ """Return a Deferred that will fire when the build finishes. If the
+ build has already finished, this deferred will fire right away. The
+ callback is given this IBuildStatus instance as an argument."""
+
+ def getProperty(propname):
+ """Return the value of the build property with the given name. Raises
+ KeyError if there is no such property on this build."""
+
+ def getReason():
+ """Return a string that indicates why the build was run. 'changes',
+ 'forced', and 'periodic' are the most likely values. 'try' will be
+ added in the future."""
+
+ def getSourceStamp():
+ """Return a SourceStamp object which can be used to re-create
+ the source tree that this build used.
+
+ This method will return None if the source information is no longer
+ available."""
+ # TODO: it should be possible to expire the patch but still remember
+ # that the build was r123+something.
+
+ def getChanges():
+ """Return a list of Change objects which represent which source
+ changes went into the build."""
+
+ def getResponsibleUsers():
+ """Return a list of Users who are to blame for the changes that went
+ into this build. If anything breaks (at least anything that wasn't
+ already broken), blame them. Specifically, this is the set of users
+ who were responsible for the Changes that went into this build. Each
+ User is a string, corresponding to their name as known by the VC
+ repository."""
+
+ def getInterestedUsers():
+ """Return a list of Users who will want to know about the results of
+ this build. This is a superset of getResponsibleUsers(): it adds
+ people who are interested in this build but who did not actually
+ make the Changes that went into it (build sheriffs, code-domain
+ owners)."""
+
+ def getNumber():
+ """Within each builder, each Build has a number. Return it."""
+
+ def getPreviousBuild():
+ """Convenience method. Returns None if the previous build is
+ unavailable."""
+
+ def getSteps():
+ """Return a list of IBuildStepStatus objects. For invariant builds
+ (those which always use the same set of Steps), this should always
+ return the complete list, however some of the steps may not have
+ started yet (step.getTimes()[0] will be None). For variant builds,
+ this may not be complete (asking again later may give you more of
+ them)."""
+
+ def getTimes():
+ """Returns a tuple of (start, end). 'start' and 'end' are the times
+ (seconds since the epoch) when the Build started and finished. If
+ the build is still running, 'end' will be None."""
+
+ # while the build is running, the following methods make sense.
+ # Afterwards they return None
+
+ def getETA():
+ """Returns the number of seconds from now in which the build is
+ expected to finish, or None if we can't make a guess. This guess will
+ be refined over time."""
+
+ def getCurrentStep():
+ """Return an IBuildStepStatus object representing the currently
+ active step."""
+
+ # Once you know the build has finished, the following methods are legal.
+ # Before ths build has finished, they all return None.
+
+ def getSlavename():
+ """Return the name of the buildslave which handled this build."""
+
+ def getText():
+ """Returns a list of strings to describe the build. These are
+ intended to be displayed in a narrow column. If more space is
+ available, the caller should join them together with spaces before
+ presenting them to the user."""
+
+ def getResults():
+ """Return a constant describing the results of the build: one of the
+ constants in buildbot.status.builder: SUCCESS, WARNINGS, or
+ FAILURE."""
+
+ def getLogs():
+ """Return a list of logs that describe the build as a whole. Some
+ steps will contribute their logs, while others are are less important
+ and will only be accessible through the IBuildStepStatus objects.
+ Each log is an object which implements the IStatusLog interface."""
+
+ def getTestResults():
+ """Return a dictionary that maps test-name tuples to ITestResult
+ objects. This may return an empty or partially-filled dictionary
+ until the build has completed."""
+
+ # subscription interface
+
+ def subscribe(receiver, updateInterval=None):
+ """Register an IStatusReceiver to receive new status events. The
+ receiver will be given stepStarted and stepFinished messages. If
+ 'updateInterval' is non-None, buildETAUpdate messages will be sent
+ every 'updateInterval' seconds."""
+
+ def unsubscribe(receiver):
+ """Unregister an IStatusReceiver. No further status messgaes will be
+ delivered."""
+
+class ITestResult(Interface):
+ """I describe the results of a single unit test."""
+
+ def getName():
+ """Returns a tuple of strings which make up the test name. Tests may
+ be arranged in a hierarchy, so looking for common prefixes may be
+ useful."""
+
+ def getResults():
+ """Returns a constant describing the results of the test: SUCCESS,
+ WARNINGS, FAILURE."""
+
+ def getText():
+ """Returns a list of short strings which describe the results of the
+ test in slightly more detail. Suggested components include
+ 'failure', 'error', 'passed', 'timeout'."""
+
+ def getLogs():
+ # in flux, it may be possible to provide more structured information
+ # like python Failure instances
+ """Returns a dictionary of test logs. The keys are strings like
+ 'stdout', 'log', 'exceptions'. The values are strings."""
+
+
+class IBuildStepStatus(Interface):
+ """I hold status for a single BuildStep."""
+
+ def getName():
+ """Returns a short string with the name of this step. This string
+ may have spaces in it."""
+
+ def getBuild():
+ """Returns the IBuildStatus object which contains this step."""
+
+ def getTimes():
+ """Returns a tuple of (start, end). 'start' and 'end' are the times
+ (seconds since the epoch) when the Step started and finished. If the
+ step has not yet started, 'start' will be None. If the step is still
+ running, 'end' will be None."""
+
+ def getExpectations():
+ """Returns a list of tuples (name, current, target). Each tuple
+ describes a single axis along which the step's progress can be
+ measured. 'name' is a string which describes the axis itself, like
+ 'filesCompiled' or 'tests run' or 'bytes of output'. 'current' is a
+ number with the progress made so far, while 'target' is the value
+ that we expect (based upon past experience) to get to when the build
+ is finished.
+
+ 'current' will change over time until the step is finished. It is
+ 'None' until the step starts. When the build is finished, 'current'
+ may or may not equal 'target' (which is merely the expectation based
+ upon previous builds)."""
+
+ def getURLs():
+ """Returns a dictionary of URLs. Each key is a link name (a short
+ string, like 'results' or 'coverage'), and each value is a URL. These
+ links will be displayed along with the LogFiles.
+ """
+
+ def getLogs():
+ """Returns a list of IStatusLog objects. If the step has not yet
+ finished, this list may be incomplete (asking again later may give
+ you more of them)."""
+
+
+ def isFinished():
+ """Return a boolean. True means the step has finished, False means it
+ is still running."""
+
+ def waitUntilFinished():
+ """Return a Deferred that will fire when the step finishes. If the
+ step has already finished, this deferred will fire right away. The
+ callback is given this IBuildStepStatus instance as an argument."""
+
+ # while the step is running, the following methods make sense.
+ # Afterwards they return None
+
+ def getETA():
+ """Returns the number of seconds from now in which the step is
+ expected to finish, or None if we can't make a guess. This guess will
+ be refined over time."""
+
+ # Once you know the step has finished, the following methods are legal.
+ # Before ths step has finished, they all return None.
+
+ def getText():
+ """Returns a list of strings which describe the step. These are
+ intended to be displayed in a narrow column. If more space is
+ available, the caller should join them together with spaces before
+ presenting them to the user."""
+
+ def getResults():
+ """Return a tuple describing the results of the step: (result,
+ strings). 'result' is one of the constants in
+ buildbot.status.builder: SUCCESS, WARNINGS, FAILURE, or SKIPPED.
+ 'strings' is an optional list of strings that the step wants to
+ append to the overall build's results. These strings are usually
+ more terse than the ones returned by getText(): in particular,
+ successful Steps do not usually contribute any text to the overall
+ build."""
+
+ # subscription interface
+
+ def subscribe(receiver, updateInterval=10):
+ """Register an IStatusReceiver to receive new status events. The
+ receiver will be given logStarted and logFinished messages. It will
+ also be given a ETAUpdate message every 'updateInterval' seconds."""
+
+ def unsubscribe(receiver):
+ """Unregister an IStatusReceiver. No further status messgaes will be
+ delivered."""
+
+class IStatusEvent(Interface):
+ """I represent a Builder Event, something non-Build related that can
+ happen to a Builder."""
+
+ def getTimes():
+ """Returns a tuple of (start, end) like IBuildStepStatus, but end==0
+ indicates that this is a 'point event', which has no duration.
+ SlaveConnect/Disconnect are point events. Ping is not: it starts
+ when requested and ends when the response (positive or negative) is
+ returned"""
+
+ def getText():
+ """Returns a list of strings which describe the event. These are
+ intended to be displayed in a narrow column. If more space is
+ available, the caller should join them together with spaces before
+ presenting them to the user."""
+
+
+LOG_CHANNEL_STDOUT = 0
+LOG_CHANNEL_STDERR = 1
+LOG_CHANNEL_HEADER = 2
+
+class IStatusLog(Interface):
+ """I represent a single Log, which is a growing list of text items that
+ contains some kind of output for a single BuildStep. I might be finished,
+ in which case this list has stopped growing.
+
+ Each Log has a name, usually something boring like 'log' or 'output'.
+ These names are not guaranteed to be unique, however they are usually
+ chosen to be useful within the scope of a single step (i.e. the Compile
+ step might produce both 'log' and 'warnings'). The name may also have
+ spaces. If you want something more globally meaningful, at least within a
+ given Build, try::
+
+ '%s.%s' % (log.getStep.getName(), log.getName())
+
+ The Log can be presented as plain text, or it can be accessed as a list
+ of items, each of which has a channel indicator (header, stdout, stderr)
+ and a text chunk. An HTML display might represent the interleaved
+ channels with different styles, while a straight download-the-text
+ interface would just want to retrieve a big string.
+
+ The 'header' channel is used by ShellCommands to prepend a note about
+ which command is about to be run ('running command FOO in directory
+ DIR'), and append another note giving the exit code of the process.
+
+ Logs can be streaming: if the Log has not yet finished, you can
+ subscribe to receive new chunks as they are added.
+
+ A ShellCommand will have a Log associated with it that gathers stdout
+ and stderr. Logs may also be created by parsing command output or
+ through other synthetic means (grepping for all the warnings in a
+ compile log, or listing all the test cases that are going to be run).
+ Such synthetic Logs are usually finished as soon as they are created."""
+
+
+ def getName():
+ """Returns a short string with the name of this log, probably 'log'.
+ """
+
+ def getStep():
+ """Returns the IBuildStepStatus which owns this log."""
+ # TODO: can there be non-Step logs?
+
+ def isFinished():
+ """Return a boolean. True means the log has finished and is closed,
+ False means it is still open and new chunks may be added to it."""
+
+ def waitUntilFinished():
+ """Return a Deferred that will fire when the log is closed. If the
+ log has already finished, this deferred will fire right away. The
+ callback is given this IStatusLog instance as an argument."""
+
+ def subscribe(receiver, catchup):
+ """Register an IStatusReceiver to receive chunks (with logChunk) as
+ data is added to the Log. If you use this, you will also want to use
+ waitUntilFinished to find out when the listener can be retired.
+ Subscribing to a closed Log is a no-op.
+
+ If 'catchup' is True, the receiver will immediately be sent a series
+ of logChunk messages to bring it up to date with the partially-filled
+ log. This allows a status client to join a Log already in progress
+ without missing any data. If the Log has already finished, it is too
+ late to catch up: just do getText() instead.
+
+ If the Log is very large, the receiver will be called many times with
+ a lot of data. There is no way to throttle this data. If the receiver
+ is planning on sending the data on to somewhere else, over a narrow
+ connection, you can get a throttleable subscription by using
+ C{subscribeConsumer} instead."""
+
+ def unsubscribe(receiver):
+ """Remove a receiver previously registered with subscribe(). Attempts
+ to remove a receiver which was not previously registered is a no-op.
+ """
+
+ def subscribeConsumer(consumer):
+ """Register an L{IStatusLogConsumer} to receive all chunks of the
+ logfile, including all the old entries and any that will arrive in
+ the future. The consumer will first have their C{registerProducer}
+ method invoked with a reference to an object that can be told
+ C{pauseProducing}, C{resumeProducing}, and C{stopProducing}. Then the
+ consumer's C{writeChunk} method will be called repeatedly with each
+ (channel, text) tuple in the log, starting with the very first. The
+ consumer will be notified with C{finish} when the log has been
+ exhausted (which can only happen when the log is finished). Note that
+ a small amount of data could be written via C{writeChunk} even after
+ C{pauseProducing} has been called.
+
+ To unsubscribe the consumer, use C{producer.stopProducing}."""
+
+ # once the log has finished, the following methods make sense. They can
+ # be called earlier, but they will only return the contents of the log up
+ # to the point at which they were called. You will lose items that are
+ # added later. Use C{subscribe} or C{subscribeConsumer} to avoid missing
+ # anything.
+
+ def hasContents():
+ """Returns True if the LogFile still has contents available. Returns
+ False for logs that have been pruned. Clients should test this before
+ offering to show the contents of any log."""
+
+ def getText():
+ """Return one big string with the contents of the Log. This merges
+ all non-header chunks together."""
+
+ def readlines(channel=LOG_CHANNEL_STDOUT):
+ """Read lines from one channel of the logfile. This returns an
+ iterator that will provide single lines of text (including the
+ trailing newline).
+ """
+
+ def getTextWithHeaders():
+ """Return one big string with the contents of the Log. This merges
+ all chunks (including headers) together."""
+
+ def getChunks():
+ """Generate a list of (channel, text) tuples. 'channel' is a number,
+ 0 for stdout, 1 for stderr, 2 for header. (note that stderr is merged
+ into stdout if PTYs are in use)."""
+
+class IStatusLogConsumer(Interface):
+ """I am an object which can be passed to IStatusLog.subscribeConsumer().
+ I represent a target for writing the contents of an IStatusLog. This
+ differs from a regular IStatusReceiver in that it can pause the producer.
+ This makes it more suitable for use in streaming data over network
+ sockets, such as an HTTP request. Note that the consumer can only pause
+ the producer until it has caught up with all the old data. After that
+ point, C{pauseProducing} is ignored and all new output from the log is
+ sent directoy to the consumer."""
+
+ def registerProducer(producer, streaming):
+ """A producer is being hooked up to this consumer. The consumer only
+ has to handle a single producer. It should send .pauseProducing and
+ .resumeProducing messages to the producer when it wants to stop or
+ resume the flow of data. 'streaming' will be set to True because the
+ producer is always a PushProducer.
+ """
+
+ def unregisterProducer():
+ """The previously-registered producer has been removed. No further
+ pauseProducing or resumeProducing calls should be made. The consumer
+ should delete its reference to the Producer so it can be released."""
+
+ def writeChunk(chunk):
+ """A chunk (i.e. a tuple of (channel, text)) is being written to the
+ consumer."""
+
+ def finish():
+ """The log has finished sending chunks to the consumer."""
+
+class IStatusReceiver(Interface):
+ """I am an object which can receive build status updates. I may be
+ subscribed to an IStatus, an IBuilderStatus, or an IBuildStatus."""
+
+ def buildsetSubmitted(buildset):
+ """A new BuildSet has been submitted to the buildmaster.
+
+ @type buildset: implementor of L{IBuildSetStatus}
+ """
+
+ def requestSubmitted(request):
+ """A new BuildRequest has been submitted to the buildmaster.
+
+ @type request: implementor of L{IBuildRequestStatus}
+ """
+
+ def builderAdded(builderName, builder):
+ """
+ A new Builder has just been added. This method may return an
+ IStatusReceiver (probably 'self') which will be subscribed to receive
+ builderChangedState and buildStarted/Finished events.
+
+ @type builderName: string
+ @type builder: L{buildbot.status.builder.BuilderStatus}
+ @rtype: implementor of L{IStatusReceiver}
+ """
+
+ def builderChangedState(builderName, state):
+ """Builder 'builderName' has changed state. The possible values for
+ 'state' are 'offline', 'idle', and 'building'."""
+
+ def buildStarted(builderName, build):
+ """Builder 'builderName' has just started a build. The build is an
+ object which implements IBuildStatus, and can be queried for more
+ information.
+
+ This method may return an IStatusReceiver (it could even return
+ 'self'). If it does so, stepStarted and stepFinished methods will be
+ invoked on the object for the steps of this one build. This is a
+ convenient way to subscribe to all build steps without missing any.
+ This receiver will automatically be unsubscribed when the build
+ finishes.
+
+ It can also return a tuple of (IStatusReceiver, interval), in which
+ case buildETAUpdate messages are sent ever 'interval' seconds, in
+ addition to the stepStarted and stepFinished messages."""
+
+ def buildETAUpdate(build, ETA):
+ """This is a periodic update on the progress this Build has made
+ towards completion."""
+
+ def stepStarted(build, step):
+ """A step has just started. 'step' is the IBuildStepStatus which
+ represents the step: it can be queried for more information.
+
+ This method may return an IStatusReceiver (it could even return
+ 'self'). If it does so, logStarted and logFinished methods will be
+ invoked on the object for logs created by this one step. This
+ receiver will be automatically unsubscribed when the step finishes.
+
+ Alternatively, the method may return a tuple of an IStatusReceiver
+ and an integer named 'updateInterval'. In addition to
+ logStarted/logFinished messages, it will also receive stepETAUpdate
+ messages about every updateInterval seconds."""
+
+ def stepTextChanged(build, step, text):
+ """The text for a step has been updated.
+
+ This is called when calling setText() on the step status, and
+ hands in the text list."""
+
+ def stepText2Changed(build, step, text2):
+ """The text2 for a step has been updated.
+
+ This is called when calling setText2() on the step status, and
+ hands in text2 list."""
+
+ def stepETAUpdate(build, step, ETA, expectations):
+ """This is a periodic update on the progress this Step has made
+ towards completion. It gets an ETA (in seconds from the present) of
+ when the step ought to be complete, and a list of expectation tuples
+ (as returned by IBuildStepStatus.getExpectations) with more detailed
+ information."""
+
+ def logStarted(build, step, log):
+ """A new Log has been started, probably because a step has just
+ started running a shell command. 'log' is the IStatusLog object
+ which can be queried for more information.
+
+ This method may return an IStatusReceiver (such as 'self'), in which
+ case the target's logChunk method will be invoked as text is added to
+ the logfile. This receiver will automatically be unsubsribed when the
+ log finishes."""
+
+ def logChunk(build, step, log, channel, text):
+ """Some text has been added to this log. 'channel' is one of
+ LOG_CHANNEL_STDOUT, LOG_CHANNEL_STDERR, or LOG_CHANNEL_HEADER, as
+ defined in IStatusLog.getChunks."""
+
+ def logFinished(build, step, log):
+ """A Log has been closed."""
+
+ def stepFinished(build, step, results):
+ """A step has just finished. 'results' is the result tuple described
+ in IBuildStepStatus.getResults."""
+
+ def buildFinished(builderName, build, results):
+ """
+ A build has just finished. 'results' is the result tuple described
+ in L{IBuildStatus.getResults}.
+
+ @type builderName: string
+ @type build: L{buildbot.status.builder.BuildStatus}
+ @type results: tuple
+ """
+
+ def builderRemoved(builderName):
+ """The Builder has been removed."""
+
+class IControl(Interface):
+ def addChange(change):
+ """Add a change to all builders. Each Builder will decide for
+ themselves whether the change is interesting or not, and may initiate
+ a build as a result."""
+
+ def submitBuildSet(buildset):
+ """Submit a BuildSet object, which will eventually be run on all of
+ the builders listed therein."""
+
+ def getBuilder(name):
+ """Retrieve the IBuilderControl object for the given Builder."""
+
+class IBuilderControl(Interface):
+ def requestBuild(request):
+ """Queue a L{buildbot.process.base.BuildRequest} object for later
+ building."""
+
+ def requestBuildSoon(request):
+ """Submit a BuildRequest like requestBuild, but raise a
+ L{buildbot.interfaces.NoSlaveError} if no slaves are currently
+ available, so it cannot be used to queue a BuildRequest in the hopes
+ that a slave will eventually connect. This method is appropriate for
+ use by things like the web-page 'Force Build' button."""
+
+ def resubmitBuild(buildStatus, reason="<rebuild, no reason given>"):
+ """Rebuild something we've already built before. This submits a
+ BuildRequest to our Builder using the same SourceStamp as the earlier
+ build. This has no effect (but may eventually raise an exception) if
+ this Build has not yet finished."""
+
+ def getPendingBuilds():
+ """Return a list of L{IBuildRequestControl} objects for this Builder.
+ Each one corresponds to a pending build that has not yet started (due
+ to a scarcity of build slaves). These upcoming builds can be canceled
+ through the control object."""
+
+ def getBuild(number):
+ """Attempt to return an IBuildControl object for the given build.
+ Returns None if no such object is available. This will only work for
+ the build that is currently in progress: once the build finishes,
+ there is nothing to control anymore."""
+
+ def ping(timeout=30):
+ """Attempt to contact the slave and see if it is still alive. This
+ returns a Deferred which fires with either True (the slave is still
+ alive) or False (the slave did not respond). As a side effect, adds
+ an event to this builder's column in the waterfall display
+ containing the results of the ping."""
+ # TODO: this ought to live in ISlaveControl, maybe with disconnect()
+ # or something. However the event that is emitted is most useful in
+ # the Builder column, so it kinda fits here too.
+
+class IBuildRequestControl(Interface):
+ def subscribe(observer):
+ """Register a callable that will be invoked (with a single
+ IBuildControl object) for each Build that is created to satisfy this
+ request. There may be multiple Builds created in an attempt to handle
+ the request: they may be interrupted by the user or abandoned due to
+ a lost slave. The last Build (the one which actually gets to run to
+ completion) is said to 'satisfy' the BuildRequest. The observer will
+ be called once for each of these Builds, both old and new."""
+ def unsubscribe(observer):
+ """Unregister the callable that was registered with subscribe()."""
+ def cancel():
+ """Remove the build from the pending queue. Has no effect if the
+ build has already been started."""
+
+class IBuildControl(Interface):
+ def getStatus():
+ """Return an IBuildStatus object for the Build that I control."""
+ def stopBuild(reason="<no reason given>"):
+ """Halt the build. This has no effect if the build has already
+ finished."""
+
+class ILogFile(Interface):
+ """This is the internal interface to a LogFile, used by the BuildStep to
+ write data into the log.
+ """
+ def addStdout(data):
+ pass
+ def addStderr(data):
+ pass
+ def addHeader(data):
+ pass
+ def finish():
+ """The process that is feeding the log file has finished, and no
+ further data will be added. This closes the logfile."""
+
+class ILogObserver(Interface):
+ """Objects which provide this interface can be used in a BuildStep to
+ watch the output of a LogFile and parse it incrementally.
+ """
+
+ # internal methods
+ def setStep(step):
+ pass
+ def setLog(log):
+ pass
+
+ # methods called by the LogFile
+ def logChunk(build, step, log, channel, text):
+ pass
+
+class IBuildSlave(Interface):
+ # this is a marker interface for the BuildSlave class
+ pass
+
+class ILatentBuildSlave(IBuildSlave):
+ """A build slave that is not always running, but can run when requested.
+ """
+ substantiated = Attribute('Substantiated',
+ 'Whether the latent build slave is currently '
+ 'substantiated with a real instance.')
+
+ def substantiate():
+ """Request that the slave substantiate with a real instance.
+
+ Returns a deferred that will callback when a real instance has
+ attached."""
+
+ # there is an insubstantiate too, but that is not used externally ATM.
+
+ def buildStarted(sb):
+ """Inform the latent build slave that a build has started.
+
+ ``sb`` is a LatentSlaveBuilder as defined in buildslave.py. The sb
+ is the one for whom the build started.
+ """
+
+ def buildFinished(sb):
+ """Inform the latent build slave that a build has finished.
+
+ ``sb`` is a LatentSlaveBuilder as defined in buildslave.py. The sb
+ is the one for whom the build finished.
+ """