Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(479)

Side by Side Diff: presubmit_support.py

Issue 391075: Revert 32057, 32058, 32059, 32062 because they still have unwanted side-effects. (Closed)
Patch Set: Created 11 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « git_cl_hooks.py ('k') | revert.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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.3.4' 9 __version__ = '1.3.3'
10 10
11 # TODO(joi) Add caching where appropriate/needed. The API is designed to allow 11 # TODO(joi) Add caching where appropriate/needed. The API is designed to allow
12 # caching (between all different invocations of presubmit scripts for a given 12 # caching (between all different invocations of presubmit scripts for a given
13 # change). We should add it as our presubmit scripts start feeling slow. 13 # change). We should add it as our presubmit scripts start feeling slow.
14 14
15 import cPickle # Exposed through the API. 15 import cPickle # Exposed through the API.
16 import cStringIO # Exposed through the API. 16 import cStringIO # Exposed through the API.
17 import exceptions 17 import exceptions
18 import fnmatch 18 import fnmatch
19 import glob 19 import glob
20 import logging 20 import logging
21 import marshal # Exposed through the API. 21 import marshal # Exposed through the API.
22 import optparse 22 import optparse
23 import os # Somewhat exposed through the API. 23 import os # Somewhat exposed through the API.
24 import pickle # Exposed through the API. 24 import pickle # Exposed through the API.
25 import random 25 import random
26 import re # Exposed through the API. 26 import re # Exposed through the API.
27 import subprocess # Exposed through the API. 27 import subprocess # Exposed through the API.
28 import sys # Parts exposed through API. 28 import sys # Parts exposed through API.
29 import tempfile # Exposed through the API. 29 import tempfile # Exposed through the API.
30 import time 30 import time
31 import traceback # Exposed through the API. 31 import traceback # Exposed through the API.
32 import types 32 import types
33 import unittest # Exposed through the API. 33 import unittest # Exposed through the API.
34 import urllib2 # Exposed through the API. 34 import urllib2 # Exposed through the API.
35 import warnings 35 import warnings
36 36
37 # Local imports. 37 # Local imports.
38 # TODO(joi) Would be cleaner to factor out utils in gcl to separate module, but
39 # for now it would only be a couple of functions so hardly worth it.
38 import gcl 40 import gcl
39 import gclient_utils 41 import gclient_scm
40 import presubmit_canned_checks 42 import presubmit_canned_checks
41 import scm
42 43
43 44
44 # Ask for feedback only once in program lifetime. 45 # Ask for feedback only once in program lifetime.
45 _ASKED_FOR_FEEDBACK = False 46 _ASKED_FOR_FEEDBACK = False
46 47
47 48
48 class NotImplementedException(Exception): 49 class NotImplementedException(Exception):
49 """We're leaving placeholders in a bunch of places to remind us of the 50 """We're leaving placeholders in a bunch of places to remind us of the
50 design of the API, but we have not implemented all of it yet. Implement as 51 design of the API, but we have not implemented all of it yet. Implement as
51 the need arises. 52 the need arises.
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after
233 234
234 Args: 235 Args:
235 Depot path as a string. 236 Depot path as a string.
236 237
237 Returns: 238 Returns:
238 The local path of the depot path under the user's current client, or None 239 The local path of the depot path under the user's current client, or None
239 if the file is not mapped. 240 if the file is not mapped.
240 241
241 Remember to check for the None case and show an appropriate error! 242 Remember to check for the None case and show an appropriate error!
242 """ 243 """
243 local_path = scm.SVN.CaptureInfo(depot_path).get('Path') 244 local_path = gclient_scm.CaptureSVNInfo(depot_path).get('Path')
244 if local_path: 245 if local_path:
245 return local_path 246 return local_path
246 247
247 def LocalToDepotPath(self, local_path): 248 def LocalToDepotPath(self, local_path):
248 """Translate a local path to a depot path. 249 """Translate a local path to a depot path.
249 250
250 Args: 251 Args:
251 Local path (relative to current directory, or absolute) as a string. 252 Local path (relative to current directory, or absolute) as a string.
252 253
253 Returns: 254 Returns:
254 The depot path (SVN URL) of the file if mapped, otherwise None. 255 The depot path (SVN URL) of the file if mapped, otherwise None.
255 """ 256 """
256 depot_path = scm.SVN.CaptureInfo(local_path).get('URL') 257 depot_path = gclient_scm.CaptureSVNInfo(local_path).get('URL')
257 if depot_path: 258 if depot_path:
258 return depot_path 259 return depot_path
259 260
260 def AffectedFiles(self, include_dirs=False, include_deletes=True): 261 def AffectedFiles(self, include_dirs=False, include_deletes=True):
261 """Same as input_api.change.AffectedFiles() except only lists files 262 """Same as input_api.change.AffectedFiles() except only lists files
262 (and optionally directories) in the same directory as the current presubmit 263 (and optionally directories) in the same directory as the current presubmit
263 script, or subdirectories thereof. 264 script, or subdirectories thereof.
264 """ 265 """
265 dir_with_slash = normpath("%s/" % self.PresubmitLocalPath()) 266 dir_with_slash = normpath("%s/" % self.PresubmitLocalPath())
266 if len(dir_with_slash) == 1: 267 if len(dir_with_slash) == 1:
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
346 347
347 def ReadFile(self, file_item, mode='r'): 348 def ReadFile(self, file_item, mode='r'):
348 """Reads an arbitrary file. 349 """Reads an arbitrary file.
349 350
350 Deny reading anything outside the repository. 351 Deny reading anything outside the repository.
351 """ 352 """
352 if isinstance(file_item, AffectedFile): 353 if isinstance(file_item, AffectedFile):
353 file_item = file_item.AbsoluteLocalPath() 354 file_item = file_item.AbsoluteLocalPath()
354 if not file_item.startswith(self.change.RepositoryRoot()): 355 if not file_item.startswith(self.change.RepositoryRoot()):
355 raise IOError('Access outside the repository root is denied.') 356 raise IOError('Access outside the repository root is denied.')
356 return gclient_utils.FileRead(file_item, mode) 357 return gcl.ReadFile(file_item, mode)
357 358
358 @staticmethod 359 @staticmethod
359 def _RightHandSideLinesImpl(affected_files): 360 def _RightHandSideLinesImpl(affected_files):
360 """Implements RightHandSideLines for InputApi and GclChange.""" 361 """Implements RightHandSideLines for InputApi and GclChange."""
361 for af in affected_files: 362 for af in affected_files:
362 lines = af.NewContents() 363 lines = af.NewContents()
363 line_number = 0 364 line_number = 0
364 for line in lines: 365 for line in lines:
365 line_number += 1 366 line_number += 1
366 yield (af, line_number, line) 367 yield (af, line_number, line)
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
424 425
425 The new version is the file in the user's workspace, i.e. the "right hand 426 The new version is the file in the user's workspace, i.e. the "right hand
426 side". 427 side".
427 428
428 Contents will be empty if the file is a directory or does not exist. 429 Contents will be empty if the file is a directory or does not exist.
429 Note: The cariage returns (LF or CR) are stripped off. 430 Note: The cariage returns (LF or CR) are stripped off.
430 """ 431 """
431 if self.IsDirectory(): 432 if self.IsDirectory():
432 return [] 433 return []
433 else: 434 else:
434 return gclient_utils.FileRead(self.AbsoluteLocalPath(), 435 return gcl.ReadFile(self.AbsoluteLocalPath()).splitlines()
435 'rU').splitlines()
436 436
437 def OldContents(self): 437 def OldContents(self):
438 """Returns an iterator over the lines in the old version of file. 438 """Returns an iterator over the lines in the old version of file.
439 439
440 The old version is the file in depot, i.e. the "left hand side". 440 The old version is the file in depot, i.e. the "left hand side".
441 """ 441 """
442 raise NotImplementedError() # Implement when needed 442 raise NotImplementedError() # Implement when needed
443 443
444 def OldFileTempPath(self): 444 def OldFileTempPath(self):
445 """Returns the path on local disk where the old contents resides. 445 """Returns the path on local disk where the old contents resides.
(...skipping 11 matching lines...) Expand all
457 class SvnAffectedFile(AffectedFile): 457 class SvnAffectedFile(AffectedFile):
458 """Representation of a file in a change out of a Subversion checkout.""" 458 """Representation of a file in a change out of a Subversion checkout."""
459 459
460 def __init__(self, *args, **kwargs): 460 def __init__(self, *args, **kwargs):
461 AffectedFile.__init__(self, *args, **kwargs) 461 AffectedFile.__init__(self, *args, **kwargs)
462 self._server_path = None 462 self._server_path = None
463 self._is_text_file = None 463 self._is_text_file = None
464 464
465 def ServerPath(self): 465 def ServerPath(self):
466 if self._server_path is None: 466 if self._server_path is None:
467 self._server_path = scm.SVN.CaptureInfo( 467 self._server_path = gclient_scm.CaptureSVNInfo(
468 self.AbsoluteLocalPath()).get('URL', '') 468 self.AbsoluteLocalPath()).get('URL', '')
469 return self._server_path 469 return self._server_path
470 470
471 def IsDirectory(self): 471 def IsDirectory(self):
472 if self._is_directory is None: 472 if self._is_directory is None:
473 path = self.AbsoluteLocalPath() 473 path = self.AbsoluteLocalPath()
474 if os.path.exists(path): 474 if os.path.exists(path):
475 # Retrieve directly from the file system; it is much faster than 475 # Retrieve directly from the file system; it is much faster than
476 # querying subversion, especially on Windows. 476 # querying subversion, especially on Windows.
477 self._is_directory = os.path.isdir(path) 477 self._is_directory = os.path.isdir(path)
478 else: 478 else:
479 self._is_directory = scm.SVN.CaptureInfo( 479 self._is_directory = gclient_scm.CaptureSVNInfo(
480 path).get('Node Kind') in ('dir', 'directory') 480 path).get('Node Kind') in ('dir', 'directory')
481 return self._is_directory 481 return self._is_directory
482 482
483 def Property(self, property_name): 483 def Property(self, property_name):
484 if not property_name in self._properties: 484 if not property_name in self._properties:
485 self._properties[property_name] = scm.SVN.GetFileProperty( 485 self._properties[property_name] = gcl.GetSVNFileProperty(
486 self.AbsoluteLocalPath(), property_name).rstrip() 486 self.AbsoluteLocalPath(), property_name).rstrip()
487 return self._properties[property_name] 487 return self._properties[property_name]
488 488
489 def IsTextFile(self): 489 def IsTextFile(self):
490 if self._is_text_file is None: 490 if self._is_text_file is None:
491 if self.Action() == 'D': 491 if self.Action() == 'D':
492 # A deleted file is not a text file. 492 # A deleted file is not a text file.
493 self._is_text_file = False 493 self._is_text_file = False
494 elif self.IsDirectory(): 494 elif self.IsDirectory():
495 self._is_text_file = False 495 self._is_text_file = False
496 else: 496 else:
497 mime_type = scm.SVN.GetFileProperty(self.AbsoluteLocalPath(), 497 mime_type = gcl.GetSVNFileProperty(self.AbsoluteLocalPath(),
498 'svn:mime-type') 498 'svn:mime-type')
499 self._is_text_file = (not mime_type or mime_type.startswith('text/')) 499 self._is_text_file = (not mime_type or mime_type.startswith('text/'))
500 return self._is_text_file 500 return self._is_text_file
501 501
502 502
503 class GitAffectedFile(AffectedFile): 503 class GitAffectedFile(AffectedFile):
504 """Representation of a file in a change out of a git checkout.""" 504 """Representation of a file in a change out of a git checkout."""
505 505
506 def __init__(self, *args, **kwargs): 506 def __init__(self, *args, **kwargs):
507 AffectedFile.__init__(self, *args, **kwargs) 507 AffectedFile.__init__(self, *args, **kwargs)
508 self._server_path = None 508 self._server_path = None
(...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after
802 executer = GetTrySlavesExecuter() 802 executer = GetTrySlavesExecuter()
803 if default_presubmit: 803 if default_presubmit:
804 if verbose: 804 if verbose:
805 output_stream.write("Running default presubmit script.\n") 805 output_stream.write("Running default presubmit script.\n")
806 results += executer.ExecPresubmitScript(default_presubmit) 806 results += executer.ExecPresubmitScript(default_presubmit)
807 for filename in presubmit_files: 807 for filename in presubmit_files:
808 filename = os.path.abspath(filename) 808 filename = os.path.abspath(filename)
809 if verbose: 809 if verbose:
810 output_stream.write("Running %s\n" % filename) 810 output_stream.write("Running %s\n" % filename)
811 # Accept CRLF presubmit script. 811 # Accept CRLF presubmit script.
812 presubmit_script = gclient_utils.FileRead(filename, 'rU') 812 presubmit_script = gcl.ReadFile(filename, 'rU')
813 results += executer.ExecPresubmitScript(presubmit_script) 813 results += executer.ExecPresubmitScript(presubmit_script)
814 814
815 slaves = list(set(results)) 815 slaves = list(set(results))
816 if slaves and verbose: 816 if slaves and verbose:
817 output_stream.write(', '.join(slaves)) 817 output_stream.write(', '.join(slaves))
818 output_stream.write('\n') 818 output_stream.write('\n')
819 return slaves 819 return slaves
820 820
821 821
822 class PresubmitExecuter(object): 822 class PresubmitExecuter(object):
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
918 if default_presubmit: 918 if default_presubmit:
919 if verbose: 919 if verbose:
920 output_stream.write("Running default presubmit script.\n") 920 output_stream.write("Running default presubmit script.\n")
921 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py') 921 fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py')
922 results += executer.ExecPresubmitScript(default_presubmit, fake_path) 922 results += executer.ExecPresubmitScript(default_presubmit, fake_path)
923 for filename in presubmit_files: 923 for filename in presubmit_files:
924 filename = os.path.abspath(filename) 924 filename = os.path.abspath(filename)
925 if verbose: 925 if verbose:
926 output_stream.write("Running %s\n" % filename) 926 output_stream.write("Running %s\n" % filename)
927 # Accept CRLF presubmit script. 927 # Accept CRLF presubmit script.
928 presubmit_script = gclient_utils.FileRead(filename, 'rU') 928 presubmit_script = gcl.ReadFile(filename, 'rU')
929 results += executer.ExecPresubmitScript(presubmit_script, filename) 929 results += executer.ExecPresubmitScript(presubmit_script, filename)
930 930
931 errors = [] 931 errors = []
932 notifications = [] 932 notifications = []
933 warnings = [] 933 warnings = []
934 for result in results: 934 for result in results:
935 if not result.IsFatal() and not result.ShouldPrompt(): 935 if not result.IsFatal() and not result.ShouldPrompt():
936 notifications.append(result) 936 notifications.append(result)
937 elif result.ShouldPrompt(): 937 elif result.ShouldPrompt():
938 warnings.append(result) 938 warnings.append(result)
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
1015 options, args = parser.parse_args(argv[1:]) 1015 options, args = parser.parse_args(argv[1:])
1016 if not options.root: 1016 if not options.root:
1017 options.root = os.getcwd() 1017 options.root = os.getcwd()
1018 if os.path.isdir(os.path.join(options.root, '.git')): 1018 if os.path.isdir(os.path.join(options.root, '.git')):
1019 change_class = GitChange 1019 change_class = GitChange
1020 if not options.files: 1020 if not options.files:
1021 if args: 1021 if args:
1022 options.files = ParseFiles(args, options.recursive) 1022 options.files = ParseFiles(args, options.recursive)
1023 else: 1023 else:
1024 # Grab modified files. 1024 # Grab modified files.
1025 options.files = scm.GIT.CaptureStatus([options.root]) 1025 options.files = gclient_scm.CaptureGitStatus([options.root])
1026 elif os.path.isdir(os.path.join(options.root, '.svn')): 1026 elif os.path.isdir(os.path.join(options.root, '.svn')):
1027 change_class = SvnChange 1027 change_class = SvnChange
1028 if not options.files: 1028 if not options.files:
1029 if args: 1029 if args:
1030 options.files = ParseFiles(args, options.recursive) 1030 options.files = ParseFiles(args, options.recursive)
1031 else: 1031 else:
1032 # Grab modified files. 1032 # Grab modified files.
1033 options.files = scm.SVN.CaptureStatus([options.root]) 1033 options.files = gclient_scm.CaptureSVNStatus([options.root])
1034 else: 1034 else:
1035 # Doesn't seem under source control. 1035 # Doesn't seem under source control.
1036 change_class = Change 1036 change_class = Change
1037 if options.verbose: 1037 if options.verbose:
1038 if len(options.files) != 1: 1038 if len(options.files) != 1:
1039 print "Found %d files." % len(options.files) 1039 print "Found %d files." % len(options.files)
1040 else: 1040 else:
1041 print "Found 1 file." 1041 print "Found 1 file."
1042 return not DoPresubmitChecks(change_class(options.name, 1042 return not DoPresubmitChecks(change_class(options.name,
1043 options.description, 1043 options.description,
1044 options.root, 1044 options.root,
1045 options.files, 1045 options.files,
1046 options.issue, 1046 options.issue,
1047 options.patchset), 1047 options.patchset),
1048 options.commit, 1048 options.commit,
1049 options.verbose, 1049 options.verbose,
1050 sys.stdout, 1050 sys.stdout,
1051 sys.stdin, 1051 sys.stdin,
1052 options.default_presubmit, 1052 options.default_presubmit,
1053 options.may_prompt) 1053 options.may_prompt)
1054 1054
1055 1055
1056 if __name__ == '__main__': 1056 if __name__ == '__main__':
1057 sys.exit(Main(sys.argv)) 1057 sys.exit(Main(sys.argv))
OLDNEW
« no previous file with comments | « git_cl_hooks.py ('k') | revert.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698