Package buildbot :: Package steps :: Module master
[frames] | no frames]

Source Code for Module buildbot.steps.master

  1  # This file is part of Buildbot.  Buildbot is free software: you can 
  2  # redistribute it and/or modify it under the terms of the GNU General Public 
  3  # License as published by the Free Software Foundation, version 2. 
  4  # 
  5  # This program is distributed in the hope that it will be useful, but WITHOUT 
  6  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
  7  # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
  8  # details. 
  9  # 
 10  # You should have received a copy of the GNU General Public License along with 
 11  # this program; if not, write to the Free Software Foundation, Inc., 51 
 12  # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 13  # 
 14  # Copyright Buildbot Team Members 
 15   
 16  import os, types, re 
 17  from twisted.python import runtime 
 18  from twisted.internet import reactor 
 19  from buildbot.process.buildstep import BuildStep 
 20  from buildbot.process.buildstep import SUCCESS, FAILURE 
 21  from twisted.internet import error 
 22  from twisted.internet.protocol import ProcessProtocol 
 23   
24 -class MasterShellCommand(BuildStep):
25 """ 26 Run a shell command locally - on the buildmaster. The shell command 27 COMMAND is specified just as for a RemoteShellCommand. Note that extra 28 logfiles are not supported. 29 """ 30 name='MasterShellCommand' 31 description='Running' 32 descriptionDone='Ran' 33 descriptionSuffix = None 34 renderables = [ 'command', 'env', 'description', 'descriptionDone', 'descriptionSuffix' ] 35 haltOnFailure = True 36 flunkOnFailure = True 37
38 - def __init__(self, command, 39 description=None, descriptionDone=None, descriptionSuffix=None, 40 env=None, path=None, usePTY=0, interruptSignal="KILL", 41 **kwargs):
42 BuildStep.__init__(self, **kwargs) 43 44 self.command=command 45 if description: 46 self.description = description 47 if isinstance(self.description, str): 48 self.description = [self.description] 49 if descriptionDone: 50 self.descriptionDone = descriptionDone 51 if isinstance(self.descriptionDone, str): 52 self.descriptionDone = [self.descriptionDone] 53 if descriptionSuffix: 54 self.descriptionSuffix = descriptionSuffix 55 if isinstance(self.descriptionSuffix, str): 56 self.descriptionSuffix = [self.descriptionSuffix] 57 self.env=env 58 self.path=path 59 self.usePTY=usePTY 60 self.interruptSignal = interruptSignal
61
62 - class LocalPP(ProcessProtocol):
63 - def __init__(self, step):
64 self.step = step
65
66 - def outReceived(self, data):
67 self.step.stdio_log.addStdout(data)
68
69 - def errReceived(self, data):
70 self.step.stdio_log.addStderr(data)
71
72 - def processEnded(self, status_object):
73 if status_object.value.exitCode is not None: 74 self.step.stdio_log.addHeader("exit status %d\n" % status_object.value.exitCode) 75 if status_object.value.signal is not None: 76 self.step.stdio_log.addHeader("signal %s\n" % status_object.value.signal) 77 self.step.processEnded(status_object)
78
79 - def start(self):
80 # render properties 81 command = self.command 82 # set up argv 83 if type(command) in types.StringTypes: 84 if runtime.platformType == 'win32': 85 argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args 86 if '/c' not in argv: argv += ['/c'] 87 argv += [command] 88 else: 89 # for posix, use /bin/sh. for other non-posix, well, doesn't 90 # hurt to try 91 argv = ['/bin/sh', '-c', command] 92 else: 93 if runtime.platformType == 'win32': 94 argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args 95 if '/c' not in argv: argv += ['/c'] 96 argv += list(command) 97 else: 98 argv = command 99 100 self.stdio_log = stdio_log = self.addLog("stdio") 101 102 if type(command) in types.StringTypes: 103 stdio_log.addHeader(command.strip() + "\n\n") 104 else: 105 stdio_log.addHeader(" ".join(command) + "\n\n") 106 stdio_log.addHeader("** RUNNING ON BUILDMASTER **\n") 107 stdio_log.addHeader(" in dir %s\n" % os.getcwd()) 108 stdio_log.addHeader(" argv: %s\n" % (argv,)) 109 self.step_status.setText(self.describe()) 110 111 if self.env is None: 112 env = os.environ 113 else: 114 assert isinstance(self.env, dict) 115 env = self.env 116 117 # do substitution on variable values matching pattern: ${name} 118 p = re.compile('\${([0-9a-zA-Z_]*)}') 119 def subst(match): 120 return os.environ.get(match.group(1), "")
121 newenv = {} 122 for key in env.keys(): 123 if env[key] is not None: 124 newenv[key] = p.sub(subst, env[key]) 125 env = newenv 126 stdio_log.addHeader(" env: %r\n" % (env,)) 127 128 # TODO add a timeout? 129 self.process = reactor.spawnProcess(self.LocalPP(self), argv[0], argv, 130 path=self.path, usePTY=self.usePTY, env=env )
131 # (the LocalPP object will call processEnded for us) 132
133 - def processEnded(self, status_object):
134 if status_object.value.signal is not None: 135 self.descriptionDone = ["killed (%s)" % status_object.value.signal] 136 self.step_status.setText(self.describe(done=True)) 137 self.finished(FAILURE) 138 elif status_object.value.exitCode != 0: 139 self.descriptionDone = ["failed (%d)" % status_object.value.exitCode] 140 self.step_status.setText(self.describe(done=True)) 141 self.finished(FAILURE) 142 else: 143 self.step_status.setText(self.describe(done=True)) 144 self.finished(SUCCESS)
145
146 - def describe(self, done=False):
147 desc = self.descriptionDone if done else self.description 148 if self.descriptionSuffix: 149 desc = desc[:] 150 desc.extend(self.descriptionSuffix) 151 return desc
152
153 - def interrupt(self, reason):
154 try: 155 self.process.signalProcess(self.interruptSignal) 156 except KeyError: # Process not started yet 157 pass 158 except error.ProcessExitedAlready: 159 pass 160 BuildStep.interrupt(self, reason)
161