OLD | NEW |
| (Empty) |
1 | |
2 """Interface documentation. | |
3 | |
4 Define the interfaces that are implemented by various buildbot classes. | |
5 """ | |
6 | |
7 from zope.interface import Interface, Attribute | |
8 | |
9 # exceptions that can be raised while trying to start a build | |
10 class NoSlaveError(Exception): | |
11 pass | |
12 class BuilderInUseError(Exception): | |
13 pass | |
14 class BuildSlaveTooOldError(Exception): | |
15 pass | |
16 class LatentBuildSlaveFailedToSubstantiate(Exception): | |
17 pass | |
18 | |
19 # other exceptions | |
20 class BuildbotNotRunningError(Exception): | |
21 pass | |
22 | |
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 | |
27 and pass it through:: | |
28 | |
29 self.changemaster.addChange(change) | |
30 """ | |
31 | |
32 def start(): | |
33 """Called when the buildmaster starts. Can be used to establish | |
34 connections to VC daemons or begin polling.""" | |
35 | |
36 def stop(): | |
37 """Called when the buildmaster shuts down. Connections should be | |
38 terminated, polling timers should be canceled.""" | |
39 | |
40 def describe(): | |
41 """Should return a string which briefly describes this source. This | |
42 string will be displayed in an HTML status page.""" | |
43 | |
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. | |
48 | |
49 @ivar properties: properties to be applied to all builds started by this | |
50 scheduler | |
51 @type properties: L<buildbot.process.properties.Properties> | |
52 """ | |
53 | |
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.""" | |
58 | |
59 def listBuilderNames(): | |
60 """Return a list of strings indicating the Builders that this | |
61 Scheduler might feed.""" | |
62 | |
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. | |
71 | |
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.""" | |
75 | |
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.""" | |
80 | |
81 def listBuilderNames(): | |
82 """Return a list of strings indicating the Builders that this | |
83 Scheduler might feed.""" | |
84 | |
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.""" | |
89 | |
90 def checkUpstreamScheduler(): | |
91 """Check if the upstream scheduler is still alive, and if not, | |
92 get a new upstream object from the master.""" | |
93 | |
94 | |
95 class ISourceStamp(Interface): | |
96 """ | |
97 @cvar branch: branch from which source was drawn | |
98 @type branch: string or None | |
99 | |
100 @cvar revision: revision of the source, or None to use CHANGES | |
101 @type revision: varies depending on VC | |
102 | |
103 @cvar patch: patch applied to the source, or None if no patch | |
104 @type patch: None or tuple (level diff) | |
105 | |
106 @cvar changes: the source step should check out hte latest revision | |
107 in the given changes | |
108 @type changes: tuple of L{buildbot.changes.changes.Change} instances, | |
109 all of which are on the same branch | |
110 """ | |
111 | |
112 def canBeMergedWith(self, other): | |
113 """ | |
114 Can this SourceStamp be merged with OTHER? | |
115 """ | |
116 | |
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.""" | |
121 | |
122 def getAbsoluteSourceStamp(self, got_revision): | |
123 """Get a new SourceStamp object reflecting the actual revision found | |
124 by a Source step.""" | |
125 | |
126 def getText(self): | |
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.""" | |
131 | |
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.""" | |
135 pass | |
136 | |
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.""" | |
142 | |
143 class IStatus(Interface): | |
144 """I am an object, obtainable from the buildmaster, which can provide | |
145 status information.""" | |
146 | |
147 def getProjectName(): | |
148 """Return the name of the project that this Buildbot is working | |
149 for.""" | |
150 def getProjectURL(): | |
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).""" | |
160 | |
161 def getChangeSources(): | |
162 """Return a list of IChangeSource objects.""" | |
163 | |
164 def getChange(number): | |
165 """Return an IChange object.""" | |
166 | |
167 def getSchedulers(): | |
168 """Return a list of ISchedulerStatus objects for all | |
169 currently-registered Schedulers.""" | |
170 | |
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.""" | |
176 | |
177 def getSlaveNames(): | |
178 """Return a list of buildslave names, suitable for passing to | |
179 getSlave().""" | |
180 def getSlave(name): | |
181 """Return the ISlaveStatus object for a given named buildslave.""" | |
182 | |
183 def getBuildSets(): | |
184 """Return a list of active (non-finished) IBuildSetStatus objects.""" | |
185 | |
186 def generateFinishedBuilds(builders=[], branches=[], | |
187 num_builds=None, finished_before=None, | |
188 max_search=200): | |
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. | |
192 | |
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 | |
196 all Builders. | |
197 | |
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 | |
201 all branches. | |
202 | |
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. | |
206 | |
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. | |
210 | |
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 | |
217 Builder. | |
218 """ | |
219 | |
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.""" | |
230 | |
231 def unsubscribe(receiver): | |
232 """Unregister an IStatusReceiver. No further status messgaes will be | |
233 delivered.""" | |
234 | |
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.""" | |
238 | |
239 def getSourceStamp(): | |
240 """Return a SourceStamp object which can be used to re-create | |
241 the source tree that this build used. | |
242 | |
243 This method will return None if the source information is no longer | |
244 available.""" | |
245 pass | |
246 def getReason(): | |
247 pass | |
248 def getID(): | |
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 | |
258 do builds.""" | |
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.""" | |
264 def isFinished(): | |
265 pass | |
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.""" | |
273 def getResults(): | |
274 pass | |
275 | |
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.""" | |
280 | |
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. | |
288 | |
289 This method will return None if the source information is no longer | |
290 available.""" | |
291 pass | |
292 def getBuilderName(): | |
293 pass | |
294 def getBuilds(): | |
295 """Return a list of IBuildStatus objects for each Build that has been | |
296 started in an attempt to satify this BuildRequest.""" | |
297 | |
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().""" | |
308 def getSubmitTime(): | |
309 """Return the time when this request was submitted""" | |
310 def setSubmitTime(t): | |
311 """Sets the time when this request was submitted""" | |
312 | |
313 | |
314 class ISlaveStatus(Interface): | |
315 def getName(): | |
316 """Return the name of the build slave.""" | |
317 | |
318 def getAdmin(): | |
319 """Return a string with the slave admin's contact data.""" | |
320 | |
321 def getHost(): | |
322 """Return a string with the slave host info.""" | |
323 | |
324 def isConnected(): | |
325 """Return True if the slave is currently online, False if not.""" | |
326 | |
327 def lastMessageReceived(): | |
328 """Return a timestamp (seconds since epoch) indicating when the most | |
329 recent message was received from the buildslave.""" | |
330 | |
331 class ISchedulerStatus(Interface): | |
332 def getName(): | |
333 """Return the name of this Scheduler (a string).""" | |
334 | |
335 def getPendingBuildsets(): | |
336 """Return an IBuildSet for all BuildSets that are pending. These | |
337 BuildSets are waiting for their tree-stable-timers to expire.""" | |
338 # TODO: this is not implemented anywhere | |
339 | |
340 | |
341 class IBuilderStatus(Interface): | |
342 def getName(): | |
343 """Return the name of this Builder (a string).""" | |
344 | |
345 def getCategory(): | |
346 """Return the category of this builder (a string).""" | |
347 | |
348 def getState(): | |
349 # TODO: this isn't nearly as meaningful as it used to be | |
350 """Return a tuple (state, builds) for this Builder. 'state' is the | |
351 so-called 'big-status', indicating overall status (as opposed to | |
352 which step is currently running). It is a string, one of 'offline', | |
353 'idle', or 'building'. 'builds' is a list of IBuildStatus objects | |
354 (possibly empty) representing the currently active builds.""" | |
355 | |
356 def getSlaves(): | |
357 """Return a list of ISlaveStatus objects for the buildslaves that are | |
358 used by this builder.""" | |
359 | |
360 def getPendingBuilds(): | |
361 """Return an IBuildRequestStatus object for all upcoming builds | |
362 (those which are ready to go but which are waiting for a buildslave | |
363 to be available.""" | |
364 | |
365 def getCurrentBuilds(): | |
366 """Return a list containing an IBuildStatus object for each build | |
367 currently in progress.""" | |
368 # again, we could probably provide an object for 'waiting' and | |
369 # 'interlocked' too, but things like the Change list might still be | |
370 # subject to change | |
371 | |
372 def getLastFinishedBuild(): | |
373 """Return the IBuildStatus object representing the last finished | |
374 build, which may be None if the builder has not yet finished any | |
375 builds.""" | |
376 | |
377 def getBuild(number): | |
378 """Return an IBuildStatus object for a historical build. Each build | |
379 is numbered (starting at 0 when the Builder is first added), | |
380 getBuild(n) will retrieve the Nth such build. getBuild(-n) will | |
381 retrieve a recent build, with -1 being the most recent build | |
382 started. If the Builder is idle, this will be the same as | |
383 getLastFinishedBuild(). If the Builder is active, it will be an | |
384 unfinished build. This method will return None if the build is no | |
385 longer available. Older builds are likely to have less information | |
386 stored: Logs are the first to go, then Steps.""" | |
387 | |
388 def getEvent(number): | |
389 """Return an IStatusEvent object for a recent Event. Builders | |
390 connecting and disconnecting are events, as are ping attempts. | |
391 getEvent(-1) will return the most recent event. Events are numbered, | |
392 but it probably doesn't make sense to ever do getEvent(+n).""" | |
393 | |
394 def generateFinishedBuilds(branches=[], | |
395 num_builds=None, | |
396 max_buildnum=None, finished_before=None, | |
397 max_search=200, | |
398 ): | |
399 """Return a generator that will produce IBuildStatus objects each | |
400 time you invoke its .next() method, starting with the most recent | |
401 finished build, then the previous build, and so on back to the oldest | |
402 build available. | |
403 | |
404 @param branches: this is a list of branch names, and the generator | |
405 will only produce builds that involve the given | |
406 branches. If the list is empty, the generator will | |
407 produce all builds regardless of what branch they | |
408 used. | |
409 | |
410 @param num_builds: if provided, the generator will stop after | |
411 providing this many builds. The default of None | |
412 means to produce as many builds as possible. | |
413 | |
414 @param max_buildnum: if provided, the generator will start by | |
415 providing the build with this number, or the | |
416 highest-numbered preceding build (i.e. the | |
417 generator will not produce any build numbered | |
418 *higher* than max_buildnum). The default of None | |
419 means to start with the most recent finished | |
420 build. -1 means the same as None. -2 means to | |
421 start with the next-most-recent completed build, | |
422 etc. | |
423 | |
424 @type finished_before: int: a timestamp, seconds since the epoch | |
425 @param finished_before: if provided, do not produce any builds that | |
426 finished after the given timestamp. | |
427 | |
428 @type max_search: int | |
429 @param max_search: this method may have to examine a lot of builds | |
430 to find some that match the search parameters, | |
431 especially if there aren't any matching builds. | |
432 This argument imposes a hard limit on the number | |
433 of builds that will be examined. | |
434 """ | |
435 | |
436 def subscribe(receiver): | |
437 """Register an IStatusReceiver to receive new status events. The | |
438 receiver will be given builderChangedState, buildStarted, and | |
439 buildFinished messages.""" | |
440 | |
441 def unsubscribe(receiver): | |
442 """Unregister an IStatusReceiver. No further status messgaes will be | |
443 delivered.""" | |
444 | |
445 class IEventSource(Interface): | |
446 def eventGenerator(branches=[], categories=[], committers=[], minTime=0): | |
447 """This function creates a generator which will yield all of this | |
448 object's status events, starting with the most recent and progressing | |
449 backwards in time. These events provide the IStatusEvent interface. | |
450 At the moment they are all instances of buildbot.status.builder.Event | |
451 or buildbot.status.builder.BuildStepStatus . | |
452 | |
453 @param branches: a list of branch names. The generator should only | |
454 return events that are associated with these branches. If the list is | |
455 empty, events for all branches should be returned (i.e. an empty list | |
456 means 'accept all' rather than 'accept none'). | |
457 | |
458 @param categories: a list of category names. The generator | |
459 should only return events that are categorized within the | |
460 given category. If the list is empty, events for all | |
461 categories should be returned. | |
462 | |
463 @param comitters: a list of committers. The generator should only | |
464 return events caused by one of the listed committers. If the list is | |
465 empty or None, events from every committers should be returned. | |
466 | |
467 @param minTime: a timestamp. Do not generate events occuring prior to | |
468 this timestamp. | |
469 """ | |
470 | |
471 class IBuildStatus(Interface): | |
472 """I represent the status of a single Build/BuildRequest. It could be | |
473 in-progress or finished.""" | |
474 | |
475 def getBuilder(): | |
476 """ | |
477 Return the BuilderStatus that owns this build. | |
478 | |
479 @rtype: implementor of L{IBuilderStatus} | |
480 """ | |
481 | |
482 def isFinished(): | |
483 """Return a boolean. True means the build has finished, False means | |
484 it is still running.""" | |
485 | |
486 def waitUntilFinished(): | |
487 """Return a Deferred that will fire when the build finishes. If the | |
488 build has already finished, this deferred will fire right away. The | |
489 callback is given this IBuildStatus instance as an argument.""" | |
490 | |
491 def getProperty(propname): | |
492 """Return the value of the build property with the given name. Raises | |
493 KeyError if there is no such property on this build.""" | |
494 | |
495 def getReason(): | |
496 """Return a string that indicates why the build was run. 'changes', | |
497 'forced', and 'periodic' are the most likely values. 'try' will be | |
498 added in the future.""" | |
499 | |
500 def getSourceStamp(): | |
501 """Return a SourceStamp object which can be used to re-create | |
502 the source tree that this build used. | |
503 | |
504 This method will return None if the source information is no longer | |
505 available.""" | |
506 # TODO: it should be possible to expire the patch but still remember | |
507 # that the build was r123+something. | |
508 | |
509 def getChanges(): | |
510 """Return a list of Change objects which represent which source | |
511 changes went into the build.""" | |
512 | |
513 def getResponsibleUsers(): | |
514 """Return a list of Users who are to blame for the changes that went | |
515 into this build. If anything breaks (at least anything that wasn't | |
516 already broken), blame them. Specifically, this is the set of users | |
517 who were responsible for the Changes that went into this build. Each | |
518 User is a string, corresponding to their name as known by the VC | |
519 repository.""" | |
520 | |
521 def getInterestedUsers(): | |
522 """Return a list of Users who will want to know about the results of | |
523 this build. This is a superset of getResponsibleUsers(): it adds | |
524 people who are interested in this build but who did not actually | |
525 make the Changes that went into it (build sheriffs, code-domain | |
526 owners).""" | |
527 | |
528 def getNumber(): | |
529 """Within each builder, each Build has a number. Return it.""" | |
530 | |
531 def getPreviousBuild(): | |
532 """Convenience method. Returns None if the previous build is | |
533 unavailable.""" | |
534 | |
535 def getSteps(): | |
536 """Return a list of IBuildStepStatus objects. For invariant builds | |
537 (those which always use the same set of Steps), this should always | |
538 return the complete list, however some of the steps may not have | |
539 started yet (step.getTimes()[0] will be None). For variant builds, | |
540 this may not be complete (asking again later may give you more of | |
541 them).""" | |
542 | |
543 def getTimes(): | |
544 """Returns a tuple of (start, end). 'start' and 'end' are the times | |
545 (seconds since the epoch) when the Build started and finished. If | |
546 the build is still running, 'end' will be None.""" | |
547 | |
548 # while the build is running, the following methods make sense. | |
549 # Afterwards they return None | |
550 | |
551 def getETA(): | |
552 """Returns the number of seconds from now in which the build is | |
553 expected to finish, or None if we can't make a guess. This guess will | |
554 be refined over time.""" | |
555 | |
556 def getCurrentStep(): | |
557 """Return an IBuildStepStatus object representing the currently | |
558 active step.""" | |
559 | |
560 # Once you know the build has finished, the following methods are legal. | |
561 # Before ths build has finished, they all return None. | |
562 | |
563 def getSlavename(): | |
564 """Return the name of the buildslave which handled this build.""" | |
565 | |
566 def getText(): | |
567 """Returns a list of strings to describe the build. These are | |
568 intended to be displayed in a narrow column. If more space is | |
569 available, the caller should join them together with spaces before | |
570 presenting them to the user.""" | |
571 | |
572 def getResults(): | |
573 """Return a constant describing the results of the build: one of the | |
574 constants in buildbot.status.builder: SUCCESS, WARNINGS, | |
575 FAILURE, SKIPPED or EXCEPTION.""" | |
576 | |
577 def getLogs(): | |
578 """Return a list of logs that describe the build as a whole. Some | |
579 steps will contribute their logs, while others are are less important | |
580 and will only be accessible through the IBuildStepStatus objects. | |
581 Each log is an object which implements the IStatusLog interface.""" | |
582 | |
583 def getTestResults(): | |
584 """Return a dictionary that maps test-name tuples to ITestResult | |
585 objects. This may return an empty or partially-filled dictionary | |
586 until the build has completed.""" | |
587 | |
588 # subscription interface | |
589 | |
590 def subscribe(receiver, updateInterval=None): | |
591 """Register an IStatusReceiver to receive new status events. The | |
592 receiver will be given stepStarted and stepFinished messages. If | |
593 'updateInterval' is non-None, buildETAUpdate messages will be sent | |
594 every 'updateInterval' seconds.""" | |
595 | |
596 def unsubscribe(receiver): | |
597 """Unregister an IStatusReceiver. No further status messgaes will be | |
598 delivered.""" | |
599 | |
600 class ITestResult(Interface): | |
601 """I describe the results of a single unit test.""" | |
602 | |
603 def getName(): | |
604 """Returns a tuple of strings which make up the test name. Tests may | |
605 be arranged in a hierarchy, so looking for common prefixes may be | |
606 useful.""" | |
607 | |
608 def getResults(): | |
609 """Returns a constant describing the results of the test: SUCCESS, | |
610 WARNINGS, FAILURE.""" | |
611 | |
612 def getText(): | |
613 """Returns a list of short strings which describe the results of the | |
614 test in slightly more detail. Suggested components include | |
615 'failure', 'error', 'passed', 'timeout'.""" | |
616 | |
617 def getLogs(): | |
618 # in flux, it may be possible to provide more structured information | |
619 # like python Failure instances | |
620 """Returns a dictionary of test logs. The keys are strings like | |
621 'stdout', 'log', 'exceptions'. The values are strings.""" | |
622 | |
623 | |
624 class IBuildStepStatus(Interface): | |
625 """I hold status for a single BuildStep.""" | |
626 | |
627 def getName(): | |
628 """Returns a short string with the name of this step. This string | |
629 may have spaces in it.""" | |
630 | |
631 def getBuild(): | |
632 """Returns the IBuildStatus object which contains this step.""" | |
633 | |
634 def getTimes(): | |
635 """Returns a tuple of (start, end). 'start' and 'end' are the times | |
636 (seconds since the epoch) when the Step started and finished. If the | |
637 step has not yet started, 'start' will be None. If the step is still | |
638 running, 'end' will be None.""" | |
639 | |
640 def getExpectations(): | |
641 """Returns a list of tuples (name, current, target). Each tuple | |
642 describes a single axis along which the step's progress can be | |
643 measured. 'name' is a string which describes the axis itself, like | |
644 'filesCompiled' or 'tests run' or 'bytes of output'. 'current' is a | |
645 number with the progress made so far, while 'target' is the value | |
646 that we expect (based upon past experience) to get to when the build | |
647 is finished. | |
648 | |
649 'current' will change over time until the step is finished. It is | |
650 'None' until the step starts. When the build is finished, 'current' | |
651 may or may not equal 'target' (which is merely the expectation based | |
652 upon previous builds).""" | |
653 | |
654 def getURLs(): | |
655 """Returns a dictionary of URLs. Each key is a link name (a short | |
656 string, like 'results' or 'coverage'), and each value is a URL. These | |
657 links will be displayed along with the LogFiles. | |
658 """ | |
659 | |
660 def getLogs(): | |
661 """Returns a list of IStatusLog objects. If the step has not yet | |
662 finished, this list may be incomplete (asking again later may give | |
663 you more of them).""" | |
664 | |
665 | |
666 def isFinished(): | |
667 """Return a boolean. True means the step has finished, False means it | |
668 is still running.""" | |
669 | |
670 def waitUntilFinished(): | |
671 """Return a Deferred that will fire when the step finishes. If the | |
672 step has already finished, this deferred will fire right away. The | |
673 callback is given this IBuildStepStatus instance as an argument.""" | |
674 | |
675 # while the step is running, the following methods make sense. | |
676 # Afterwards they return None | |
677 | |
678 def getETA(): | |
679 """Returns the number of seconds from now in which the step is | |
680 expected to finish, or None if we can't make a guess. This guess will | |
681 be refined over time.""" | |
682 | |
683 # Once you know the step has finished, the following methods are legal. | |
684 # Before ths step has finished, they all return None. | |
685 | |
686 def getText(): | |
687 """Returns a list of strings which describe the step. These are | |
688 intended to be displayed in a narrow column. If more space is | |
689 available, the caller should join them together with spaces before | |
690 presenting them to the user.""" | |
691 | |
692 def getResults(): | |
693 """Return a tuple describing the results of the step: (result, | |
694 strings). 'result' is one of the constants in | |
695 buildbot.status.builder: SUCCESS, WARNINGS, FAILURE, or SKIPPED. | |
696 'strings' is an optional list of strings that the step wants to | |
697 append to the overall build's results. These strings are usually | |
698 more terse than the ones returned by getText(): in particular, | |
699 successful Steps do not usually contribute any text to the overall | |
700 build.""" | |
701 | |
702 # subscription interface | |
703 | |
704 def subscribe(receiver, updateInterval=10): | |
705 """Register an IStatusReceiver to receive new status events. The | |
706 receiver will be given logStarted and logFinished messages. It will | |
707 also be given a ETAUpdate message every 'updateInterval' seconds.""" | |
708 | |
709 def unsubscribe(receiver): | |
710 """Unregister an IStatusReceiver. No further status messgaes will be | |
711 delivered.""" | |
712 | |
713 class IStatusEvent(Interface): | |
714 """I represent a Builder Event, something non-Build related that can | |
715 happen to a Builder.""" | |
716 | |
717 def getTimes(): | |
718 """Returns a tuple of (start, end) like IBuildStepStatus, but end==0 | |
719 indicates that this is a 'point event', which has no duration. | |
720 SlaveConnect/Disconnect are point events. Ping is not: it starts | |
721 when requested and ends when the response (positive or negative) is | |
722 returned""" | |
723 | |
724 def getText(): | |
725 """Returns a list of strings which describe the event. These are | |
726 intended to be displayed in a narrow column. If more space is | |
727 available, the caller should join them together with spaces before | |
728 presenting them to the user.""" | |
729 | |
730 | |
731 LOG_CHANNEL_STDOUT = 0 | |
732 LOG_CHANNEL_STDERR = 1 | |
733 LOG_CHANNEL_HEADER = 2 | |
734 | |
735 class IStatusLog(Interface): | |
736 """I represent a single Log, which is a growing list of text items that | |
737 contains some kind of output for a single BuildStep. I might be finished, | |
738 in which case this list has stopped growing. | |
739 | |
740 Each Log has a name, usually something boring like 'log' or 'output'. | |
741 These names are not guaranteed to be unique, however they are usually | |
742 chosen to be useful within the scope of a single step (i.e. the Compile | |
743 step might produce both 'log' and 'warnings'). The name may also have | |
744 spaces. If you want something more globally meaningful, at least within a | |
745 given Build, try:: | |
746 | |
747 '%s.%s' % (log.getStep.getName(), log.getName()) | |
748 | |
749 The Log can be presented as plain text, or it can be accessed as a list | |
750 of items, each of which has a channel indicator (header, stdout, stderr) | |
751 and a text chunk. An HTML display might represent the interleaved | |
752 channels with different styles, while a straight download-the-text | |
753 interface would just want to retrieve a big string. | |
754 | |
755 The 'header' channel is used by ShellCommands to prepend a note about | |
756 which command is about to be run ('running command FOO in directory | |
757 DIR'), and append another note giving the exit code of the process. | |
758 | |
759 Logs can be streaming: if the Log has not yet finished, you can | |
760 subscribe to receive new chunks as they are added. | |
761 | |
762 A ShellCommand will have a Log associated with it that gathers stdout | |
763 and stderr. Logs may also be created by parsing command output or | |
764 through other synthetic means (grepping for all the warnings in a | |
765 compile log, or listing all the test cases that are going to be run). | |
766 Such synthetic Logs are usually finished as soon as they are created.""" | |
767 | |
768 | |
769 def getName(): | |
770 """Returns a short string with the name of this log, probably 'log'. | |
771 """ | |
772 | |
773 def getStep(): | |
774 """Returns the IBuildStepStatus which owns this log.""" | |
775 # TODO: can there be non-Step logs? | |
776 | |
777 def isFinished(): | |
778 """Return a boolean. True means the log has finished and is closed, | |
779 False means it is still open and new chunks may be added to it.""" | |
780 | |
781 def waitUntilFinished(): | |
782 """Return a Deferred that will fire when the log is closed. If the | |
783 log has already finished, this deferred will fire right away. The | |
784 callback is given this IStatusLog instance as an argument.""" | |
785 | |
786 def subscribe(receiver, catchup): | |
787 """Register an IStatusReceiver to receive chunks (with logChunk) as | |
788 data is added to the Log. If you use this, you will also want to use | |
789 waitUntilFinished to find out when the listener can be retired. | |
790 Subscribing to a closed Log is a no-op. | |
791 | |
792 If 'catchup' is True, the receiver will immediately be sent a series | |
793 of logChunk messages to bring it up to date with the partially-filled | |
794 log. This allows a status client to join a Log already in progress | |
795 without missing any data. If the Log has already finished, it is too | |
796 late to catch up: just do getText() instead. | |
797 | |
798 If the Log is very large, the receiver will be called many times with | |
799 a lot of data. There is no way to throttle this data. If the receiver | |
800 is planning on sending the data on to somewhere else, over a narrow | |
801 connection, you can get a throttleable subscription by using | |
802 C{subscribeConsumer} instead.""" | |
803 | |
804 def unsubscribe(receiver): | |
805 """Remove a receiver previously registered with subscribe(). Attempts | |
806 to remove a receiver which was not previously registered is a no-op. | |
807 """ | |
808 | |
809 def subscribeConsumer(consumer): | |
810 """Register an L{IStatusLogConsumer} to receive all chunks of the | |
811 logfile, including all the old entries and any that will arrive in | |
812 the future. The consumer will first have their C{registerProducer} | |
813 method invoked with a reference to an object that can be told | |
814 C{pauseProducing}, C{resumeProducing}, and C{stopProducing}. Then the | |
815 consumer's C{writeChunk} method will be called repeatedly with each | |
816 (channel, text) tuple in the log, starting with the very first. The | |
817 consumer will be notified with C{finish} when the log has been | |
818 exhausted (which can only happen when the log is finished). Note that | |
819 a small amount of data could be written via C{writeChunk} even after | |
820 C{pauseProducing} has been called. | |
821 | |
822 To unsubscribe the consumer, use C{producer.stopProducing}.""" | |
823 | |
824 # once the log has finished, the following methods make sense. They can | |
825 # be called earlier, but they will only return the contents of the log up | |
826 # to the point at which they were called. You will lose items that are | |
827 # added later. Use C{subscribe} or C{subscribeConsumer} to avoid missing | |
828 # anything. | |
829 | |
830 def hasContents(): | |
831 """Returns True if the LogFile still has contents available. Returns | |
832 False for logs that have been pruned. Clients should test this before | |
833 offering to show the contents of any log.""" | |
834 | |
835 def getText(): | |
836 """Return one big string with the contents of the Log. This merges | |
837 all non-header chunks together.""" | |
838 | |
839 def readlines(channel=LOG_CHANNEL_STDOUT): | |
840 """Read lines from one channel of the logfile. This returns an | |
841 iterator that will provide single lines of text (including the | |
842 trailing newline). | |
843 """ | |
844 | |
845 def getTextWithHeaders(): | |
846 """Return one big string with the contents of the Log. This merges | |
847 all chunks (including headers) together.""" | |
848 | |
849 def getChunks(): | |
850 """Generate a list of (channel, text) tuples. 'channel' is a number, | |
851 0 for stdout, 1 for stderr, 2 for header. (note that stderr is merged | |
852 into stdout if PTYs are in use).""" | |
853 | |
854 class IStatusLogConsumer(Interface): | |
855 """I am an object which can be passed to IStatusLog.subscribeConsumer(). | |
856 I represent a target for writing the contents of an IStatusLog. This | |
857 differs from a regular IStatusReceiver in that it can pause the producer. | |
858 This makes it more suitable for use in streaming data over network | |
859 sockets, such as an HTTP request. Note that the consumer can only pause | |
860 the producer until it has caught up with all the old data. After that | |
861 point, C{pauseProducing} is ignored and all new output from the log is | |
862 sent directoy to the consumer.""" | |
863 | |
864 def registerProducer(producer, streaming): | |
865 """A producer is being hooked up to this consumer. The consumer only | |
866 has to handle a single producer. It should send .pauseProducing and | |
867 .resumeProducing messages to the producer when it wants to stop or | |
868 resume the flow of data. 'streaming' will be set to True because the | |
869 producer is always a PushProducer. | |
870 """ | |
871 | |
872 def unregisterProducer(): | |
873 """The previously-registered producer has been removed. No further | |
874 pauseProducing or resumeProducing calls should be made. The consumer | |
875 should delete its reference to the Producer so it can be released.""" | |
876 | |
877 def writeChunk(chunk): | |
878 """A chunk (i.e. a tuple of (channel, text)) is being written to the | |
879 consumer.""" | |
880 | |
881 def finish(): | |
882 """The log has finished sending chunks to the consumer.""" | |
883 | |
884 class IStatusReceiver(Interface): | |
885 """I am an object which can receive build status updates. I may be | |
886 subscribed to an IStatus, an IBuilderStatus, or an IBuildStatus.""" | |
887 | |
888 def buildsetSubmitted(buildset): | |
889 """A new BuildSet has been submitted to the buildmaster. | |
890 | |
891 @type buildset: implementor of L{IBuildSetStatus} | |
892 """ | |
893 | |
894 def requestSubmitted(request): | |
895 """A new BuildRequest has been submitted to the buildmaster. | |
896 | |
897 @type request: implementor of L{IBuildRequestStatus} | |
898 """ | |
899 | |
900 def requestCancelled(builder, request): | |
901 """A BuildRequest has been cancelled on the given Builder. | |
902 | |
903 @type builder: L{buildbot.status.builder.BuilderStatus} | |
904 @type request: implementor of L{IBuildRequestStatus} | |
905 """ | |
906 | |
907 def builderAdded(builderName, builder): | |
908 """ | |
909 A new Builder has just been added. This method may return an | |
910 IStatusReceiver (probably 'self') which will be subscribed to receive | |
911 builderChangedState and buildStarted/Finished events. | |
912 | |
913 @type builderName: string | |
914 @type builder: L{buildbot.status.builder.BuilderStatus} | |
915 @rtype: implementor of L{IStatusReceiver} | |
916 """ | |
917 | |
918 def builderChangedState(builderName, state): | |
919 """Builder 'builderName' has changed state. The possible values for | |
920 'state' are 'offline', 'idle', and 'building'.""" | |
921 | |
922 def buildStarted(builderName, build): | |
923 """Builder 'builderName' has just started a build. The build is an | |
924 object which implements IBuildStatus, and can be queried for more | |
925 information. | |
926 | |
927 This method may return an IStatusReceiver (it could even return | |
928 'self'). If it does so, stepStarted and stepFinished methods will be | |
929 invoked on the object for the steps of this one build. This is a | |
930 convenient way to subscribe to all build steps without missing any. | |
931 This receiver will automatically be unsubscribed when the build | |
932 finishes. | |
933 | |
934 It can also return a tuple of (IStatusReceiver, interval), in which | |
935 case buildETAUpdate messages are sent ever 'interval' seconds, in | |
936 addition to the stepStarted and stepFinished messages.""" | |
937 | |
938 def buildETAUpdate(build, ETA): | |
939 """This is a periodic update on the progress this Build has made | |
940 towards completion.""" | |
941 | |
942 def changeAdded(change): | |
943 """A new Change was added to the ChangeMaster. By the time this event | |
944 is received, all schedulers have already received the change.""" | |
945 | |
946 def stepStarted(build, step): | |
947 """A step has just started. 'step' is the IBuildStepStatus which | |
948 represents the step: it can be queried for more information. | |
949 | |
950 This method may return an IStatusReceiver (it could even return | |
951 'self'). If it does so, logStarted and logFinished methods will be | |
952 invoked on the object for logs created by this one step. This | |
953 receiver will be automatically unsubscribed when the step finishes. | |
954 | |
955 Alternatively, the method may return a tuple of an IStatusReceiver | |
956 and an integer named 'updateInterval'. In addition to | |
957 logStarted/logFinished messages, it will also receive stepETAUpdate | |
958 messages about every updateInterval seconds.""" | |
959 | |
960 def stepTextChanged(build, step, text): | |
961 """The text for a step has been updated. | |
962 | |
963 This is called when calling setText() on the step status, and | |
964 hands in the text list.""" | |
965 | |
966 def stepText2Changed(build, step, text2): | |
967 """The text2 for a step has been updated. | |
968 | |
969 This is called when calling setText2() on the step status, and | |
970 hands in text2 list.""" | |
971 | |
972 def stepETAUpdate(build, step, ETA, expectations): | |
973 """This is a periodic update on the progress this Step has made | |
974 towards completion. It gets an ETA (in seconds from the present) of | |
975 when the step ought to be complete, and a list of expectation tuples | |
976 (as returned by IBuildStepStatus.getExpectations) with more detailed | |
977 information.""" | |
978 | |
979 def logStarted(build, step, log): | |
980 """A new Log has been started, probably because a step has just | |
981 started running a shell command. 'log' is the IStatusLog object | |
982 which can be queried for more information. | |
983 | |
984 This method may return an IStatusReceiver (such as 'self'), in which | |
985 case the target's logChunk method will be invoked as text is added to | |
986 the logfile. This receiver will automatically be unsubsribed when the | |
987 log finishes.""" | |
988 | |
989 def logChunk(build, step, log, channel, text): | |
990 """Some text has been added to this log. 'channel' is one of | |
991 LOG_CHANNEL_STDOUT, LOG_CHANNEL_STDERR, or LOG_CHANNEL_HEADER, as | |
992 defined in IStatusLog.getChunks.""" | |
993 | |
994 def logFinished(build, step, log): | |
995 """A Log has been closed.""" | |
996 | |
997 def stepFinished(build, step, results): | |
998 """A step has just finished. 'results' is the result tuple described | |
999 in IBuildStepStatus.getResults.""" | |
1000 | |
1001 def buildFinished(builderName, build, results): | |
1002 """ | |
1003 A build has just finished. 'results' is the result tuple described | |
1004 in L{IBuildStatus.getResults}. | |
1005 | |
1006 @type builderName: string | |
1007 @type build: L{buildbot.status.builder.BuildStatus} | |
1008 @type results: tuple | |
1009 """ | |
1010 | |
1011 def builderRemoved(builderName): | |
1012 """The Builder has been removed.""" | |
1013 | |
1014 def slaveConnected(slaveName): | |
1015 """The slave has connected.""" | |
1016 | |
1017 def slaveDisconnected(slaveName): | |
1018 """The slave has disconnected.""" | |
1019 | |
1020 class IControl(Interface): | |
1021 def addChange(change): | |
1022 """Add a change to all builders. Each Builder will decide for | |
1023 themselves whether the change is interesting or not, and may initiate | |
1024 a build as a result.""" | |
1025 | |
1026 def submitBuildSet(buildset): | |
1027 """Submit a BuildSet object, which will eventually be run on all of | |
1028 the builders listed therein.""" | |
1029 | |
1030 def getBuilder(name): | |
1031 """Retrieve the IBuilderControl object for the given Builder.""" | |
1032 | |
1033 class IBuilderControl(Interface): | |
1034 def requestBuild(request): | |
1035 """Queue a L{buildbot.process.base.BuildRequest} object for later | |
1036 building.""" | |
1037 | |
1038 def requestBuildSoon(request): | |
1039 """Submit a BuildRequest like requestBuild, but raise a | |
1040 L{buildbot.interfaces.NoSlaveError} if no slaves are currently | |
1041 available, so it cannot be used to queue a BuildRequest in the hopes | |
1042 that a slave will eventually connect. This method is appropriate for | |
1043 use by things like the web-page 'Force Build' button.""" | |
1044 | |
1045 def resubmitBuild(buildStatus, reason="<rebuild, no reason given>"): | |
1046 """Rebuild something we've already built before. This submits a | |
1047 BuildRequest to our Builder using the same SourceStamp as the earlier | |
1048 build. This has no effect (but may eventually raise an exception) if | |
1049 this Build has not yet finished.""" | |
1050 | |
1051 def getPendingBuilds(): | |
1052 """Return a list of L{IBuildRequestControl} objects for this Builder. | |
1053 Each one corresponds to a pending build that has not yet started (due | |
1054 to a scarcity of build slaves). These upcoming builds can be canceled | |
1055 through the control object.""" | |
1056 | |
1057 def getBuild(number): | |
1058 """Attempt to return an IBuildControl object for the given build. | |
1059 Returns None if no such object is available. This will only work for | |
1060 the build that is currently in progress: once the build finishes, | |
1061 there is nothing to control anymore.""" | |
1062 | |
1063 def ping(): | |
1064 """Attempt to contact the slave and see if it is still alive. This | |
1065 returns a Deferred which fires with either True (the slave is still | |
1066 alive) or False (the slave did not respond). As a side effect, adds an | |
1067 event to this builder's column in the waterfall display containing the | |
1068 results of the ping. Note that this may not fail for a long time, it is | |
1069 implemented in terms of the timeout on the underlying TCP connection.""" | |
1070 # TODO: this ought to live in ISlaveControl, maybe with disconnect() | |
1071 # or something. However the event that is emitted is most useful in | |
1072 # the Builder column, so it kinda fits here too. | |
1073 | |
1074 class IBuildRequestControl(Interface): | |
1075 def subscribe(observer): | |
1076 """Register a callable that will be invoked (with a single | |
1077 IBuildControl object) for each Build that is created to satisfy this | |
1078 request. There may be multiple Builds created in an attempt to handle | |
1079 the request: they may be interrupted by the user or abandoned due to | |
1080 a lost slave. The last Build (the one which actually gets to run to | |
1081 completion) is said to 'satisfy' the BuildRequest. The observer will | |
1082 be called once for each of these Builds, both old and new.""" | |
1083 def unsubscribe(observer): | |
1084 """Unregister the callable that was registered with subscribe().""" | |
1085 def cancel(): | |
1086 """Remove the build from the pending queue. Has no effect if the | |
1087 build has already been started.""" | |
1088 | |
1089 class IBuildControl(Interface): | |
1090 def getStatus(): | |
1091 """Return an IBuildStatus object for the Build that I control.""" | |
1092 def stopBuild(reason="<no reason given>"): | |
1093 """Halt the build. This has no effect if the build has already | |
1094 finished.""" | |
1095 | |
1096 class ILogFile(Interface): | |
1097 """This is the internal interface to a LogFile, used by the BuildStep to | |
1098 write data into the log. | |
1099 """ | |
1100 def addStdout(data): | |
1101 pass | |
1102 def addStderr(data): | |
1103 pass | |
1104 def addHeader(data): | |
1105 pass | |
1106 def finish(): | |
1107 """The process that is feeding the log file has finished, and no | |
1108 further data will be added. This closes the logfile.""" | |
1109 | |
1110 class ILogObserver(Interface): | |
1111 """Objects which provide this interface can be used in a BuildStep to | |
1112 watch the output of a LogFile and parse it incrementally. | |
1113 """ | |
1114 | |
1115 # internal methods | |
1116 def setStep(step): | |
1117 pass | |
1118 def setLog(log): | |
1119 pass | |
1120 | |
1121 # methods called by the LogFile | |
1122 def logChunk(build, step, log, channel, text): | |
1123 pass | |
1124 | |
1125 class IBuildSlave(Interface): | |
1126 # this is a marker interface for the BuildSlave class | |
1127 pass | |
1128 | |
1129 class ILatentBuildSlave(IBuildSlave): | |
1130 """A build slave that is not always running, but can run when requested. | |
1131 """ | |
1132 substantiated = Attribute('Substantiated', | |
1133 'Whether the latent build slave is currently ' | |
1134 'substantiated with a real instance.') | |
1135 | |
1136 def substantiate(): | |
1137 """Request that the slave substantiate with a real instance. | |
1138 | |
1139 Returns a deferred that will callback when a real instance has | |
1140 attached.""" | |
1141 | |
1142 # there is an insubstantiate too, but that is not used externally ATM. | |
1143 | |
1144 def buildStarted(sb): | |
1145 """Inform the latent build slave that a build has started. | |
1146 | |
1147 ``sb`` is a LatentSlaveBuilder as defined in buildslave.py. The sb | |
1148 is the one for whom the build started. | |
1149 """ | |
1150 | |
1151 def buildFinished(sb): | |
1152 """Inform the latent build slave that a build has finished. | |
1153 | |
1154 ``sb`` is a LatentSlaveBuilder as defined in buildslave.py. The sb | |
1155 is the one for whom the build finished. | |
1156 """ | |
OLD | NEW |