OLD | NEW |
1 # coding=utf8 | 1 # coding=utf8 |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 """Manages a project checkout. | 5 """Manages a project checkout. |
6 | 6 |
7 Includes support for svn, git-svn and git. | 7 Includes support for svn, git-svn and git. |
8 """ | 8 """ |
9 | 9 |
10 from __future__ import with_statement | 10 from __future__ import with_statement |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
170 commit_pwd = None | 170 commit_pwd = None |
171 svn_url = None | 171 svn_url = None |
172 project_path = None | 172 project_path = None |
173 # Override at class level when necessary. If used, --non-interactive is | 173 # Override at class level when necessary. If used, --non-interactive is |
174 # implied. | 174 # implied. |
175 svn_config = SvnConfig() | 175 svn_config = SvnConfig() |
176 # Set to True when non-interactivity is necessary but a custom subversion | 176 # Set to True when non-interactivity is necessary but a custom subversion |
177 # configuration directory is not necessary. | 177 # configuration directory is not necessary. |
178 non_interactive = False | 178 non_interactive = False |
179 | 179 |
180 def _add_svn_flags(self, args, non_interactive): | 180 def _add_svn_flags(self, args, non_interactive, credentials=True): |
181 args = ['svn'] + args | 181 args = ['svn'] + args |
182 if not self.svn_config.default: | 182 if not self.svn_config.default: |
183 args.extend(['--config-dir', self.svn_config.svn_config_dir]) | 183 args.extend(['--config-dir', self.svn_config.svn_config_dir]) |
184 if not self.svn_config.default or self.non_interactive or non_interactive: | 184 if not self.svn_config.default or self.non_interactive or non_interactive: |
185 args.append('--non-interactive') | 185 args.append('--non-interactive') |
186 if self.commit_user: | 186 if credentials: |
187 args.extend(['--username', self.commit_user]) | 187 if self.commit_user: |
188 if self.commit_pwd: | 188 args.extend(['--username', self.commit_user]) |
189 args.extend(['--password', self.commit_pwd]) | 189 if self.commit_pwd: |
| 190 args.extend(['--password', self.commit_pwd]) |
190 return args | 191 return args |
191 | 192 |
192 def _check_call_svn(self, args, **kwargs): | 193 def _check_call_svn(self, args, **kwargs): |
193 """Runs svn and throws an exception if the command failed.""" | 194 """Runs svn and throws an exception if the command failed.""" |
194 kwargs.setdefault('cwd', self.project_path) | 195 kwargs.setdefault('cwd', self.project_path) |
195 kwargs.setdefault('stdout', self.VOID) | 196 kwargs.setdefault('stdout', self.VOID) |
196 return subprocess2.check_call_out( | 197 return subprocess2.check_call_out( |
197 self._add_svn_flags(args, False), **kwargs) | 198 self._add_svn_flags(args, False), **kwargs) |
198 | 199 |
199 def _check_output_svn(self, args, **kwargs): | 200 def _check_output_svn(self, args, credentials=True, **kwargs): |
200 """Runs svn and throws an exception if the command failed. | 201 """Runs svn and throws an exception if the command failed. |
201 | 202 |
202 Returns the output. | 203 Returns the output. |
203 """ | 204 """ |
204 kwargs.setdefault('cwd', self.project_path) | 205 kwargs.setdefault('cwd', self.project_path) |
205 return subprocess2.check_output(self._add_svn_flags(args, True), **kwargs) | 206 return subprocess2.check_output( |
| 207 self._add_svn_flags(args, True, credentials), **kwargs) |
206 | 208 |
207 @staticmethod | 209 @staticmethod |
208 def _parse_svn_info(output, key): | 210 def _parse_svn_info(output, key): |
209 """Returns value for key from svn info output. | 211 """Returns value for key from svn info output. |
210 | 212 |
211 Case insensitive. | 213 Case insensitive. |
212 """ | 214 """ |
213 values = {} | 215 values = {} |
214 key = key.lower() | 216 key = key.lower() |
215 for line in output.splitlines(False): | 217 for line in output.splitlines(False): |
(...skipping 25 matching lines...) Expand all Loading... |
241 revision = self._revert() | 243 revision = self._revert() |
242 if revision != self._last_seen_revision: | 244 if revision != self._last_seen_revision: |
243 logging.info('Updated at revision %d' % revision) | 245 logging.info('Updated at revision %d' % revision) |
244 self._last_seen_revision = revision | 246 self._last_seen_revision = revision |
245 return revision | 247 return revision |
246 | 248 |
247 def apply_patch(self, patches, post_processor=None): | 249 def apply_patch(self, patches, post_processor=None): |
248 post_processor = post_processor or [] | 250 post_processor = post_processor or [] |
249 for p in patches: | 251 for p in patches: |
250 try: | 252 try: |
| 253 # It is important to use credentials=False otherwise credentials could |
| 254 # leak in the error message. Credentials are not necessary here for the |
| 255 # following commands anyway. |
251 stdout = '' | 256 stdout = '' |
252 if p.is_delete: | 257 if p.is_delete: |
253 stdout += self._check_output_svn(['delete', p.filename, '--force']) | 258 stdout += self._check_output_svn( |
| 259 ['delete', p.filename, '--force'], credentials=False) |
254 else: | 260 else: |
255 new = not os.path.exists(p.filename) | 261 new = not os.path.exists(p.filename) |
256 | 262 |
257 # svn add while creating directories otherwise svn add on the | 263 # svn add while creating directories otherwise svn add on the |
258 # contained files will silently fail. | 264 # contained files will silently fail. |
259 # First, find the root directory that exists. | 265 # First, find the root directory that exists. |
260 dirname = os.path.dirname(p.filename) | 266 dirname = os.path.dirname(p.filename) |
261 dirs_to_create = [] | 267 dirs_to_create = [] |
262 while (dirname and | 268 while (dirname and |
263 not os.path.isdir(os.path.join(self.project_path, dirname))): | 269 not os.path.isdir(os.path.join(self.project_path, dirname))): |
264 dirs_to_create.append(dirname) | 270 dirs_to_create.append(dirname) |
265 dirname = os.path.dirname(dirname) | 271 dirname = os.path.dirname(dirname) |
266 for dir_to_create in reversed(dirs_to_create): | 272 for dir_to_create in reversed(dirs_to_create): |
267 os.mkdir(os.path.join(self.project_path, dir_to_create)) | 273 os.mkdir(os.path.join(self.project_path, dir_to_create)) |
268 stdout += self._check_output_svn( | 274 stdout += self._check_output_svn( |
269 ['add', dir_to_create, '--force']) | 275 ['add', dir_to_create, '--force'], credentials=False) |
270 | 276 |
271 if p.is_binary: | 277 if p.is_binary: |
272 with open(os.path.join(self.project_path, p.filename), 'wb') as f: | 278 with open(os.path.join(self.project_path, p.filename), 'wb') as f: |
273 f.write(p.get()) | 279 f.write(p.get()) |
274 else: | 280 else: |
275 cmd = ['patch', '-p%s' % p.patchlevel, '--forward', '--force'] | 281 cmd = ['patch', '-p%s' % p.patchlevel, '--forward', '--force'] |
276 stdout += subprocess2.check_output( | 282 stdout += subprocess2.check_output( |
277 cmd, stdin=p.get(), cwd=self.project_path) | 283 cmd, stdin=p.get(), cwd=self.project_path) |
278 if new: | 284 if new: |
279 stdout += self._check_output_svn(['add', p.filename, '--force']) | 285 stdout += self._check_output_svn( |
| 286 ['add', p.filename, '--force'], credentials=False) |
280 for prop in p.svn_properties: | 287 for prop in p.svn_properties: |
281 stdout += self._check_output_svn( | 288 stdout += self._check_output_svn( |
282 ['propset', prop[0], prop[1], p.filename]) | 289 ['propset', prop[0], prop[1], p.filename], credentials=False) |
283 for prop, value in self.svn_config.auto_props.iteritems(): | 290 for prop, values in self.svn_config.auto_props.iteritems(): |
284 if fnmatch.fnmatch(p.filename, prop): | 291 if fnmatch.fnmatch(p.filename, prop): |
285 stdout += self._check_output_svn( | 292 for value in values.split(';'): |
286 ['propset'] + value.split('=', 1) + [p.filename]) | 293 if '=' not in value: |
| 294 params = [value, '*'] |
| 295 else: |
| 296 params = value.split('=', 1) |
| 297 stdout += self._check_output_svn( |
| 298 ['propset'] + params + [p.filename], credentials=False) |
287 for post in post_processor: | 299 for post in post_processor: |
288 post(self, p) | 300 post(self, p) |
289 except OSError, e: | 301 except OSError, e: |
290 raise PatchApplicationFailed(p.filename, '%s%s' % (stdout, e)) | 302 raise PatchApplicationFailed(p.filename, '%s%s' % (stdout, e)) |
291 except subprocess.CalledProcessError, e: | 303 except subprocess.CalledProcessError, e: |
292 raise PatchApplicationFailed( | 304 raise PatchApplicationFailed( |
293 p.filename, '%s%s' % (stdout, getattr(e, 'stdout', ''))) | 305 p.filename, |
| 306 'While running %s;\n%s%s' % ( |
| 307 ' '.join(e.cmd), stdout, getattr(e, 'stdout', ''))) |
294 | 308 |
295 def commit(self, commit_message, user): | 309 def commit(self, commit_message, user): |
296 logging.info('Committing patch for %s' % user) | 310 logging.info('Committing patch for %s' % user) |
297 assert self.commit_user | 311 assert self.commit_user |
298 assert isinstance(commit_message, unicode) | 312 assert isinstance(commit_message, unicode) |
299 handle, commit_filename = tempfile.mkstemp(text=True) | 313 handle, commit_filename = tempfile.mkstemp(text=True) |
300 try: | 314 try: |
301 # Shouldn't assume default encoding is UTF-8. But really, if you are using | 315 # Shouldn't assume default encoding is UTF-8. But really, if you are using |
302 # anything else, you are living in another world. | 316 # anything else, you are living in another world. |
303 os.write(handle, commit_message.encode('utf-8')) | 317 os.write(handle, commit_message.encode('utf-8')) |
(...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
645 user, message)) | 659 user, message)) |
646 return 'FAKE' | 660 return 'FAKE' |
647 | 661 |
648 @property | 662 @property |
649 def project_name(self): | 663 def project_name(self): |
650 return self.checkout.project_name | 664 return self.checkout.project_name |
651 | 665 |
652 @property | 666 @property |
653 def project_path(self): | 667 def project_path(self): |
654 return self.checkout.project_path | 668 return self.checkout.project_path |
OLD | NEW |