| 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 |