aboutsummaryrefslogblamecommitdiffstats
path: root/GroupBuildStep.py
blob: 5f7d168919ca88b5c965a47d1b66f07af698f910 (plain) (tree)








































































































































































































                                                                                               
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)