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