import types import GroupBuildStepStatus from twisted.internet import defer from twisted.python import log from buildbot.process.buildstep import LoggingBuildStep from buildbot.status.builder import Results from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, SKIPPED, \ EXCEPTION, RETRY, worst_status class GroupBuildStep(LoggingBuildStep): renderables = ['description', 'descriptionDone'] finished = False results = None stopped = False steps = [] buildslave = None workdir = None def __init__(self, steps = [], description=None, descriptionDone=None, links=[], **kwargs): LoggingBuildStep.__init__(self, **kwargs) self.addFactoryArguments(steps=steps, description=description, descriptionDone=descriptionDone, links=links) self.factories = [s.getStepFactory() for s in steps] self.description = [description] self.descriptionDone = [descriptionDone] self.links = links self.currentStep = None self.terminate = False self.results = [] # list of FAILURE, SUCCESS, WARNINGS, SKIPPED self.result = SUCCESS # overall result, may downgrade after each step self.text = [] # list of text string lists (text2) def describe(self, done=False): if done and self.descriptionDone is not None: return self.descriptionDone if self.description is not None: return self.description if currentStep is not None: return currentStep.describe(done) return LoggingBuildStep.describe(self, done) def setBuild(self, build): LoggingBuildStep.setBuild(self, build) # create build steps like Build does it # this has to be done here to be able to execute this # build step more then once afte a master restart for factory, args in self.factories: args = args.copy() try: self.steps.append(factory(**args)) except: log.msg("error while creating step, factory=%s, args=%s" % (factory, args)) raise for step in self.steps: step.setBuild(build) def setBuildSlave(self, buildslave): LoggingBuildStep.setBuildSlave(self, buildslave) for step in self.steps: step.setBuildSlave(buildslave) def setDefaultWorkdir(self, workdir): LoggingBuildStep.setDefaultWorkdir(self, workdir) for step in self.steps: step.setDefaultWorkdir(workdir) def setBuildSlave(self, buildslave): self.buildslave = buildslave LoggingBuildStep.setBuildSlave(self, buildslave) def setDefaultWorkdir(self, workdir): self.workdir = workdir LoggingBuildStep.setDefaultWorkdir(self, workdir) def setStepStatus(self, step_status): LoggingBuildStep.setStepStatus(self, step_status) i = 0 for step in self.steps: substep_status = GroupBuildStepStatus(step_status, i) substep_status.setName(step.name) step.setBuild(self.build) step.setDefaultWorkdir(self.workdir) step.setBuildSlave(self.buildslave) step.setStepStatus(substep_status) i = i + 1 def startStep(self, remote): self.remote = remote return LoggingBuildStep.startStep(self, remote) def start(self): log.msg("start") self.startNextStep() def getNextStep(self): if not self.steps: return None if not self.remote: return None if self.terminate or self.stopped: # Run any remaining alwaysRun steps, and skip over the others while True: s = self.steps.pop(0) if s.alwaysRun: return s if not self.steps: return None else: return self.steps.pop(0) def startNextStep(self): try: s = self.getNextStep() except StopIteration: s = None if not s: return self.allStepsDone() self.currentStep = s d = defer.maybeDeferred(s.startStep, self.remote) d.addCallback(self._stepDone, s) d.addErrback(self.failed) def _stepDone(self, results, step): self.currentStep = None if self.finished: return # build was interrupted, don't keep building terminate = self.stepDone(results, step) # interpret/merge results if terminate: self.terminate = True return self.startNextStep() def stepDone(self, result, step): terminate = False text = None if type(result) == types.TupleType: result, text = result assert type(result) == type(SUCCESS) log.msg(" step '%s' complete: %s" % (step.name, Results[result])) self.results.append(result) if text: self.text.extend(text) if not self.remote: terminate = True possible_overall_result = result if result == FAILURE: if not step.flunkOnFailure: possible_overall_result = SUCCESS if step.warnOnFailure: possible_overall_result = WARNINGS if step.flunkOnFailure: possible_overall_result = FAILURE if step.haltOnFailure: terminate = True elif result == WARNINGS: if not step.warnOnWarnings: possible_overall_result = SUCCESS else: possible_overall_result = WARNINGS if step.flunkOnWarnings: possible_overall_result = FAILURE elif result in (EXCEPTION, RETRY): terminate = True # if we skipped this step, then don't adjust the build status if result != SKIPPED: self.result = worst_status(self.result, possible_overall_result) return terminate def allStepsDone(self): for link in self.links: self.addURL(link['title'], self.build.getProperties().render(link['href'])) return LoggingBuildStep.finished(self, self.result) def failed(self, why): LoggingBuildStep.failed(self, why) def interrupt(self, reason=""): log.msg(" %s: stopping build: %s" % (self, reason)) if self.finished: return self.stopped = True if self.currentStep: self.currentStep.interrupt(reason) self.result = EXCEPTION if self._acquiringLock: lock, access, d = self._acquiringLock lock.stopWaitingUntilAvailable(self, access, d) d.callback(None)