import types
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
from GroupBuildStepStatus import GroupBuildStepStatus
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)