1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16  import re 
 17  import xml.dom.minidom 
 18  import xml.parsers.expat 
 19   
 20  from twisted.python import log 
 21  from twisted.internet import defer 
 22   
 23  from buildbot.process import buildstep 
 24  from buildbot.steps.source.base import Source 
 25  from buildbot.interfaces import BuildSlaveTooOldError 
 26  from buildbot.config import ConfigErrors 
 27   
 28 -class SVN(Source): 
  29      """I perform Subversion checkout/update operations.""" 
 30   
 31      name = 'svn' 
 32   
 33      renderables = [ 'repourl' ] 
 34      possible_modes = ('incremental', 'full') 
 35      possible_methods = ('clean', 'fresh', 'clobber', 'copy', 'export', None) 
 36   
 37 -    def __init__(self, repourl=None, mode='incremental', 
 38                   method=None, username=None, 
 39                   password=None, extra_args=None, keep_on_purge=None, 
 40                   depth=None, **kwargs): 
  62   
 63 -    def startVC(self, branch, revision, patch): 
  64          self.revision = revision 
 65          self.method = self._getMethod() 
 66          self.stdio_log = self.addLog("stdio") 
 67   
 68          d = self.checkSvn() 
 69          def checkInstall(svnInstalled): 
 70              if not svnInstalled: 
 71                  raise BuildSlaveTooOldError("SVN is not installed on slave") 
 72              return 0 
  73          d.addCallback(checkInstall) 
 74   
 75          if self.mode == 'full': 
 76              d.addCallback(self.full) 
 77          elif self.mode == 'incremental': 
 78              d.addCallback(self.incremental) 
 79          d.addCallback(self.parseGotRevision) 
 80          d.addCallback(self.finish) 
 81          d.addErrback(self.failed) 
 82          return d 
  83   
 84      @defer.inlineCallbacks 
 86          if self.method == 'clobber': 
 87              yield self.clobber() 
 88              return 
 89          elif self.method in ['copy', 'export']: 
 90              yield self.copy() 
 91              return 
 92   
 93          updatable = yield self._sourcedirIsUpdatable() 
 94          if not updatable: 
 95               
 96              yield self._rmdir(self.workdir) 
 97   
 98               
 99              checkout_cmd = ['checkout', self.repourl, '.'] 
100              if self.revision: 
101                  checkout_cmd.extend(["--revision", str(self.revision)]) 
102              yield self._dovccmd(checkout_cmd) 
103          elif self.method == 'clean': 
104              yield self.clean() 
105          elif self.method == 'fresh': 
106              yield self.fresh() 
 107   
108      @defer.inlineCallbacks 
110          updatable = yield self._sourcedirIsUpdatable() 
111   
112          if not updatable: 
113               
114              yield self._rmdir(self.workdir) 
115   
116               
117              command = ['checkout', self.repourl, '.'] 
118          else: 
119               
120              command = ['update'] 
121   
122          if self.revision: 
123              command.extend(['--revision', str(self.revision)]) 
124   
125          yield self._dovccmd(command) 
 126   
127      @defer.inlineCallbacks 
141   
143          d = self.purge(True) 
144          cmd = ['update'] 
145          if self.revision: 
146              cmd.extend(['--revision', str(self.revision)]) 
147          d.addCallback(lambda _: self._dovccmd(cmd)) 
148          return d 
 149   
151          d = self.purge(False) 
152          cmd = ['update'] 
153          if self.revision: 
154              cmd.extend(['--revision', str(self.revision)]) 
155          d.addCallback(lambda _: self._dovccmd(cmd)) 
156          return d 
 157   
158      @defer.inlineCallbacks 
160          cmd = buildstep.RemoteCommand('rmdir', {'dir': self.workdir, 
161                                                  'logEnviron': self.logEnviron,}) 
162          cmd.useLog(self.stdio_log, False) 
163          yield self.runCommand(cmd) 
164   
165          if cmd.didFail(): 
166              raise buildstep.BuildStepFailed() 
167   
168           
169          try: 
170              old_workdir = self.workdir 
171              self.workdir = 'source' 
172              yield self.incremental(None) 
173          except:  
174              self.workdir = old_workdir 
175              raise 
176          self.workdir = old_workdir 
177   
178           
179          if self.method == 'copy': 
180              cmd = buildstep.RemoteCommand('cpdir', 
181                      { 'fromdir': 'source', 'todir':self.workdir, 
182                        'logEnviron': self.logEnviron }) 
183          else: 
184              export_cmd = ['svn', 'export'] 
185              if self.revision: 
186                  export_cmd.extend(["--revision", str(self.revision)]) 
187              export_cmd.extend(['source', self.workdir]) 
188   
189              cmd = buildstep.RemoteShellCommand('', export_cmd, 
190                      env=self.env, logEnviron=self.logEnviron, timeout=self.timeout) 
191          cmd.useLog(self.stdio_log, False) 
192   
193          yield self.runCommand(cmd) 
194   
195          if cmd.didFail(): 
196              raise buildstep.BuildStepFailed() 
 197   
