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, **kwargs)
74 self.command = c
75 d = c.start()
76 if cb:
77 d.addCallback(self._abandonOnFailure)
78 d.addCallback(cb)
79 return d
80
84
86 if self.sourcedirIsPatched() or self.always_purge:
87 return self._purgeAndUpdate()
88 revision = self.args['revision'] or 'HEAD'
89
90 return self._dovccmd('update', ['--revision', str(revision)])
91
103
105 ''' Since svnversion cannot be used on a svn export, we find the HEAD
106 revision from the repository and pass it to the --revision arg'''
107
108 def parseInfo(res):
109 answer = [i.split(': ') for i in self.command.stdout.splitlines() if i]
110 answer = dict(answer)
111 self.exported_rev = answer['Revision']
112 return self.exported_rev
113
114 def exportCmd(res):
115 args = ['--revision', str(res), self.svnurl, self.srcdir]
116 return self._dovccmd('export', args, rootdir=self.builder.basedir)
117
118 svn_info_d = self._dovccmd('info', (self.svnurl,), rootdir=self.builder.basedir, keepStdout=True)
119
120 svn_info_d.addCallbacks(parseInfo, self._abandonOnFailure)
121 svn_info_d.addCallbacks(exportCmd)
122
123 return svn_info_d
124
126 """svn revert has several corner cases that make it unpractical.
127
128 Use the Force instead and delete everything that shows up in status."""
129 args = ['--xml']
130 if self.ignore_ignores:
131 args.append('--no-ignore')
132 return self._dovccmd('status', args, keepStdout=True, sendStdout=False,
133 cb=self._purgeAndUpdate2)
134
135 @staticmethod
137 """Delete everything that shown up on status."""
138 result_xml = parseString(stdout)
139 for entry in result_xml.getElementsByTagName('entry'):
140 (wc_status,) = entry.getElementsByTagName('wc-status')
141 if wc_status.getAttribute('item') == 'external':
142 continue
143 if wc_status.getAttribute('item') == 'missing':
144 continue
145 filename = entry.getAttribute('path')
146 if filename in keep_on_purge:
147 continue
148 yield filename
149
164
166 """
167 Get the (shell) command used to determine SVN revision number
168 of checked-out code
169
170 return: list of strings, passable as the command argument to RunProcess
171 """
172
173
174
175 svnversion_command = utils.getCommand("svnversion")
176
177
178 return [svnversion_command, "."]
179
181 if self.mode == 'export':
182 ss_rev = self.args['revision']
183 got_revision = ss_rev and ss_rev or self.exported_rev
184 return defer.succeed(got_revision)
185
186 c = runprocess.RunProcess(self.builder,
187 self.getSvnVersionCommand(),
188 os.path.join(self.builder.basedir, self.srcdir),
189 environ=self.env, timeout=self.timeout,
190 sendStdout=False, sendStderr=False, sendRC=False,
191 keepStdout=True, usePTY=False)
192 d = c.start()
193 def _parse(res):
194 r_raw = c.stdout.strip()
195
196 r = r_raw.rstrip('MS')
197 r = r.split(':')[-1]
198 got_version = None
199 try:
200 got_version = int(r)
201 except ValueError:
202 msg =("SVN.parseGotRevision unable to parse output "
203 "of svnversion: '%s'" % r_raw)
204 log.msg(msg)
205 self.sendStatus({'header': msg + "\n"})
206 return got_version
207 d.addCallback(_parse)
208 return d
209