1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16  import os 
 17  from xml.dom.minidom import parseString 
 18   
 19  from twisted.python import log 
 20  from twisted.internet import defer 
 21   
 22  from buildslave.commands.base import SourceBaseCommand 
 23  from buildslave import runprocess 
 24  from buildslave.commands import utils 
 25  from buildslave.util import Obfuscated 
 26   
 27 -class SVN(SourceBaseCommand): 
  28      """Subversion-specific VC operation. In addition to the arguments 
 29      handled by SourceBaseCommand, this command reads the following keys: 
 30   
 31      ['svnurl'] (required): the SVN repository string 
 32      ['username']:          Username passed to the svn command 
 33      ['password']:          Password passed to the svn command 
 34      ['keep_on_purge']:     Files and directories to keep between updates 
 35      ['ignore_ignores']:    Ignore ignores when purging changes 
 36      ['always_purge']:      Always purge local changes after each build 
 37      ['depth']:             Pass depth argument to subversion 1.5+ 
 38      """ 
 39   
 40      header = "svn operation" 
 41   
 43          SourceBaseCommand.setup(self, args) 
 44          self.svnurl = args['svnurl'] 
 45          self.sourcedata = "%s\n" % self.svnurl 
 46          self.keep_on_purge = args.get('keep_on_purge', []) 
 47          self.keep_on_purge.append(".buildbot-sourcedata") 
 48          self.ignore_ignores = args.get('ignore_ignores', True) 
 49          self.always_purge = args.get('always_purge', False) 
 50   
 51          self.exported_rev = 'HEAD' 
 52   
 53          self.svn_args = [] 
 54          if args.has_key('username'): 
 55              self.svn_args.extend(["--username", args['username']]) 
 56          if args.has_key('password'): 
 57              self.svn_args.extend(["--password", Obfuscated(args['password'], "XXXX")]) 
 58          if args.get('extra_args', None) is not None: 
 59              self.svn_args.extend(args['extra_args']) 
 60   
 61          if args.has_key('depth'): 
 62              self.svn_args.extend(["--depth",args['depth']]) 
  63   
 64 -    def _dovccmd(self, command, args, rootdir=None, cb=None, **kwargs): 
  65          svn = self.getCommand("svn") 
 66          if rootdir is None: 
 67              rootdir = os.path.join(self.builder.basedir, self.srcdir) 
 68          fullCmd = [svn, command, '--non-interactive', '--no-auth-cache'] 
 69          fullCmd.extend(self.svn_args) 
 70          fullCmd.extend(args) 
 71          c = runprocess.RunProcess(self.builder, fullCmd, rootdir, 
 72                           environ=self.env, sendRC=False, timeout=self.timeout, 
 73                           maxTime=self.maxTime, usePTY=False, 
 74                           logEnviron=self.logEnviron, **kwargs) 
 75          self.command = c 
 76          d = c.start() 
 77          if cb: 
 78              d.addCallback(self._abandonOnFailure) 
 79              d.addCallback(cb) 
 80          return d 
  81   
 85   
 87          if self.sourcedirIsPatched() or self.always_purge: 
 88              return self._purgeAndUpdate() 
 89          revision = self.args['revision'] or 'HEAD' 
 90           
 91          return self._dovccmd('update', ['--revision', str(revision)]) 
  92   
104   
106          ''' Since svnversion cannot be used on a svn export, we find the HEAD 
107              revision from the repository and pass it to the --revision arg''' 
108   
109          def parseInfo(res): 
110              answer = [i.split(': ') for i in self.command.stdout.splitlines() if i] 
111              answer = dict(answer) 
112              self.exported_rev = answer['Revision'] 
113              return self.exported_rev 
 114   
115          def exportCmd(res): 
116              args = ['--revision', str(res), self.svnurl, self.srcdir] 
117              return self._dovccmd('export', args, rootdir=self.builder.basedir) 
 118   
119          svn_info_d = self._dovccmd('info', (self.svnurl,), rootdir=self.builder.basedir, keepStdout=True) 
120   
121          svn_info_d.addCallbacks(parseInfo, self._abandonOnFailure) 
122          svn_info_d.addCallbacks(exportCmd)  
123   
124          return svn_info_d 
125   
127          """svn revert has several corner cases that make it unpractical. 
128   
129          Use the Force instead and delete everything that shows up in status.""" 
130          args = ['--xml'] 
131          if self.ignore_ignores: 
132              args.append('--no-ignore') 
133          return self._dovccmd('status', args, keepStdout=True, sendStdout=False, 
134                               cb=self._purgeAndUpdate2) 
 135   
136      @staticmethod 
138          """Delete everything that shown up on status.""" 
139          result_xml = parseString(stdout) 
140          for entry in result_xml.getElementsByTagName('entry'): 
141              (wc_status,) = entry.getElementsByTagName('wc-status') 
142              if wc_status.getAttribute('item') == 'external': 
143                  continue 
144              if wc_status.getAttribute('item') == 'missing': 
145                  continue 
146              filename = entry.getAttribute('path') 
147              if filename in keep_on_purge: 
148                  continue 
149              yield filename 
 150   
165   
167          """ 
168          Get the (shell) command used to determine SVN revision number 
169          of checked-out code 
170   
171          return: list of strings, passable as the command argument to RunProcess 
172          """ 
173           
174           
175           
176          svnversion_command = utils.getCommand("svnversion") 
177           
178           
179          return [svnversion_command, "."] 
 180   
182          if self.mode == 'export': 
183              ss_rev = self.args['revision'] 
184              got_revision = ss_rev and ss_rev or self.exported_rev 
185              return defer.succeed(got_revision) 
186   
187          c = runprocess.RunProcess(self.builder, 
188                           self.getSvnVersionCommand(), 
189                           os.path.join(self.builder.basedir, self.srcdir), 
190                           environ=self.env, timeout=self.timeout, 
191                           sendStdout=False, sendStderr=False, sendRC=False, 
192                           keepStdout=True, usePTY=False, 
193                           logEnviron=self.logEnviron) 
194          d = c.start() 
195          def _parse(res): 
196              r_raw = c.stdout.strip() 
197               
198              r = r_raw.rstrip('MSP') 
199              r = r.split(':')[-1] 
200              got_version = None 
201              try: 
202                  got_version = int(r) 
203              except ValueError: 
204                  msg =("SVN.parseGotRevision unable to parse output " 
205                        "of svnversion: '%s'" % r_raw) 
206                  log.msg(msg) 
207                  self.sendStatus({'header': msg + "\n"}) 
208              return got_version 
 209          d.addCallback(_parse) 
210          return d 
211