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