2 """Interface documentation.
4 Define the interfaces that are implemented by various buildbot classes.
7 from zope
.interface
import Interface
, Attribute
9 # exceptions that can be raised while trying to start a build
10 class NoSlaveError(Exception):
12 class BuilderInUseError(Exception):
14 class BuildSlaveTooOldError(Exception):
16 class LatentBuildSlaveFailedToSubstantiate(Exception):
20 class BuildbotNotRunningError(Exception):
23 class IChangeSource(Interface
):
24 """Object which feeds Change objects to the changemaster. When files or
25 directories are changed and the version control system provides some
26 kind of notification, this object should turn it into a Change object
29 self.changemaster.addChange(change)
33 """Called when the buildmaster starts. Can be used to establish
34 connections to VC daemons or begin polling."""
37 """Called when the buildmaster shuts down. Connections should be
38 terminated, polling timers should be canceled."""
41 """Should return a string which briefly describes this source. This
42 string will be displayed in an HTML status page."""
44 class IScheduler(Interface
):
45 """I watch for Changes in the source tree and decide when to trigger
46 Builds. I create BuildSet objects and submit them to the BuildMaster. I
47 am a service, and the BuildMaster is always my parent.
49 @ivar properties: properties to be applied to all builds started by this
51 @type properties: L<buildbot.process.properties.Properties>
54 def addChange(change
):
55 """A Change has just been dispatched by one of the ChangeSources.
56 Each Scheduler will receive this Change. I may decide to start a
57 build as a result, or I might choose to ignore it."""
59 def listBuilderNames():
60 """Return a list of strings indicating the Builders that this
61 Scheduler might feed."""
63 def getPendingBuildTimes():
64 """Return a list of timestamps for any builds that are waiting in the
65 tree-stable-timer queue. This is only relevant for Change-based
66 schedulers, all others can just return an empty list."""
67 # TODO: it might be nice to make this into getPendingBuildSets, which
68 # would let someone subscribe to the buildset being finished.
69 # However, the Scheduler doesn't actually create the buildset until
70 # it gets submitted, so doing this would require some major rework.
72 class IUpstreamScheduler(Interface
):
73 """This marks an IScheduler as being eligible for use as the 'upstream='
74 argument to a buildbot.scheduler.Dependent instance."""
76 def subscribeToSuccessfulBuilds(target
):
77 """Request that the target callbable be invoked after every
78 successful buildset. The target will be called with a single
79 argument: the SourceStamp used by the successful builds."""
81 def listBuilderNames():
82 """Return a list of strings indicating the Builders that this
83 Scheduler might feed."""
85 class IDownstreamScheduler(Interface
):
86 """This marks an IScheduler to be listening to other schedulers.
87 On reconfigs, these might get notified to check if their upstream
88 scheduler are stil the same."""
90 def checkUpstreamScheduler():
91 """Check if the upstream scheduler is still alive, and if not,
92 get a new upstream object from the master."""
95 class ISourceStamp(Interface
):
97 @cvar branch: branch from which source was drawn
98 @type branch: string or None
100 @cvar revision: revision of the source, or None to use CHANGES
101 @type revision: varies depending on VC
103 @cvar patch: patch applied to the source, or None if no patch
104 @type patch: None or tuple (level diff)
106 @cvar changes: the source step should check out hte latest revision
108 @type changes: tuple of L{buildbot.changes.changes.Change} instances,
109 all of which are on the same branch
112 def canBeMergedWith(self
, other
):
114 Can this SourceStamp be merged with OTHER?
117 def mergeWith(self
, others
):
118 """Generate a SourceStamp for the merger of me and all the other
119 BuildRequests. This is called by a Build when it starts, to figure
120 out what its sourceStamp should be."""
122 def getAbsoluteSourceStamp(self
, got_revision
):
123 """Get a new SourceStamp object reflecting the actual revision found
127 """Returns a list of strings to describe the stamp. These are
128 intended to be displayed in a narrow column. If more space is
129 available, the caller should join them together with spaces before
130 presenting them to the user."""
132 class IEmailSender(Interface
):
133 """I know how to send email, and can be used by other parts of the
134 Buildbot to contact developers."""
137 class IEmailLookup(Interface
):
138 def getAddress(user
):
139 """Turn a User-name string into a valid email address. Either return
140 a string (with an @ in it), None (to indicate that the user cannot
141 be reached by email), or a Deferred which will fire with the same."""
143 class IStatus(Interface
):
144 """I am an object, obtainable from the buildmaster, which can provide
145 status information."""
147 def getProjectName():
148 """Return the name of the project that this Buildbot is working
151 """Return the URL of this Buildbot's project."""
152 def getBuildbotURL():
153 """Return the URL of the top-most Buildbot status page, or None if
154 this Buildbot does not provide a web status page."""
155 def getURLForThing(thing
):
156 """Return the URL of a page which provides information on 'thing',
157 which should be an object that implements one of the status
158 interfaces defined in L{buildbot.interfaces}. Returns None if no
159 suitable page is available (or if no Waterfall is running)."""
161 def getChangeSources():
162 """Return a list of IChangeSource objects."""
164 def getChange(number
):
165 """Return an IChange object."""
168 """Return a list of ISchedulerStatus objects for all
169 currently-registered Schedulers."""
171 def getBuilderNames(categories
=None):
172 """Return a list of the names of all current Builders."""
173 def getBuilder(name
):
174 """Return the IBuilderStatus object for a given named Builder. Raises
175 KeyError if there is no Builder by that name."""
178 """Return a list of buildslave names, suitable for passing to
181 """Return the ISlaveStatus object for a given named buildslave."""
184 """Return a list of active (non-finished) IBuildSetStatus objects."""
186 def generateFinishedBuilds(builders
=[], branches
=[],
187 num_builds
=None, finished_before
=None,
189 """Return a generator that will produce IBuildStatus objects each
190 time you invoke its .next() method, starting with the most recent
191 finished build and working backwards.
193 @param builders: this is a list of Builder names, and the generator
194 will only produce builds that ran on the given
195 Builders. If the list is empty, produce builds from
198 @param branches: this is a list of branch names, and the generator
199 will only produce builds that used the given
200 branches. If the list is empty, produce builds from
203 @param num_builds: the generator will stop after providing this many
204 builds. The default of None means to produce as
205 many builds as possible.
207 @type finished_before: int: a timestamp, seconds since the epoch
208 @param finished_before: if provided, do not produce any builds that
209 finished after the given timestamp.
211 @type max_search: int
212 @param max_search: this method may have to examine a lot of builds
213 to find some that match the search parameters,
214 especially if there aren't any matching builds.
215 This argument imposes a hard limit on the number
216 of builds that will be examined within any given
220 def subscribe(receiver
):
221 """Register an IStatusReceiver to receive new status events. The
222 receiver will immediately be sent a set of 'builderAdded' messages
223 for all current builders. It will receive further 'builderAdded' and
224 'builderRemoved' messages as the config file is reloaded and builders
225 come and go. It will also receive 'buildsetSubmitted' messages for
226 all outstanding BuildSets (and each new BuildSet that gets
227 submitted). No additional messages will be sent unless the receiver
228 asks for them by calling .subscribe on the IBuilderStatus objects
229 which accompany the addedBuilder message."""
231 def unsubscribe(receiver
):
232 """Unregister an IStatusReceiver. No further status messgaes will be
235 class IBuildSetStatus(Interface
):
236 """I represent a set of Builds, each run on a separate Builder but all
237 using the same source tree."""
239 def getSourceStamp():
240 """Return a SourceStamp object which can be used to re-create
241 the source tree that this build used.
243 This method will return None if the source information is no longer
249 """Return the BuildSet's ID string, if any. The 'try' feature uses a
250 random string as a BuildSetID to relate submitted jobs with the
251 resulting BuildSet."""
252 def getResponsibleUsers():
253 pass # not implemented
254 def getInterestedUsers():
255 pass # not implemented
256 def getBuilderNames():
257 """Return a list of the names of all Builders on which this set will
259 def getBuildRequests():
260 """Return a list of IBuildRequestStatus objects that represent my
261 component Builds. This list might correspond to the Builders named by
262 getBuilderNames(), but if builder categories are used, or 'Builder
263 Aliases' are implemented, then they may not."""
266 def waitUntilSuccess():
267 """Return a Deferred that fires (with this IBuildSetStatus object)
268 when the outcome of the BuildSet is known, i.e., upon the first
269 failure, or after all builds complete successfully."""
270 def waitUntilFinished():
271 """Return a Deferred that fires (with this IBuildSetStatus object)
272 when all builds have finished."""
276 class IBuildRequestStatus(Interface
):
277 """I represent a request to build a particular set of source code on a
278 particular Builder. These requests may be merged by the time they are
279 finally turned into a Build."""
281 def getSourceStamp():
282 """Return a SourceStamp object which can be used to re-create
283 the source tree that this build used. This method will
284 return an absolute SourceStamp if possible, and its results
285 may change as the build progresses. Specifically, a "HEAD"
286 build may later be more accurately specified by an absolute
287 SourceStamp with the specific revision information.
289 This method will return None if the source information is no longer
292 def getBuilderName():
295 """Return a list of IBuildStatus objects for each Build that has been
296 started in an attempt to satify this BuildRequest."""
298 def subscribe(observer
):
299 """Register a callable that will be invoked (with a single
300 IBuildStatus object) for each Build that is created to satisfy this
301 request. There may be multiple Builds created in an attempt to handle
302 the request: they may be interrupted by the user or abandoned due to
303 a lost slave. The last Build (the one which actually gets to run to
304 completion) is said to 'satisfy' the BuildRequest. The observer will
305 be called once for each of these Builds, both old and new."""
306 def unsubscribe(observer
):
307 """Unregister the callable that was registered with subscribe()."""
310 class ISlaveStatus(Interface
):
312 """Return the name of the build slave."""
315 """Return a string with the slave admin's contact data."""
318 """Return a string with the slave host info."""
321 """Return True if the slave is currently online, False if not."""
323 def lastMessageReceived():
324 """Return a timestamp (seconds since epoch) indicating when the most
325 recent message was received from the buildslave."""
327 class ISchedulerStatus(Interface
):
329 """Return the name of this Scheduler (a string)."""
331 def getPendingBuildsets():
332 """Return an IBuildSet for all BuildSets that are pending. These
333 BuildSets are waiting for their tree-stable-timers to expire."""
334 # TODO: this is not implemented anywhere
337 class IBuilderStatus(Interface
):
339 """Return the name of this Builder (a string)."""
342 # TODO: this isn't nearly as meaningful as it used to be
343 """Return a tuple (state, builds) for this Builder. 'state' is the
344 so-called 'big-status', indicating overall status (as opposed to
345 which step is currently running). It is a string, one of 'offline',
346 'idle', or 'building'. 'builds' is a list of IBuildStatus objects
347 (possibly empty) representing the currently active builds."""
350 """Return a list of ISlaveStatus objects for the buildslaves that are
351 used by this builder."""
353 def getPendingBuilds():
354 """Return an IBuildRequestStatus object for all upcoming builds
355 (those which are ready to go but which are waiting for a buildslave
358 def getCurrentBuilds():
359 """Return a list containing an IBuildStatus object for each build
360 currently in progress."""
361 # again, we could probably provide an object for 'waiting' and
362 # 'interlocked' too, but things like the Change list might still be
365 def getLastFinishedBuild():
366 """Return the IBuildStatus object representing the last finished
367 build, which may be None if the builder has not yet finished any
370 def getBuild(number
):
371 """Return an IBuildStatus object for a historical build. Each build
372 is numbered (starting at 0 when the Builder is first added),
373 getBuild(n) will retrieve the Nth such build. getBuild(-n) will
374 retrieve a recent build, with -1 being the most recent build
375 started. If the Builder is idle, this will be the same as
376 getLastFinishedBuild(). If the Builder is active, it will be an
377 unfinished build. This method will return None if the build is no
378 longer available. Older builds are likely to have less information
379 stored: Logs are the first to go, then Steps."""
381 def getEvent(number
):
382 """Return an IStatusEvent object for a recent Event. Builders
383 connecting and disconnecting are events, as are ping attempts.
384 getEvent(-1) will return the most recent event. Events are numbered,
385 but it probably doesn't make sense to ever do getEvent(+n)."""
387 def generateFinishedBuilds(branches
=[],
389 max_buildnum
=None, finished_before
=None,
392 """Return a generator that will produce IBuildStatus objects each
393 time you invoke its .next() method, starting with the most recent
394 finished build, then the previous build, and so on back to the oldest
397 @param branches: this is a list of branch names, and the generator
398 will only produce builds that involve the given
399 branches. If the list is empty, the generator will
400 produce all builds regardless of what branch they
403 @param num_builds: if provided, the generator will stop after
404 providing this many builds. The default of None
405 means to produce as many builds as possible.
407 @param max_buildnum: if provided, the generator will start by
408 providing the build with this number, or the
409 highest-numbered preceding build (i.e. the
410 generator will not produce any build numbered
411 *higher* than max_buildnum). The default of None
412 means to start with the most recent finished
413 build. -1 means the same as None. -2 means to
414 start with the next-most-recent completed build,
417 @type finished_before: int: a timestamp, seconds since the epoch
418 @param finished_before: if provided, do not produce any builds that
419 finished after the given timestamp.
421 @type max_search: int
422 @param max_search: this method may have to examine a lot of builds
423 to find some that match the search parameters,
424 especially if there aren't any matching builds.
425 This argument imposes a hard limit on the number
426 of builds that will be examined.
429 def subscribe(receiver
):
430 """Register an IStatusReceiver to receive new status events. The
431 receiver will be given builderChangedState, buildStarted, and
432 buildFinished messages."""
434 def unsubscribe(receiver
):
435 """Unregister an IStatusReceiver. No further status messgaes will be
438 class IEventSource(Interface
):
439 def eventGenerator(branches
=[]):
440 """This function creates a generator which will yield all of this
441 object's status events, starting with the most recent and progressing
442 backwards in time. These events provide the IStatusEvent interface.
443 At the moment they are all instances of buildbot.status.builder.Event
444 or buildbot.status.builder.BuildStepStatus .
446 @param branches: a list of branch names. The generator should only
447 return events that are associated with these branches. If the list is
448 empty, events for all branches should be returned (i.e. an empty list
449 means 'accept all' rather than 'accept none').
452 class IBuildStatus(Interface
):
453 """I represent the status of a single Build/BuildRequest. It could be
454 in-progress or finished."""
458 Return the BuilderStatus that owns this build.
460 @rtype: implementor of L{IBuilderStatus}
464 """Return a boolean. True means the build has finished, False means
465 it is still running."""
467 def waitUntilFinished():
468 """Return a Deferred that will fire when the build finishes. If the
469 build has already finished, this deferred will fire right away. The
470 callback is given this IBuildStatus instance as an argument."""
472 def getProperty(propname
):
473 """Return the value of the build property with the given name. Raises
474 KeyError if there is no such property on this build."""
477 """Return a string that indicates why the build was run. 'changes',
478 'forced', and 'periodic' are the most likely values. 'try' will be
479 added in the future."""
481 def getSourceStamp():
482 """Return a SourceStamp object which can be used to re-create
483 the source tree that this build used.
485 This method will return None if the source information is no longer
487 # TODO: it should be possible to expire the patch but still remember
488 # that the build was r123+something.
491 """Return a list of Change objects which represent which source
492 changes went into the build."""
494 def getResponsibleUsers():
495 """Return a list of Users who are to blame for the changes that went
496 into this build. If anything breaks (at least anything that wasn't
497 already broken), blame them. Specifically, this is the set of users
498 who were responsible for the Changes that went into this build. Each
499 User is a string, corresponding to their name as known by the VC
502 def getInterestedUsers():
503 """Return a list of Users who will want to know about the results of
504 this build. This is a superset of getResponsibleUsers(): it adds
505 people who are interested in this build but who did not actually
506 make the Changes that went into it (build sheriffs, code-domain
510 """Within each builder, each Build has a number. Return it."""
512 def getPreviousBuild():
513 """Convenience method. Returns None if the previous build is
517 """Return a list of IBuildStepStatus objects. For invariant builds
518 (those which always use the same set of Steps), this should always
519 return the complete list, however some of the steps may not have
520 started yet (step.getTimes()[0] will be None). For variant builds,
521 this may not be complete (asking again later may give you more of
525 """Returns a tuple of (start, end). 'start' and 'end' are the times
526 (seconds since the epoch) when the Build started and finished. If
527 the build is still running, 'end' will be None."""
529 # while the build is running, the following methods make sense.
530 # Afterwards they return None
533 """Returns the number of seconds from now in which the build is
534 expected to finish, or None if we can't make a guess. This guess will
535 be refined over time."""
537 def getCurrentStep():
538 """Return an IBuildStepStatus object representing the currently
541 # Once you know the build has finished, the following methods are legal.
542 # Before ths build has finished, they all return None.
545 """Return the name of the buildslave which handled this build."""
548 """Returns a list of strings to describe the build. These are
549 intended to be displayed in a narrow column. If more space is
550 available, the caller should join them together with spaces before
551 presenting them to the user."""
554 """Return a constant describing the results of the build: one of the
555 constants in buildbot.status.builder: SUCCESS, WARNINGS, or
559 """Return a list of logs that describe the build as a whole. Some
560 steps will contribute their logs, while others are are less important
561 and will only be accessible through the IBuildStepStatus objects.
562 Each log is an object which implements the IStatusLog interface."""
564 def getTestResults():
565 """Return a dictionary that maps test-name tuples to ITestResult
566 objects. This may return an empty or partially-filled dictionary
567 until the build has completed."""
569 # subscription interface
571 def subscribe(receiver
, updateInterval
=None):
572 """Register an IStatusReceiver to receive new status events. The
573 receiver will be given stepStarted and stepFinished messages. If
574 'updateInterval' is non-None, buildETAUpdate messages will be sent
575 every 'updateInterval' seconds."""
577 def unsubscribe(receiver
):
578 """Unregister an IStatusReceiver. No further status messgaes will be
581 class ITestResult(Interface
):
582 """I describe the results of a single unit test."""
585 """Returns a tuple of strings which make up the test name. Tests may
586 be arranged in a hierarchy, so looking for common prefixes may be
590 """Returns a constant describing the results of the test: SUCCESS,
591 WARNINGS, FAILURE."""
594 """Returns a list of short strings which describe the results of the
595 test in slightly more detail. Suggested components include
596 'failure', 'error', 'passed', 'timeout'."""
599 # in flux, it may be possible to provide more structured information
600 # like python Failure instances
601 """Returns a dictionary of test logs. The keys are strings like
602 'stdout', 'log', 'exceptions'. The values are strings."""
605 class IBuildStepStatus(Interface
):
606 """I hold status for a single BuildStep."""
609 """Returns a short string with the name of this step. This string
610 may have spaces in it."""
613 """Returns the IBuildStatus object which contains this step."""
616 """Returns a tuple of (start, end). 'start' and 'end' are the times
617 (seconds since the epoch) when the Step started and finished. If the
618 step has not yet started, 'start' will be None. If the step is still
619 running, 'end' will be None."""
621 def getExpectations():
622 """Returns a list of tuples (name, current, target). Each tuple
623 describes a single axis along which the step's progress can be
624 measured. 'name' is a string which describes the axis itself, like
625 'filesCompiled' or 'tests run' or 'bytes of output'. 'current' is a
626 number with the progress made so far, while 'target' is the value
627 that we expect (based upon past experience) to get to when the build
630 'current' will change over time until the step is finished. It is
631 'None' until the step starts. When the build is finished, 'current'
632 may or may not equal 'target' (which is merely the expectation based
633 upon previous builds)."""
636 """Returns a dictionary of URLs. Each key is a link name (a short
637 string, like 'results' or 'coverage'), and each value is a URL. These
638 links will be displayed along with the LogFiles.
642 """Returns a list of IStatusLog objects. If the step has not yet
643 finished, this list may be incomplete (asking again later may give
644 you more of them)."""
648 """Return a boolean. True means the step has finished, False means it
651 def waitUntilFinished():
652 """Return a Deferred that will fire when the step finishes. If the
653 step has already finished, this deferred will fire right away. The
654 callback is given this IBuildStepStatus instance as an argument."""
656 # while the step is running, the following methods make sense.
657 # Afterwards they return None
660 """Returns the number of seconds from now in which the step is
661 expected to finish, or None if we can't make a guess. This guess will
662 be refined over time."""
664 # Once you know the step has finished, the following methods are legal.
665 # Before ths step has finished, they all return None.
668 """Returns a list of strings which describe the step. These are
669 intended to be displayed in a narrow column. If more space is
670 available, the caller should join them together with spaces before
671 presenting them to the user."""
674 """Return a tuple describing the results of the step: (result,
675 strings). 'result' is one of the constants in
676 buildbot.status.builder: SUCCESS, WARNINGS, FAILURE, or SKIPPED.
677 'strings' is an optional list of strings that the step wants to
678 append to the overall build's results. These strings are usually
679 more terse than the ones returned by getText(): in particular,
680 successful Steps do not usually contribute any text to the overall
683 # subscription interface
685 def subscribe(receiver
, updateInterval
=10):
686 """Register an IStatusReceiver to receive new status events. The
687 receiver will be given logStarted and logFinished messages. It will
688 also be given a ETAUpdate message every 'updateInterval' seconds."""
690 def unsubscribe(receiver
):
691 """Unregister an IStatusReceiver. No further status messgaes will be
694 class IStatusEvent(Interface
):
695 """I represent a Builder Event, something non-Build related that can
696 happen to a Builder."""
699 """Returns a tuple of (start, end) like IBuildStepStatus, but end==0
700 indicates that this is a 'point event', which has no duration.
701 SlaveConnect/Disconnect are point events. Ping is not: it starts
702 when requested and ends when the response (positive or negative) is
706 """Returns a list of strings which describe the event. These are
707 intended to be displayed in a narrow column. If more space is
708 available, the caller should join them together with spaces before
709 presenting them to the user."""
712 LOG_CHANNEL_STDOUT
= 0
713 LOG_CHANNEL_STDERR
= 1
714 LOG_CHANNEL_HEADER
= 2
716 class IStatusLog(Interface
):
717 """I represent a single Log, which is a growing list of text items that
718 contains some kind of output for a single BuildStep. I might be finished,
719 in which case this list has stopped growing.
721 Each Log has a name, usually something boring like 'log' or 'output'.
722 These names are not guaranteed to be unique, however they are usually
723 chosen to be useful within the scope of a single step (i.e. the Compile
724 step might produce both 'log' and 'warnings'). The name may also have
725 spaces. If you want something more globally meaningful, at least within a
728 '%s.%s' % (log.getStep.getName(), log.getName())
730 The Log can be presented as plain text, or it can be accessed as a list
731 of items, each of which has a channel indicator (header, stdout, stderr)
732 and a text chunk. An HTML display might represent the interleaved
733 channels with different styles, while a straight download-the-text
734 interface would just want to retrieve a big string.
736 The 'header' channel is used by ShellCommands to prepend a note about
737 which command is about to be run ('running command FOO in directory
738 DIR'), and append another note giving the exit code of the process.
740 Logs can be streaming: if the Log has not yet finished, you can
741 subscribe to receive new chunks as they are added.
743 A ShellCommand will have a Log associated with it that gathers stdout
744 and stderr. Logs may also be created by parsing command output or
745 through other synthetic means (grepping for all the warnings in a
746 compile log, or listing all the test cases that are going to be run).
747 Such synthetic Logs are usually finished as soon as they are created."""
751 """Returns a short string with the name of this log, probably 'log'.
755 """Returns the IBuildStepStatus which owns this log."""
756 # TODO: can there be non-Step logs?
759 """Return a boolean. True means the log has finished and is closed,
760 False means it is still open and new chunks may be added to it."""
762 def waitUntilFinished():
763 """Return a Deferred that will fire when the log is closed. If the
764 log has already finished, this deferred will fire right away. The
765 callback is given this IStatusLog instance as an argument."""
767 def subscribe(receiver
, catchup
):
768 """Register an IStatusReceiver to receive chunks (with logChunk) as
769 data is added to the Log. If you use this, you will also want to use
770 waitUntilFinished to find out when the listener can be retired.
771 Subscribing to a closed Log is a no-op.
773 If 'catchup' is True, the receiver will immediately be sent a series
774 of logChunk messages to bring it up to date with the partially-filled
775 log. This allows a status client to join a Log already in progress
776 without missing any data. If the Log has already finished, it is too
777 late to catch up: just do getText() instead.
779 If the Log is very large, the receiver will be called many times with
780 a lot of data. There is no way to throttle this data. If the receiver
781 is planning on sending the data on to somewhere else, over a narrow
782 connection, you can get a throttleable subscription by using
783 C{subscribeConsumer} instead."""
785 def unsubscribe(receiver
):
786 """Remove a receiver previously registered with subscribe(). Attempts
787 to remove a receiver which was not previously registered is a no-op.
790 def subscribeConsumer(consumer
):
791 """Register an L{IStatusLogConsumer} to receive all chunks of the
792 logfile, including all the old entries and any that will arrive in
793 the future. The consumer will first have their C{registerProducer}
794 method invoked with a reference to an object that can be told
795 C{pauseProducing}, C{resumeProducing}, and C{stopProducing}. Then the
796 consumer's C{writeChunk} method will be called repeatedly with each
797 (channel, text) tuple in the log, starting with the very first. The
798 consumer will be notified with C{finish} when the log has been
799 exhausted (which can only happen when the log is finished). Note that
800 a small amount of data could be written via C{writeChunk} even after
801 C{pauseProducing} has been called.
803 To unsubscribe the consumer, use C{producer.stopProducing}."""
805 # once the log has finished, the following methods make sense. They can
806 # be called earlier, but they will only return the contents of the log up
807 # to the point at which they were called. You will lose items that are
808 # added later. Use C{subscribe} or C{subscribeConsumer} to avoid missing
812 """Returns True if the LogFile still has contents available. Returns
813 False for logs that have been pruned. Clients should test this before
814 offering to show the contents of any log."""
817 """Return one big string with the contents of the Log. This merges
818 all non-header chunks together."""
820 def readlines(channel
=LOG_CHANNEL_STDOUT
):
821 """Read lines from one channel of the logfile. This returns an
822 iterator that will provide single lines of text (including the
826 def getTextWithHeaders():
827 """Return one big string with the contents of the Log. This merges
828 all chunks (including headers) together."""
831 """Generate a list of (channel, text) tuples. 'channel' is a number,
832 0 for stdout, 1 for stderr, 2 for header. (note that stderr is merged
833 into stdout if PTYs are in use)."""
835 class IStatusLogConsumer(Interface
):
836 """I am an object which can be passed to IStatusLog.subscribeConsumer().
837 I represent a target for writing the contents of an IStatusLog. This
838 differs from a regular IStatusReceiver in that it can pause the producer.
839 This makes it more suitable for use in streaming data over network
840 sockets, such as an HTTP request. Note that the consumer can only pause
841 the producer until it has caught up with all the old data. After that
842 point, C{pauseProducing} is ignored and all new output from the log is
843 sent directoy to the consumer."""
845 def registerProducer(producer
, streaming
):
846 """A producer is being hooked up to this consumer. The consumer only
847 has to handle a single producer. It should send .pauseProducing and
848 .resumeProducing messages to the producer when it wants to stop or
849 resume the flow of data. 'streaming' will be set to True because the
850 producer is always a PushProducer.
853 def unregisterProducer():
854 """The previously-registered producer has been removed. No further
855 pauseProducing or resumeProducing calls should be made. The consumer
856 should delete its reference to the Producer so it can be released."""
858 def writeChunk(chunk
):
859 """A chunk (i.e. a tuple of (channel, text)) is being written to the
863 """The log has finished sending chunks to the consumer."""
865 class IStatusReceiver(Interface
):
866 """I am an object which can receive build status updates. I may be
867 subscribed to an IStatus, an IBuilderStatus, or an IBuildStatus."""
869 def buildsetSubmitted(buildset
):
870 """A new BuildSet has been submitted to the buildmaster.
872 @type buildset: implementor of L{IBuildSetStatus}
875 def builderAdded(builderName
, builder
):
877 A new Builder has just been added. This method may return an
878 IStatusReceiver (probably 'self') which will be subscribed to receive
879 builderChangedState and buildStarted/Finished events.
881 @type builderName: string
882 @type builder: L{buildbot.status.builder.BuilderStatus}
883 @rtype: implementor of L{IStatusReceiver}
886 def builderChangedState(builderName
, state
):
887 """Builder 'builderName' has changed state. The possible values for
888 'state' are 'offline', 'idle', and 'building'."""
890 def buildStarted(builderName
, build
):
891 """Builder 'builderName' has just started a build. The build is an
892 object which implements IBuildStatus, and can be queried for more
895 This method may return an IStatusReceiver (it could even return
896 'self'). If it does so, stepStarted and stepFinished methods will be
897 invoked on the object for the steps of this one build. This is a
898 convenient way to subscribe to all build steps without missing any.
899 This receiver will automatically be unsubscribed when the build
902 It can also return a tuple of (IStatusReceiver, interval), in which
903 case buildETAUpdate messages are sent ever 'interval' seconds, in
904 addition to the stepStarted and stepFinished messages."""
906 def buildETAUpdate(build
, ETA
):
907 """This is a periodic update on the progress this Build has made
908 towards completion."""
910 def stepStarted(build
, step
):
911 """A step has just started. 'step' is the IBuildStepStatus which
912 represents the step: it can be queried for more information.
914 This method may return an IStatusReceiver (it could even return
915 'self'). If it does so, logStarted and logFinished methods will be
916 invoked on the object for logs created by this one step. This
917 receiver will be automatically unsubscribed when the step finishes.
919 Alternatively, the method may return a tuple of an IStatusReceiver
920 and an integer named 'updateInterval'. In addition to
921 logStarted/logFinished messages, it will also receive stepETAUpdate
922 messages about every updateInterval seconds."""
924 def stepTextChanged(build
, step
, text
):
925 """The text for a step has been updated.
927 This is called when calling setText() on the step status, and
928 hands in the text list."""
930 def stepText2Changed(build
, step
, text2
):
931 """The text2 for a step has been updated.
933 This is called when calling setText2() on the step status, and
934 hands in text2 list."""
936 def stepETAUpdate(build
, step
, ETA
, expectations
):
937 """This is a periodic update on the progress this Step has made
938 towards completion. It gets an ETA (in seconds from the present) of
939 when the step ought to be complete, and a list of expectation tuples
940 (as returned by IBuildStepStatus.getExpectations) with more detailed
943 def logStarted(build
, step
, log
):
944 """A new Log has been started, probably because a step has just
945 started running a shell command. 'log' is the IStatusLog object
946 which can be queried for more information.
948 This method may return an IStatusReceiver (such as 'self'), in which
949 case the target's logChunk method will be invoked as text is added to
950 the logfile. This receiver will automatically be unsubsribed when the
953 def logChunk(build
, step
, log
, channel
, text
):
954 """Some text has been added to this log. 'channel' is one of
955 LOG_CHANNEL_STDOUT, LOG_CHANNEL_STDERR, or LOG_CHANNEL_HEADER, as
956 defined in IStatusLog.getChunks."""
958 def logFinished(build
, step
, log
):
959 """A Log has been closed."""
961 def stepFinished(build
, step
, results
):
962 """A step has just finished. 'results' is the result tuple described
963 in IBuildStepStatus.getResults."""
965 def buildFinished(builderName
, build
, results
):
967 A build has just finished. 'results' is the result tuple described
968 in L{IBuildStatus.getResults}.
970 @type builderName: string
971 @type build: L{buildbot.status.builder.BuildStatus}
975 def builderRemoved(builderName
):
976 """The Builder has been removed."""
978 class IControl(Interface
):
979 def addChange(change
):
980 """Add a change to all builders. Each Builder will decide for
981 themselves whether the change is interesting or not, and may initiate
982 a build as a result."""
984 def submitBuildSet(buildset
):
985 """Submit a BuildSet object, which will eventually be run on all of
986 the builders listed therein."""
988 def getBuilder(name
):
989 """Retrieve the IBuilderControl object for the given Builder."""
991 class IBuilderControl(Interface
):
992 def requestBuild(request
):
993 """Queue a L{buildbot.process.base.BuildRequest} object for later
996 def requestBuildSoon(request
):
997 """Submit a BuildRequest like requestBuild, but raise a
998 L{buildbot.interfaces.NoSlaveError} if no slaves are currently
999 available, so it cannot be used to queue a BuildRequest in the hopes
1000 that a slave will eventually connect. This method is appropriate for
1001 use by things like the web-page 'Force Build' button."""
1003 def resubmitBuild(buildStatus
, reason
="<rebuild, no reason given>"):
1004 """Rebuild something we've already built before. This submits a
1005 BuildRequest to our Builder using the same SourceStamp as the earlier
1006 build. This has no effect (but may eventually raise an exception) if
1007 this Build has not yet finished."""
1009 def getPendingBuilds():
1010 """Return a list of L{IBuildRequestControl} objects for this Builder.
1011 Each one corresponds to a pending build that has not yet started (due
1012 to a scarcity of build slaves). These upcoming builds can be canceled
1013 through the control object."""
1015 def getBuild(number
):
1016 """Attempt to return an IBuildControl object for the given build.
1017 Returns None if no such object is available. This will only work for
1018 the build that is currently in progress: once the build finishes,
1019 there is nothing to control anymore."""
1021 def ping(timeout
=30):
1022 """Attempt to contact the slave and see if it is still alive. This
1023 returns a Deferred which fires with either True (the slave is still
1024 alive) or False (the slave did not respond). As a side effect, adds
1025 an event to this builder's column in the waterfall display
1026 containing the results of the ping."""
1027 # TODO: this ought to live in ISlaveControl, maybe with disconnect()
1028 # or something. However the event that is emitted is most useful in
1029 # the Builder column, so it kinda fits here too.
1031 class IBuildRequestControl(Interface
):
1032 def subscribe(observer
):
1033 """Register a callable that will be invoked (with a single
1034 IBuildControl object) for each Build that is created to satisfy this
1035 request. There may be multiple Builds created in an attempt to handle
1036 the request: they may be interrupted by the user or abandoned due to
1037 a lost slave. The last Build (the one which actually gets to run to
1038 completion) is said to 'satisfy' the BuildRequest. The observer will
1039 be called once for each of these Builds, both old and new."""
1040 def unsubscribe(observer
):
1041 """Unregister the callable that was registered with subscribe()."""
1043 """Remove the build from the pending queue. Has no effect if the
1044 build has already been started."""
1046 class IBuildControl(Interface
):
1048 """Return an IBuildStatus object for the Build that I control."""
1049 def stopBuild(reason
="<no reason given>"):
1050 """Halt the build. This has no effect if the build has already
1053 class ILogFile(Interface
):
1054 """This is the internal interface to a LogFile, used by the BuildStep to
1055 write data into the log.
1057 def addStdout(data
):
1059 def addStderr(data
):
1061 def addHeader(data
):
1064 """The process that is feeding the log file has finished, and no
1065 further data will be added. This closes the logfile."""
1067 class ILogObserver(Interface
):
1068 """Objects which provide this interface can be used in a BuildStep to
1069 watch the output of a LogFile and parse it incrementally.
1078 # methods called by the LogFile
1079 def logChunk(build
, step
, log
, channel
, text
):
1082 class IBuildSlave(Interface
):
1083 # this is a marker interface for the BuildSlave class
1086 class ILatentBuildSlave(IBuildSlave
):
1087 """A build slave that is not always running, but can run when requested.
1089 substantiated
= Attribute('Substantiated',
1090 'Whether the latent build slave is currently '
1091 'substantiated with a real instance.')
1094 """Request that the slave substantiate with a real instance.
1096 Returns a deferred that will callback when a real instance has
1099 # there is an insubstantiate too, but that is not used externally ATM.
1101 def buildStarted(sb
):
1102 """Inform the latent build slave that a build has started.
1104 ``sb`` is a LatentSlaveBuilder as defined in buildslave.py. The sb
1105 is the one for whom the build started.
1108 def buildFinished(sb
):
1109 """Inform the latent build slave that a build has finished.
1111 ``sb`` is a LatentSlaveBuilder as defined in buildslave.py. The sb
1112 is the one for whom the build finished.