OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2006-2009 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 | 5 |
6 """Enables directory-specific presubmit checks to run at upload and/or commit. | 6 """Enables directory-specific presubmit checks to run at upload and/or commit. |
7 """ | 7 """ |
8 | 8 |
9 __version__ = '1.0.1' | 9 __version__ = '1.0.1' |
10 | 10 |
(...skipping 306 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
317 for line in lines: | 317 for line in lines: |
318 line_number += 1 | 318 line_number += 1 |
319 yield (af, line_number, line) | 319 yield (af, line_number, line) |
320 | 320 |
321 | 321 |
322 class AffectedFile(object): | 322 class AffectedFile(object): |
323 """Representation of a file in a change.""" | 323 """Representation of a file in a change.""" |
324 | 324 |
325 def __init__(self, path, action, repository_root=''): | 325 def __init__(self, path, action, repository_root=''): |
326 self.path = path | 326 self.path = path |
327 self.action = action.strip() | 327 self.action = action |
328 self.repository_root = repository_root | 328 self.repository_root = repository_root |
| 329 self.server_path = None |
| 330 self.is_directory = None |
| 331 self.properties = {} |
329 | 332 |
330 def ServerPath(self): | 333 def ServerPath(self): |
331 """Returns a path string that identifies the file in the SCM system. | 334 """Returns a path string that identifies the file in the SCM system. |
332 | 335 |
333 Returns the empty string if the file does not exist in SCM. | 336 Returns the empty string if the file does not exist in SCM. |
334 """ | 337 """ |
335 return gclient.CaptureSVNInfo(self.AbsoluteLocalPath()).get('URL', '') | 338 return "" |
336 | 339 |
337 def LocalPath(self): | 340 def LocalPath(self): |
338 """Returns the path of this file on the local disk relative to client root. | 341 """Returns the path of this file on the local disk relative to client root. |
339 """ | 342 """ |
340 return normpath(self.path) | 343 return normpath(self.path) |
341 | 344 |
342 def AbsoluteLocalPath(self): | 345 def AbsoluteLocalPath(self): |
343 """Returns the absolute path of this file on the local disk. | 346 """Returns the absolute path of this file on the local disk. |
344 """ | 347 """ |
345 return normpath(os.path.join(self.repository_root, self.LocalPath())) | 348 return normpath(os.path.join(self.repository_root, self.LocalPath())) |
346 | 349 |
347 def IsDirectory(self): | 350 def IsDirectory(self): |
348 """Returns true if this object is a directory.""" | 351 """Returns true if this object is a directory.""" |
349 path = self.AbsoluteLocalPath() | 352 path = self.AbsoluteLocalPath() |
350 if os.path.exists(path): | 353 if self.is_directory is None: |
351 # Retrieve directly from the file system; it is much faster than querying | 354 self.is_directory = (os.path.exists(path) and |
352 # subversion, especially on Windows. | 355 os.path.isdir(path)) |
353 return os.path.isdir(path) | 356 return self.is_directory |
354 else: | |
355 return gclient.CaptureSVNInfo(path).get('Node Kind') in ('dir', | |
356 'directory') | |
357 | |
358 def SvnProperty(self, property_name): | |
359 """Returns the specified SVN property of this file, or the empty string | |
360 if no such property. | |
361 """ | |
362 return gcl.GetSVNFileProperty(self.AbsoluteLocalPath(), property_name) | |
363 | 357 |
364 def Action(self): | 358 def Action(self): |
365 """Returns the action on this opened file, e.g. A, M, D, etc.""" | 359 """Returns the action on this opened file, e.g. A, M, D, etc.""" |
| 360 # TODO(maruel): Somewhat crappy, Could be "A" or "A +" for svn but |
| 361 # different for other SCM. |
366 return self.action | 362 return self.action |
367 | 363 |
| 364 def Property(self, property_name): |
| 365 """Returns the specified SCM property of this file, or None if no such |
| 366 property. |
| 367 """ |
| 368 return self.properties.get(property_name, None) |
| 369 |
368 def NewContents(self): | 370 def NewContents(self): |
369 """Returns an iterator over the lines in the new version of file. | 371 """Returns an iterator over the lines in the new version of file. |
370 | 372 |
371 The new version is the file in the user's workspace, i.e. the "right hand | 373 The new version is the file in the user's workspace, i.e. the "right hand |
372 side". | 374 side". |
373 | 375 |
374 Contents will be empty if the file is a directory or does not exist. | 376 Contents will be empty if the file is a directory or does not exist. |
375 """ | 377 """ |
376 if self.IsDirectory(): | 378 if self.IsDirectory(): |
377 return [] | 379 return [] |
(...skipping 10 matching lines...) Expand all Loading... |
388 def OldFileTempPath(self): | 390 def OldFileTempPath(self): |
389 """Returns the path on local disk where the old contents resides. | 391 """Returns the path on local disk where the old contents resides. |
390 | 392 |
391 The old version is the file in depot, i.e. the "left hand side". | 393 The old version is the file in depot, i.e. the "left hand side". |
392 This is a read-only cached copy of the old contents. *DO NOT* try to | 394 This is a read-only cached copy of the old contents. *DO NOT* try to |
393 modify this file. | 395 modify this file. |
394 """ | 396 """ |
395 raise NotImplementedError() # Implement if/when needed. | 397 raise NotImplementedError() # Implement if/when needed. |
396 | 398 |
397 | 399 |
| 400 class SvnAffectedFile(AffectedFile): |
| 401 def ServerPath(self): |
| 402 if self.server_path is None: |
| 403 self.server_path = gclient.CaptureSVNInfo( |
| 404 self.AbsoluteLocalPath()).get('URL', '') |
| 405 return self.server_path |
| 406 |
| 407 def IsDirectory(self): |
| 408 path = self.AbsoluteLocalPath() |
| 409 if self.is_directory is None: |
| 410 if os.path.exists(path): |
| 411 # Retrieve directly from the file system; it is much faster than |
| 412 # querying subversion, especially on Windows. |
| 413 self.is_directory = os.path.isdir(path) |
| 414 else: |
| 415 self.is_directory = gclient.CaptureSVNInfo( |
| 416 path).get('Node Kind') in ('dir', 'directory') |
| 417 return self.is_directory |
| 418 |
| 419 def Property(self, property_name): |
| 420 if not property_name in self.properties: |
| 421 self.properties[property_name] = gcl.GetSVNFileProperty( |
| 422 self.AbsoluteLocalPath(), property_name) |
| 423 return self.properties[property_name] |
| 424 |
| 425 |
398 class GclChange(object): | 426 class GclChange(object): |
399 """A gcl change. See gcl.ChangeInfo for more info.""" | 427 """A gcl change. See gcl.ChangeInfo for more info.""" |
400 | 428 |
401 def __init__(self, change_info, repository_root=''): | 429 def __init__(self, change_info, repository_root=''): |
402 self.name = change_info.name | 430 self.name = change_info.name |
403 self.full_description = change_info.description | 431 self.full_description = change_info.description |
404 self.repository_root = repository_root | 432 self.repository_root = repository_root |
405 | 433 |
406 # From the description text, build up a dictionary of key/value pairs | 434 # From the description text, build up a dictionary of key/value pairs |
407 # plus the description minus all key/value or "tag" lines. | 435 # plus the description minus all key/value or "tag" lines. |
408 self.description_without_tags = [] | 436 self.description_without_tags = [] |
409 self.tags = {} | 437 self.tags = {} |
410 for line in change_info.description.splitlines(): | 438 for line in change_info.description.splitlines(): |
411 m = _tag_line_re.match(line) | 439 m = _tag_line_re.match(line) |
412 if m: | 440 if m: |
413 self.tags[m.group('key')] = m.group('value') | 441 self.tags[m.group('key')] = m.group('value') |
414 else: | 442 else: |
415 self.description_without_tags.append(line) | 443 self.description_without_tags.append(line) |
416 | 444 |
417 # Change back to text and remove whitespace at end. | 445 # Change back to text and remove whitespace at end. |
418 self.description_without_tags = '\n'.join(self.description_without_tags) | 446 self.description_without_tags = '\n'.join(self.description_without_tags) |
419 self.description_without_tags = self.description_without_tags.rstrip() | 447 self.description_without_tags = self.description_without_tags.rstrip() |
420 | 448 |
421 self.affected_files = [AffectedFile(info[1], info[0], repository_root) for | 449 self.affected_files = [ |
422 info in change_info.files] | 450 SvnAffectedFile(info[1], info[0].strip(), repository_root) |
| 451 for info in change_info.files |
| 452 ] |
423 | 453 |
424 def Change(self): | 454 def Change(self): |
425 """Returns the change name.""" | 455 """Returns the change name.""" |
426 return self.name | 456 return self.name |
427 | 457 |
428 def Changelist(self): | 458 def Changelist(self): |
429 """Synonym for Change().""" | 459 """Synonym for Change().""" |
430 return self.Change() | 460 return self.Change() |
431 | 461 |
432 def DescriptionText(self): | 462 def DescriptionText(self): |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
544 | 574 |
545 checked_dirs[dir] = '' | 575 checked_dirs[dir] = '' |
546 if dir in ['', '.']: | 576 if dir in ['', '.']: |
547 break | 577 break |
548 else: | 578 else: |
549 dir = os.path.dirname(dir) | 579 dir = os.path.dirname(dir) |
550 return presubmit_files | 580 return presubmit_files |
551 | 581 |
552 | 582 |
553 class PresubmitExecuter(object): | 583 class PresubmitExecuter(object): |
554 | |
555 def __init__(self, change_info, committing): | 584 def __init__(self, change_info, committing): |
556 """ | 585 """ |
557 Args: | 586 Args: |
558 change_info: The ChangeInfo object for the change. | 587 change_info: The ChangeInfo object for the change. |
559 committing: True if 'gcl commit' is running, False if 'gcl upload' is. | 588 committing: True if 'gcl commit' is running, False if 'gcl upload' is. |
560 """ | 589 """ |
561 self.change = GclChange(change_info, gcl.GetRepositoryRoot()) | 590 self.change = GclChange(change_info, gcl.GetRepositoryRoot()) |
562 self.committing = committing | 591 self.committing = committing |
563 | 592 |
564 def ExecPresubmitScript(self, script_text, presubmit_path): | 593 def ExecPresubmitScript(self, script_text, presubmit_path): |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
711 return not DoPresubmitChecks(gcl.ChangeInfo(name='temp', files=files), | 740 return not DoPresubmitChecks(gcl.ChangeInfo(name='temp', files=files), |
712 options.commit, | 741 options.commit, |
713 options.verbose, | 742 options.verbose, |
714 sys.stdout, | 743 sys.stdout, |
715 sys.stdin, | 744 sys.stdin, |
716 default_presubmit=None) | 745 default_presubmit=None) |
717 | 746 |
718 | 747 |
719 if __name__ == '__main__': | 748 if __name__ == '__main__': |
720 sys.exit(Main(sys.argv)) | 749 sys.exit(Main(sys.argv)) |
OLD | NEW |