203          d.addCallback(_gotResults) 
204          d.addCallbacks(self.finished, self.checkDisconnect) 
205          return d 
206   
207      @defer.inlineCallbacks 
215   
216 -    def _dovccmd(self, command, collectStdout=False): 
 217          assert command, "No command specified" 
218          command.extend(['--non-interactive', '--no-auth-cache']) 
219          if self.username: 
220              command.extend(['--username', self.username]) 
221          if self.password: 
222              command.extend(['--password', self.password]) 
223          if self.depth: 
224              command.extend(['--depth', self.depth]) 
225          if self.extra_args: 
226              command.extend(self.extra_args) 
227   
228          cmd = buildstep.RemoteShellCommand(self.workdir, ['svn'] + command, 
229                                             env=self.env, 
230                                             logEnviron=self.logEnviron, 
231                                             timeout=self.timeout, 
232                                             collectStdout=collectStdout) 
233          cmd.useLog(self.stdio_log, False) 
234          log.msg("Starting SVN command : svn %s" % (" ".join(command), )) 
235          d = self.runCommand(cmd) 
236          def evaluateCommand(cmd): 
237              if cmd.didFail(): 
238                  log.msg("Source step failed while running command %s" % cmd) 
239                  raise buildstep.BuildStepFailed() 
240              if collectStdout: 
241                  return cmd.stdout 
242              else: 
243                  return cmd.rc 
 244          d.addCallback(lambda _: evaluateCommand(cmd)) 
245          return d 
246   
248          if self.method is not None and self.mode != 'incremental': 
249              return self.method 
250          elif self.mode == 'incremental': 
251              return None 
252          elif self.method is None and self.mode == 'full': 
253              return 'fresh' 
 254   
255      @defer.inlineCallbacks 
257           
258          cmd = buildstep.RemoteCommand('stat', {'file': self.workdir + '/.svn', 
259                                                 'logEnviron': self.logEnviron,}) 
260          cmd.useLog(self.stdio_log, False) 
261          yield self.runCommand(cmd) 
262   
263          if cmd.didFail(): 
264              defer.returnValue(False) 
265              return 
266   
267           
268          stdout = yield self._dovccmd(['info'], collectStdout=True) 
269   
270           
271           
272          mo = re.search('^URL:\s*(.*?)\s*$', stdout, re.M) 
273          defer.returnValue(mo and mo.group(1) == self.repourl) 
274          return 
 275   
305          d.addCallback(lambda _: _setrev(cmd.rc)) 
306          return d 
307   
308 -    def purge(self, ignore_ignores): 
 332          d.addCallback(parseAndRemove) 
333          def evaluateCommand(rc): 
334              if rc != 0: 
335                  log.msg("Failed removing files") 
336                  raise buildstep.BuildStepFailed() 
337              return rc 
338          d.addCallback(evaluateCommand) 
339          return d 
340   
341      @staticmethod 
343          try: 
344              result_xml = xml.dom.minidom.parseString(xmlStr) 
345          except xml.parsers.expat.ExpatError: 
346              log.err("Corrupted xml, aborting step") 
347              raise buildstep.BuildStepFailed() 
348   
349          for entry in result_xml.getElementsByTagName('entry'): 
350              (wc_status,) = entry.getElementsByTagName('wc-status') 
351              if wc_status.getAttribute('item') == 'external': 
352                  continue 
353              if wc_status.getAttribute('item') == 'missing': 
354                  continue 
355              filename = entry.getAttribute('path') 
356              if filename in keep_on_purge or filename == '': 
357                  continue 
358              yield filename 
 359   
360      @defer.inlineCallbacks 
371   
383          d.addCallback(lambda _: evaluate(cmd)) 
384          return d 
385   
391