| Index: tools/checkperms/checkperms.py
|
| diff --git a/tools/checkperms/checkperms.py b/tools/checkperms/checkperms.py
|
| index c5fb7c46aee3a73fa4fb30ca9653b204a71a9b35..1eb16d37c3e74f3e1a20303db6055c88fc974764 100755
|
| --- a/tools/checkperms/checkperms.py
|
| +++ b/tools/checkperms/checkperms.py
|
| @@ -9,85 +9,122 @@ Some developers have broken SCM configurations that flip the svn:executable
|
| permission on for no good reason. Unix developers who run ls --color will then
|
| see .cc files in green and get confused.
|
|
|
| -To ignore a particular file, add it to WHITELIST_FILES.
|
| -To ignore a particular extension, add it to WHITELIST_EXTENSIONS.
|
| -To ignore whatever regexps your heart desires, add it WHITELIST_REGEX.
|
| +- For file extensions that must be executable, add it to EXECUTABLE_EXTENSIONS.
|
| +- For file extensions that must not be executable, add it to
|
| + NOT_EXECUTABLE_EXTENSIONS.
|
| +- To ignore all the files inside a directory, add it to IGNORED_PATHS.
|
| +- For file base name with ambiguous state and that should not be checked for
|
| + shebang, add it to IGNORED_FILENAMES.
|
| +
|
| +Any file not matching the above will be opened and looked if it has a shebang.
|
| +It this doesn't match the executable bit on the file, the file will be flagged.
|
|
|
| Note that all directory separators must be slashes (Unix-style) and not
|
| backslashes. All directories should be relative to the source root and all
|
| file paths should be only lowercase.
|
| """
|
|
|
| +import logging
|
| import optparse
|
| import os
|
| -import pipes
|
| -import re
|
| import stat
|
| +import subprocess
|
| import sys
|
|
|
| #### USER EDITABLE SECTION STARTS HERE ####
|
|
|
| -# Files with these extensions are allowed to have executable permissions.
|
| -WHITELIST_EXTENSIONS = [
|
| - 'bash',
|
| - 'bat',
|
| - 'dll',
|
| - 'dylib',
|
| - 'exe',
|
| - 'pl',
|
| - 'py',
|
| - 'rb',
|
| - 'sed',
|
| - 'sh',
|
| -]
|
| -
|
| -# Files that end the following paths are whitelisted too.
|
| -WHITELIST_FILES = [
|
| - '/build/gyp_chromium',
|
| - '/build/linux/dump_app_syms',
|
| - '/build/linux/pkg-config-wrapper',
|
| - '/build/mac/strip_from_xcode',
|
| - '/build/mac/strip_save_dsym',
|
| - '/chrome/installer/mac/pkg-dmg',
|
| - '/chrome/test/data/webrtc/linux/peerconnection_server',
|
| - '/chrome/tools/build/linux/chrome-wrapper',
|
| - '/chrome/tools/build/mac/build_app_dmg',
|
| - '/chrome/tools/build/mac/clean_up_old_versions',
|
| - '/chrome/tools/build/mac/dump_product_syms',
|
| - '/chrome/tools/build/mac/generate_localizer',
|
| - '/chrome/tools/build/mac/make_sign_sh',
|
| - '/chrome/tools/build/mac/verify_order',
|
| - '/o3d/build/gyp_o3d',
|
| - '/o3d/gypbuild',
|
| - '/o3d/installer/linux/debian.in/rules',
|
| - '/third_party/icu/source/runconfigureicu',
|
| - '/third_party/gold/gold32',
|
| - '/third_party/gold/gold64',
|
| - '/third_party/gold/ld',
|
| - '/third_party/gold/ld.bfd',
|
| - '/third_party/lcov/bin/gendesc',
|
| - '/third_party/lcov/bin/genhtml',
|
| - '/third_party/lcov/bin/geninfo',
|
| - '/third_party/lcov/bin/genpng',
|
| - '/third_party/lcov/bin/lcov',
|
| - '/third_party/lcov/bin/mcov',
|
| - '/third_party/lcov-1.9/bin/gendesc',
|
| - '/third_party/lcov-1.9/bin/genhtml',
|
| - '/third_party/lcov-1.9/bin/geninfo',
|
| - '/third_party/lcov-1.9/bin/genpng',
|
| - '/third_party/lcov-1.9/bin/lcov',
|
| - '/third_party/libxml/linux/xml2-config',
|
| - '/third_party/lzma_sdk/executable/7za.exe',
|
| - '/third_party/swig/linux/swig',
|
| - '/third_party/tcmalloc/chromium/src/pprof',
|
| - '/tools/deep_memory_profiler/dmprof',
|
| - '/tools/git/post-checkout',
|
| - '/tools/git/post-merge',
|
| - '/tools/ld_bfd/ld',
|
| -]
|
| +# Files with these extensions must have executable bit set.
|
| +EXECUTABLE_EXTENSIONS = (
|
| + 'bat',
|
| + 'dll',
|
| + 'dylib',
|
| + 'exe',
|
| +)
|
| +
|
| +# These files must have executable bit set.
|
| +EXECUTABLE_PATHS = (
|
| + # TODO(maruel): Detect ELF files.
|
| + 'chrome/test/data/webrtc/linux/peerconnection_server',
|
| + 'chrome/installer/mac/sign_app.sh.in',
|
| + 'chrome/installer/mac/sign_versioned_dir.sh.in',
|
| +)
|
| +
|
| +# These files must not have the executable bit set. This is mainly a performance
|
| +# optimization as these files are not checked for shebang. The list was
|
| +# partially generated from:
|
| +# git ls-files | grep "\\." | sed 's/.*\.//' | sort | uniq -c | sort -b -g
|
| +NON_EXECUTABLE_EXTENSIONS = (
|
| + '1',
|
| + '3ds',
|
| + 'S',
|
| + 'am',
|
| + 'applescript',
|
| + 'asm',
|
| + 'c',
|
| + 'cc',
|
| + 'cfg',
|
| + 'chromium',
|
| + 'cpp',
|
| + 'crx',
|
| + 'cs',
|
| + 'css',
|
| + 'cur',
|
| + 'def',
|
| + 'der',
|
| + 'expected',
|
| + 'gif',
|
| + 'grd',
|
| + 'gyp',
|
| + 'gypi',
|
| + 'h',
|
| + 'hh',
|
| + 'htm',
|
| + 'html',
|
| + 'hyph',
|
| + 'ico',
|
| + 'idl',
|
| + 'java',
|
| + 'jpg',
|
| + 'js',
|
| + 'json',
|
| + 'm',
|
| + 'm4',
|
| + 'mm',
|
| + 'mms',
|
| + 'mock-http-headers',
|
| + 'nmf',
|
| + 'onc',
|
| + 'pat',
|
| + 'patch',
|
| + 'pdf',
|
| + 'pem',
|
| + 'plist',
|
| + 'png',
|
| + 'proto',
|
| + 'rc',
|
| + 'rfx',
|
| + 'rgs',
|
| + 'rules',
|
| + 'spec',
|
| + 'sql',
|
| + 'srpc',
|
| + 'svg',
|
| + 'tcl',
|
| + 'test',
|
| + 'tga',
|
| + 'txt',
|
| + 'vcproj',
|
| + 'vsprops',
|
| + 'webm',
|
| + 'word',
|
| + 'xib',
|
| + 'xml',
|
| + 'xtb',
|
| + 'zip',
|
| +)
|
|
|
| # File names that are always whitelisted. (These are all autoconf spew.)
|
| -WHITELIST_FILENAMES = set((
|
| +IGNORED_FILENAMES = (
|
| 'config.guess',
|
| 'config.sub',
|
| 'configure',
|
| @@ -95,256 +132,344 @@ WHITELIST_FILENAMES = set((
|
| 'install-sh',
|
| 'missing',
|
| 'mkinstalldirs',
|
| - 'scons',
|
| 'naclsdk',
|
| -))
|
| -
|
| -# File paths that contain these regexps will be whitelisted as well.
|
| -WHITELIST_REGEX = [
|
| - re.compile('/third_party/openssl/'),
|
| - re.compile('/third_party/sqlite/'),
|
| - re.compile('/third_party/xdg-utils/'),
|
| - re.compile('/third_party/yasm/source/patched-yasm/config'),
|
| - re.compile('/third_party/ffmpeg/tools'),
|
| -]
|
| + 'scons',
|
| +)
|
| +
|
| +# File paths starting with one of these will be ignored as well.
|
| +IGNORED_PATHS = (
|
| + # TODO(maruel): Detect ELF files.
|
| + 'chrome/test/data/webrtc/linux/peerconnection_server',
|
| + 'chrome/installer/mac/sign_app.sh.in',
|
| + 'chrome/installer/mac/sign_versioned_dir.sh.in',
|
| + 'native_client_sdk/src/build_tools/sdk_tools/third_party/',
|
| + 'out/',
|
| + # TODO(maruel): Fix these.
|
| + 'third_party/android_testrunner/',
|
| + 'third_party/closure_linter/',
|
| + 'third_party/devscripts/licensecheck.pl.vanilla',
|
| + 'third_party/hyphen/',
|
| + 'third_party/jemalloc/',
|
| + 'third_party/lcov-1.9/contrib/galaxy/conglomerate_functions.pl',
|
| + 'third_party/lcov-1.9/contrib/galaxy/gen_makefile.sh',
|
| + 'third_party/lcov/contrib/galaxy/conglomerate_functions.pl',
|
| + 'third_party/lcov/contrib/galaxy/gen_makefile.sh',
|
| + 'third_party/libevent/autogen.sh',
|
| + 'third_party/libevent/test/test.sh',
|
| + 'third_party/libxml/linux/xml2-config',
|
| + 'third_party/libxml/src/ltmain.sh',
|
| + 'third_party/mesa/',
|
| + 'third_party/protobuf/',
|
| + 'third_party/python_gflags/gflags.py',
|
| + 'third_party/sqlite/',
|
| + 'third_party/talloc/script/mksyms.sh',
|
| + 'third_party/tcmalloc/',
|
| + 'third_party/tlslite/setup.py',
|
| +)
|
|
|
| #### USER EDITABLE SECTION ENDS HERE ####
|
|
|
| -WHITELIST_EXTENSIONS_REGEX = re.compile(r'\.(%s)' %
|
| - '|'.join(WHITELIST_EXTENSIONS))
|
| +assert set(EXECUTABLE_EXTENSIONS) & set(NON_EXECUTABLE_EXTENSIONS) == set()
|
|
|
| -WHITELIST_FILES_REGEX = re.compile(r'(%s)$' % '|'.join(WHITELIST_FILES))
|
|
|
| -# Set to true for more output. This is set by the command line options.
|
| -VERBOSE = False
|
| +def capture(cmd, cwd):
|
| + """Returns the output of a command.
|
|
|
| -# Using forward slashes as directory separators, ending in a forward slash.
|
| -# Set by the command line options.
|
| -BASE_DIRECTORY = ''
|
| + Ignores the error code or stderr.
|
| + """
|
| + logging.debug('%s; cwd=%s' % (' '.join(cmd), cwd))
|
| + env = os.environ.copy()
|
| + env['LANGUAGE'] = 'en_US.UTF-8'
|
| + p = subprocess.Popen(
|
| + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd, env=env)
|
| + return p.communicate()[0]
|
|
|
| -# The default if BASE_DIRECTORY is not set on the command line.
|
| -DEFAULT_BASE_DIRECTORY = '../../..'
|
|
|
| -# The directories which contain the sources managed by git.
|
| -GIT_SOURCE_DIRECTORY = set()
|
| +def get_svn_info(dir_path):
|
| + """Returns svn meta-data for a svn checkout."""
|
| + if not os.path.isdir(dir_path):
|
| + return {}
|
| + out = capture(['svn', 'info', '.', '--non-interactive'], dir_path)
|
| + return dict(l.split(': ', 1) for l in out.splitlines() if l)
|
|
|
| -# The SVN repository url.
|
| -SVN_REPO_URL = ''
|
|
|
| -# Whether we are using SVN or GIT.
|
| -IS_SVN = True
|
| +def get_svn_url(dir_path):
|
| + return get_svn_info(dir_path).get('URL')
|
|
|
| -# Executable permission mask
|
| -EXECUTABLE_PERMISSION = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
|
|
|
| +def get_svn_root(dir_path):
|
| + """Returns the svn checkout root or None."""
|
| + svn_url = get_svn_url(dir_path)
|
| + if not svn_url:
|
| + return None
|
| + logging.info('svn url: %s' % svn_url)
|
| + while True:
|
| + parent = os.path.dirname(dir_path)
|
| + if parent == dir_path:
|
| + return None
|
| + svn_url = svn_url.rsplit('/', 1)[0]
|
| + if svn_url != get_svn_url(parent):
|
| + return dir_path
|
| + dir_path = parent
|
| +
|
| +
|
| +def get_git_root(dir_path):
|
| + """Returns the git checkout root or None."""
|
| + root = capture(['git', 'rev-parse', '--show-toplevel'], dir_path).strip()
|
| + if root:
|
| + return root
|
| +
|
| +
|
| +def is_ignored(rel_path):
|
| + """Returns True if rel_path is in our whitelist of files to ignore."""
|
| + rel_path = rel_path.lower()
|
| + return (
|
| + os.path.basename(rel_path) in IGNORED_FILENAMES or
|
| + rel_path.startswith(IGNORED_PATHS))
|
| +
|
| +
|
| +def must_be_executable(rel_path):
|
| + """The file name represents a file type that must have the executable bit
|
| + set.
|
| + """
|
| + return (
|
| + os.path.splitext(rel_path)[1][1:].lower() in EXECUTABLE_EXTENSIONS or
|
| + rel_path in EXECUTABLE_PATHS)
|
|
|
| -def IsWhiteListed(file_path):
|
| - """Returns True if file_path is in our whitelist of files to ignore."""
|
| - if WHITELIST_EXTENSIONS_REGEX.match(os.path.splitext(file_path)[1]):
|
| - return True
|
| - if WHITELIST_FILES_REGEX.search(file_path):
|
| - return True
|
| - if os.path.basename(file_path) in WHITELIST_FILENAMES:
|
| - return True
|
| - for regex in WHITELIST_REGEX:
|
| - if regex.search(file_path):
|
| - return True
|
| - return False
|
|
|
| +def must_not_be_executable(rel_path):
|
| + """The file name represents a file type that must not have the executable
|
| + bit set.
|
| + """
|
| + return os.path.splitext(rel_path)[1][1:].lower() in NON_EXECUTABLE_EXTENSIONS
|
|
|
| -def CheckFile(file_path):
|
| - """Checks file_path's permissions.
|
|
|
| - Args:
|
| - file_path: The file path to check.
|
| +def has_executable_bit(full_path):
|
| + """Returns if any executable bit is set."""
|
| + permission = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
|
| + return bool(permission & os.stat(full_path).st_mode)
|
|
|
| - Returns:
|
| - Either a string describing the error if there was one, or None if the file
|
| - checked out OK.
|
| - """
|
| - if VERBOSE:
|
| - print 'Checking file: ' + file_path
|
|
|
| - file_path_lower = file_path.lower()
|
| - if IsWhiteListed(file_path_lower):
|
| - return None
|
| +def has_shebang(full_path):
|
| + """Returns if the file starts with #!/.
|
|
|
| - # Not whitelisted, stat the file and check permissions.
|
| - try:
|
| - st_mode = os.stat(file_path).st_mode
|
| - except IOError, e:
|
| - return 'Failed to stat file: %s' % e
|
| - except OSError, e:
|
| - return 'Failed to stat file: %s' % e
|
| -
|
| - if EXECUTABLE_PERMISSION & st_mode:
|
| - # Look if the file starts with #!/
|
| - with open(file_path, 'rb') as f:
|
| - if f.read(3) == '#!/':
|
| - # That's fine.
|
| - return None
|
| - # TODO(maruel): Check that non-executable file do not start with a shebang.
|
| - error = 'Contains executable permission'
|
| - if VERBOSE:
|
| - return '%s: %06o' % (error, st_mode)
|
| - return error
|
| - return None
|
| -
|
| -
|
| -def ShouldCheckDirectory(dir_path):
|
| - """Determine if we should check the content of dir_path."""
|
| - if not IS_SVN:
|
| - return dir_path in GIT_SOURCE_DIRECTORY
|
| - repo_url = GetSvnRepositoryRoot(dir_path)
|
| - if not repo_url:
|
| - return False
|
| - return repo_url == SVN_REPO_URL
|
| -
|
| -
|
| -def CheckDirectory(dir_path):
|
| - """Check the files in dir_path; recursively check its subdirectories."""
|
| - # Collect a list of all files and directories to check.
|
| - files_to_check = []
|
| - dirs_to_check = []
|
| - success = True
|
| - contents = os.listdir(dir_path)
|
| - for cur in contents:
|
| - full_path = os.path.join(dir_path, cur)
|
| - if os.path.isdir(full_path) and ShouldCheckDirectory(full_path):
|
| - dirs_to_check.append(full_path)
|
| - elif os.path.isfile(full_path):
|
| - files_to_check.append(full_path)
|
| -
|
| - # First check all files in this directory.
|
| - for cur_file in files_to_check:
|
| - file_status = CheckFile(cur_file)
|
| - if file_status is not None:
|
| - print 'ERROR in %s\n%s' % (cur_file, file_status)
|
| - success = False
|
| -
|
| - # Next recurse into the subdirectories.
|
| - for cur_dir in dirs_to_check:
|
| - if not CheckDirectory(cur_dir):
|
| - success = False
|
| - return success
|
| -
|
| -
|
| -def GetGitSourceDirectory(root):
|
| - """Returns a set of the directories to be checked.
|
| -
|
| - Args:
|
| - root: The repository root where a .git directory must exist.
|
| -
|
| - Returns:
|
| - A set of directories which contain sources managed by git.
|
| + file_path is the absolute path to the file.
|
| """
|
| - git_source_directory = set()
|
| - popen_out = os.popen('cd %s && git ls-files --full-name .' %
|
| - pipes.quote(root))
|
| - for line in popen_out:
|
| - dir_path = os.path.join(root, os.path.dirname(line))
|
| - git_source_directory.add(dir_path)
|
| - git_source_directory.add(root)
|
| - return git_source_directory
|
| + with open(full_path, 'rb') as f:
|
| + return f.read(3) == '#!/'
|
| +
|
| +
|
| +class ApiBase(object):
|
| + def __init__(self, root_dir, bare_output):
|
| + self.root_dir = root_dir
|
| + self.bare_output = bare_output
|
| + self.count = 0
|
| + self.count_shebang = 0
|
| +
|
| + def check_file(self, rel_path):
|
| + """Checks file_path's permissions and returns an error if it is
|
| + inconsistent.
|
| +
|
| + It is assumed that the file is not ignored by is_ignored().
|
| +
|
| + If the file name is matched with must_be_executable() or
|
| + must_not_be_executable(), only its executable bit is checked.
|
| + Otherwise, the 3 first bytes of the file are read to verify if it has a
|
| + shebang and compares this with the executable bit on the file.
|
| + """
|
| + logging.debug('check_file(%s)' % rel_path)
|
| + self.count += 1
|
| +
|
| + full_path = os.path.join(self.root_dir, rel_path)
|
| + try:
|
| + bit = has_executable_bit(full_path)
|
| + except OSError:
|
| + # It's faster to catch exception than call os.path.islink(). Chromium
|
| + # tree happens to have invalid symlinks under
|
| + # third_party/openssl/openssl/test/.
|
| + return None
|
| +
|
| + if must_be_executable(rel_path):
|
| + if not bit:
|
| + if self.bare_output:
|
| + return rel_path
|
| + return '%s: Must have executable bit set' % rel_path
|
| + return
|
| + if must_not_be_executable(rel_path):
|
| + if bit:
|
| + if self.bare_output:
|
| + return rel_path
|
| + return '%s: Must not have executable bit set' % rel_path
|
| + return
|
| +
|
| + # For the others, it depends on the shebang.
|
| + shebang = has_shebang(full_path)
|
| + self.count_shebang += 1
|
| + if bit != shebang:
|
| + if self.bare_output:
|
| + return rel_path
|
| + if bit:
|
| + return '%s: Has executable bit but not shebang' % rel_path
|
| + else:
|
| + return '%s: Has shebang but not executable bit' % rel_path
|
| +
|
| + def check_dir(self, rel_path):
|
| + return self.check(rel_path)
|
| +
|
| + def check(self, start_dir):
|
| + """Check the files in start_dir, recursively check its subdirectories."""
|
| + errors = []
|
| + items = self.list_dir(start_dir)
|
| + logging.info('check(%s) -> %d' % (start_dir, len(items)))
|
| + for item in items:
|
| + full_path = os.path.join(self.root_dir, start_dir, item)
|
| + rel_path = full_path[len(self.root_dir) + 1:]
|
| + if is_ignored(rel_path):
|
| + continue
|
| + if os.path.isdir(full_path):
|
| + # Depth first.
|
| + errors.extend(self.check_dir(rel_path))
|
| + else:
|
| + error = self.check_file(rel_path)
|
| + if error:
|
| + errors.append(error)
|
| + return errors
|
| +
|
| + def list_dir(self, start_dir):
|
| + """Lists all the files and directory inside start_dir."""
|
| + return sorted(
|
| + x for x in os.listdir(os.path.join(self.root_dir, start_dir))
|
| + if not x.startswith('.')
|
| + )
|
| +
|
| +
|
| +class ApiSvnQuick(ApiBase):
|
| + """Returns all files in svn-versioned directories, independent of the fact if
|
| + they are versionned.
|
| +
|
| + Uses svn info in each directory to determine which directories should be
|
| + crawled.
|
| + """
|
| + def __init__(self, *args):
|
| + super(ApiSvnQuick, self).__init__(*args)
|
| + self.url = get_svn_url(self.root_dir)
|
|
|
| + def check_dir(self, rel_path):
|
| + url = self.url + '/' + rel_path
|
| + if get_svn_url(os.path.join(self.root_dir, rel_path)) != url:
|
| + return []
|
| + return super(ApiSvnQuick, self).check_dir(rel_path)
|
|
|
| -def GetSvnRepositoryRoot(dir_path):
|
| - """Returns the repository root for a directory.
|
|
|
| - Args:
|
| - dir_path: A directory where a .svn subdirectory should exist.
|
| +class ApiAllFilesAtOnceBase(ApiBase):
|
| + _files = None
|
| +
|
| + def list_dir(self, start_dir):
|
| + """Lists all the files and directory inside start_dir."""
|
| + if self._files is None:
|
| + self._files = sorted(self._get_all_files())
|
| + if not self.bare_output:
|
| + print 'Found %s files' % len(self._files)
|
| + start_dir = start_dir[len(self.root_dir) + 1:]
|
| + return [
|
| + x[len(start_dir):] for x in self._files if x.startswith(start_dir)
|
| + ]
|
| +
|
| + def _get_all_files(self):
|
| + """Lists all the files and directory inside self._root_dir."""
|
| + raise NotImplementedError()
|
|
|
| - Returns:
|
| - The svn repository that contains dir_path or None.
|
| - """
|
| - svn_dir = os.path.join(dir_path, '.svn')
|
| - if not os.path.isdir(svn_dir):
|
| - return None
|
| - popen_out = os.popen('cd %s && svn info' % pipes.quote(dir_path))
|
| - for line in popen_out:
|
| - if line.startswith('Repository Root: '):
|
| - return line[len('Repository Root: '):].rstrip()
|
| - return None
|
|
|
| +class ApiSvn(ApiAllFilesAtOnceBase):
|
| + """Returns all the subversion controlled files.
|
|
|
| -def main(argv):
|
| + Warning: svn ls is abnormally slow.
|
| + """
|
| + def _get_all_files(self):
|
| + cmd = ['svn', 'ls', '--non-interactive', '--recursive']
|
| + return (
|
| + x for x in capture(cmd, self.root_dir).splitlines()
|
| + if not x.endswith(os.path.sep))
|
| +
|
| +
|
| +class ApiGit(ApiAllFilesAtOnceBase):
|
| + def _get_all_files(self):
|
| + return capture(['git', 'ls-files'], cwd=self.root_dir).splitlines()
|
| +
|
| +
|
| +def get_scm(dir_path, bare):
|
| + """Returns a properly configured ApiBase instance."""
|
| + cwd = os.getcwd()
|
| + root = get_svn_root(dir_path or cwd)
|
| + if root:
|
| + if not bare:
|
| + print('Found subversion checkout at %s' % root)
|
| + return ApiSvnQuick(dir_path or root, bare)
|
| + root = get_git_root(dir_path or cwd)
|
| + if root:
|
| + if not bare:
|
| + print('Found git repository at %s' % root)
|
| + return ApiGit(dir_path or root, bare)
|
| +
|
| + # Returns a non-scm aware checker.
|
| + if not bare:
|
| + print('Failed to determine the SCM for %s' % dir_path)
|
| + return ApiBase(dir_path or cwd, bare)
|
| +
|
| +
|
| +def main():
|
| usage = """Usage: python %prog [--root <root>] [tocheck]
|
| tocheck Specifies the directory, relative to root, to check. This defaults
|
| to "." so it checks everything.
|
|
|
| Examples:
|
| - python checkperms.py
|
| - python checkperms.py --root /path/to/source chrome"""
|
| -
|
| - option_parser = optparse.OptionParser(usage=usage)
|
| - option_parser.add_option('--root', dest='base_directory',
|
| - default=DEFAULT_BASE_DIRECTORY,
|
| - help='Specifies the repository root. This defaults '
|
| - 'to %default relative to the script file, which '
|
| - 'will normally be the repository root.')
|
| - option_parser.add_option('-v', '--verbose', action='store_true',
|
| - help='Print debug logging')
|
| - options, args = option_parser.parse_args()
|
| -
|
| - global VERBOSE
|
| - if options.verbose:
|
| - VERBOSE = True
|
| -
|
| - # Optional base directory of the repository.
|
| - global BASE_DIRECTORY
|
| - if (not options.base_directory or
|
| - options.base_directory == DEFAULT_BASE_DIRECTORY):
|
| - BASE_DIRECTORY = os.path.abspath(
|
| - os.path.join(os.path.abspath(argv[0]), DEFAULT_BASE_DIRECTORY))
|
| - else:
|
| - BASE_DIRECTORY = os.path.abspath(argv[2])
|
| -
|
| - # Figure out which directory we have to check.
|
| - if not args:
|
| - # No directory to check specified, use the repository root.
|
| - start_dir = BASE_DIRECTORY
|
| - elif len(args) == 1:
|
| - # Directory specified. Start here. It's supposed to be relative to the
|
| - # base directory.
|
| - start_dir = os.path.abspath(os.path.join(BASE_DIRECTORY, args[0]))
|
| + python %prog
|
| + python %prog --root /path/to/source chrome"""
|
| +
|
| + parser = optparse.OptionParser(usage=usage)
|
| + parser.add_option(
|
| + '--root',
|
| + help='Specifies the repository root. This defaults '
|
| + 'to the checkout repository root')
|
| + parser.add_option(
|
| + '-v', '--verbose', action='count', default=0, help='Print debug logging')
|
| + parser.add_option(
|
| + '--bare',
|
| + action='store_true',
|
| + default=False,
|
| + help='Prints the bare filename triggering the checks')
|
| + options, args = parser.parse_args()
|
| +
|
| + levels = [logging.ERROR, logging.INFO, logging.DEBUG]
|
| + logging.basicConfig(level=levels[min(len(levels) - 1, options.verbose)])
|
| +
|
| + if len(args) > 1:
|
| + parser.error('Too many arguments used')
|
| +
|
| + if options.root:
|
| + options.root = os.path.abspath(options.root)
|
| +
|
| + api = get_scm(options.root, options.bare)
|
| + if args:
|
| + start_dir = args[0]
|
| else:
|
| - # More than one argument, we don't handle this.
|
| - option_parser.print_help()
|
| - return 1
|
| + start_dir = api.root_dir
|
|
|
| - print 'Using base directory:', BASE_DIRECTORY
|
| - print 'Checking directory:', start_dir
|
| -
|
| - BASE_DIRECTORY = BASE_DIRECTORY.replace('\\', '/')
|
| - start_dir = start_dir.replace('\\', '/')
|
| -
|
| - success = True
|
| - if os.path.exists(os.path.join(BASE_DIRECTORY, '.svn')):
|
| - global SVN_REPO_URL
|
| - SVN_REPO_URL = GetSvnRepositoryRoot(BASE_DIRECTORY)
|
| - if not SVN_REPO_URL:
|
| - print 'Cannot determine the SVN repo URL'
|
| - success = False
|
| - elif os.path.exists(os.path.join(BASE_DIRECTORY, '.git')):
|
| - global IS_SVN
|
| - IS_SVN = False
|
| - global GIT_SOURCE_DIRECTORY
|
| - GIT_SOURCE_DIRECTORY = GetGitSourceDirectory(BASE_DIRECTORY)
|
| - if not GIT_SOURCE_DIRECTORY:
|
| - print 'Cannot determine the list of GIT directories'
|
| - success = False
|
| - else:
|
| - print 'Cannot determine the SCM used in %s' % BASE_DIRECTORY
|
| - success = False
|
| + errors = api.check(start_dir)
|
| +
|
| + if not options.bare:
|
| + print 'Processed %s files, %d files where tested for shebang' % (
|
| + api.count, api.count_shebang)
|
|
|
| - if success:
|
| - success = CheckDirectory(start_dir)
|
| - if not success:
|
| - print '\nFAILED\n'
|
| + if errors:
|
| + if not options.bare:
|
| + print '\nFAILED\n'
|
| + print '\n'.join(errors)
|
| return 1
|
| - print '\nSUCCESS\n'
|
| + if not options.bare:
|
| + print '\nSUCCESS\n'
|
| return 0
|
|
|
|
|
| if '__main__' == __name__:
|
| - sys.exit(main(sys.argv))
|
| + sys.exit(main())
|
|
|