diff options
Diffstat (limited to 'GroupBuildStep.py')
-rw-r--r-- | GroupBuildStep.py | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/GroupBuildStep.py b/GroupBuildStep.py new file mode 100644 index 0000000..5f7d168 --- /dev/null +++ b/GroupBuildStep.py @@ -0,0 +1,201 @@ +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="<no reason given>"): + 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) |