OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 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 """Makes sure files have the right permissions. | 6 """Makes sure files have the right permissions. |
7 | 7 |
8 Some developers have broken SCM configurations that flip the svn:executable | 8 Some developers have broken SCM configurations that flip the svn:executable |
9 permission on for no good reason. Unix developers who run ls --color will then | 9 permission on for no good reason. Unix developers who run ls --color will then |
10 see .cc files in green and get confused. | 10 see .cc files in green and get confused. |
11 | 11 |
12 To ignore a particular file, add it to WHITELIST_FILES. | 12 To ignore a particular file, add it to WHITELIST_FILES. |
13 To ignore a particular extension, add it to WHITELIST_EXTENSIONS. | 13 To ignore a particular extension, add it to WHITELIST_EXTENSIONS. |
14 To ignore whatever regexps your heart desires, add it WHITELIST_REGEX. | 14 To ignore a particular path, add it WHITELIST_PATHS. |
15 | 15 |
16 Note that all directory separators must be slashes (Unix-style) and not | 16 Note that all directory separators must be slashes (Unix-style) and not |
17 backslashes. All directories should be relative to the source root and all | 17 backslashes. All directories should be relative to the source root and all |
18 file paths should be only lowercase. | 18 file paths should be only lowercase. |
19 """ | 19 """ |
20 | 20 |
21 import logging | |
21 import optparse | 22 import optparse |
22 import os | 23 import os |
23 import pipes | |
24 import re | |
25 import stat | 24 import stat |
25 import subprocess | |
26 import sys | 26 import sys |
27 | 27 |
28 #### USER EDITABLE SECTION STARTS HERE #### | 28 #### USER EDITABLE SECTION STARTS HERE #### |
29 | 29 |
30 # Files with these extensions are allowed to have executable permissions. | 30 # Files with these extensions are allowed to have executable permissions. |
31 WHITELIST_EXTENSIONS = [ | 31 WHITELIST_EXTENSIONS = ( |
32 'bash', | 32 'bat', |
33 'bat', | 33 'dll', |
34 'dll', | 34 'dylib', |
35 'dylib', | 35 'in', |
Lei Zhang
2012/04/24 19:34:01
Why is the '.in' extension whitelisted? Also, 'in'
M-A Ruel
2012/04/25 01:30:11
Replace 'in' with the 2 equivalent exceptions.
| |
36 'exe', | 36 'exe', |
37 'pl', | 37 ) |
38 'py', | |
39 'rb', | |
40 'sed', | |
41 'sh', | |
42 ] | |
43 | |
44 # Files that end the following paths are whitelisted too. | |
45 WHITELIST_FILES = [ | |
46 '/build/gyp_chromium', | |
47 '/build/linux/dump_app_syms', | |
48 '/build/linux/pkg-config-wrapper', | |
49 '/build/mac/strip_from_xcode', | |
50 '/build/mac/strip_save_dsym', | |
51 '/chrome/installer/mac/pkg-dmg', | |
52 '/chrome/test/data/webrtc/linux/peerconnection_server', | |
53 '/chrome/tools/build/linux/chrome-wrapper', | |
54 '/chrome/tools/build/mac/build_app_dmg', | |
55 '/chrome/tools/build/mac/clean_up_old_versions', | |
56 '/chrome/tools/build/mac/dump_product_syms', | |
57 '/chrome/tools/build/mac/generate_localizer', | |
58 '/chrome/tools/build/mac/make_sign_sh', | |
59 '/chrome/tools/build/mac/verify_order', | |
60 '/o3d/build/gyp_o3d', | |
61 '/o3d/gypbuild', | |
62 '/o3d/installer/linux/debian.in/rules', | |
63 '/third_party/icu/source/runconfigureicu', | |
64 '/third_party/gold/gold32', | |
65 '/third_party/gold/gold64', | |
66 '/third_party/gold/ld', | |
67 '/third_party/gold/ld.bfd', | |
68 '/third_party/lcov/bin/gendesc', | |
69 '/third_party/lcov/bin/genhtml', | |
70 '/third_party/lcov/bin/geninfo', | |
71 '/third_party/lcov/bin/genpng', | |
72 '/third_party/lcov/bin/lcov', | |
73 '/third_party/lcov/bin/mcov', | |
74 '/third_party/lcov-1.9/bin/gendesc', | |
75 '/third_party/lcov-1.9/bin/genhtml', | |
76 '/third_party/lcov-1.9/bin/geninfo', | |
77 '/third_party/lcov-1.9/bin/genpng', | |
78 '/third_party/lcov-1.9/bin/lcov', | |
79 '/third_party/libxml/linux/xml2-config', | |
80 '/third_party/lzma_sdk/executable/7za.exe', | |
81 '/third_party/swig/linux/swig', | |
82 '/third_party/tcmalloc/chromium/src/pprof', | |
83 '/tools/deep_memory_profiler/dmprof', | |
84 '/tools/git/post-checkout', | |
85 '/tools/git/post-merge', | |
86 '/tools/ld_bfd/ld', | |
87 ] | |
88 | 38 |
89 # File names that are always whitelisted. (These are all autoconf spew.) | 39 # File names that are always whitelisted. (These are all autoconf spew.) |
90 WHITELIST_FILENAMES = set(( | 40 WHITELIST_FILENAMES = set(( |
91 'config.guess', | 41 'config.guess', |
92 'config.sub', | 42 'config.sub', |
93 'configure', | 43 'configure', |
94 'depcomp', | 44 'depcomp', |
95 'install-sh', | 45 'install-sh', |
96 'missing', | 46 'missing', |
97 'mkinstalldirs', | 47 'mkinstalldirs', |
48 'naclsdk', | |
98 'scons', | 49 'scons', |
99 'naclsdk', | |
100 )) | 50 )) |
101 | 51 |
102 # File paths that contain these regexps will be whitelisted as well. | 52 # File paths that contain these regexps will be whitelisted as well. |
103 WHITELIST_REGEX = [ | 53 WHITELIST_PATHS = ( |
104 re.compile('/third_party/openssl/'), | 54 # TODO(maruel): Detect ELF files. |
105 re.compile('/third_party/sqlite/'), | 55 'chrome/test/data/webrtc/linux/peerconnection_server', |
106 re.compile('/third_party/xdg-utils/'), | 56 'native_client_sdk/src/build_tools/sdk_tools/third_party/', |
107 re.compile('/third_party/yasm/source/patched-yasm/config'), | 57 'out/', |
108 re.compile('/third_party/ffmpeg/tools'), | 58 # TODO(maruel): Fix these. |
109 ] | 59 'third_party/android_testrunner/', |
60 'third_party/closure_linter/', | |
61 'third_party/devscripts/licensecheck.pl.vanilla', | |
62 'third_party/hyphen/', | |
63 'third_party/jemalloc/', | |
64 'third_party/lcov-1.9/contrib/galaxy/conglomerate_functions.pl', | |
65 'third_party/lcov-1.9/contrib/galaxy/gen_makefile.sh', | |
66 'third_party/lcov/contrib/galaxy/conglomerate_functions.pl', | |
67 'third_party/lcov/contrib/galaxy/gen_makefile.sh', | |
68 'third_party/libevent/autogen.sh', | |
69 'third_party/libevent/test/test.sh', | |
70 'third_party/libxml/linux/xml2-config', | |
71 'third_party/libxml/src/ltmain.sh', | |
72 'third_party/mesa/', | |
73 'third_party/protobuf/', | |
74 'third_party/python_gflags/gflags.py', | |
75 'third_party/sqlite/', | |
76 'third_party/talloc/script/mksyms.sh', | |
77 'third_party/tcmalloc/', | |
78 'third_party/tlslite/setup.py', | |
79 ) | |
110 | 80 |
111 #### USER EDITABLE SECTION ENDS HERE #### | 81 #### USER EDITABLE SECTION ENDS HERE #### |
112 | 82 |
113 WHITELIST_EXTENSIONS_REGEX = re.compile(r'\.(%s)' % | 83 |
114 '|'.join(WHITELIST_EXTENSIONS)) | 84 def capture(cmd, cwd): |
115 | 85 """Returns the output of a command. |
116 WHITELIST_FILES_REGEX = re.compile(r'(%s)$' % '|'.join(WHITELIST_FILES)) | 86 |
117 | 87 Ignores the error code or stderr. |
118 # Set to true for more output. This is set by the command line options. | |
119 VERBOSE = False | |
120 | |
121 # Using forward slashes as directory separators, ending in a forward slash. | |
122 # Set by the command line options. | |
123 BASE_DIRECTORY = '' | |
124 | |
125 # The default if BASE_DIRECTORY is not set on the command line. | |
126 DEFAULT_BASE_DIRECTORY = '../../..' | |
127 | |
128 # The directories which contain the sources managed by git. | |
129 GIT_SOURCE_DIRECTORY = set() | |
130 | |
131 # The SVN repository url. | |
132 SVN_REPO_URL = '' | |
133 | |
134 # Whether we are using SVN or GIT. | |
135 IS_SVN = True | |
136 | |
137 # Executable permission mask | |
138 EXECUTABLE_PERMISSION = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH | |
139 | |
140 | |
141 def IsWhiteListed(file_path): | |
142 """Returns True if file_path is in our whitelist of files to ignore.""" | |
143 if WHITELIST_EXTENSIONS_REGEX.match(os.path.splitext(file_path)[1]): | |
144 return True | |
145 if WHITELIST_FILES_REGEX.search(file_path): | |
146 return True | |
147 if os.path.basename(file_path) in WHITELIST_FILENAMES: | |
148 return True | |
149 for regex in WHITELIST_REGEX: | |
150 if regex.search(file_path): | |
151 return True | |
152 return False | |
153 | |
154 | |
155 def CheckFile(file_path): | |
156 """Checks file_path's permissions. | |
157 | |
158 Args: | |
159 file_path: The file path to check. | |
160 | |
161 Returns: | |
162 Either a string describing the error if there was one, or None if the file | |
163 checked out OK. | |
164 """ | 88 """ |
165 if VERBOSE: | 89 env = os.environ.copy() |
166 print 'Checking file: ' + file_path | 90 env['LANG'] = 'en_us.UTF-8' |
167 | 91 p = subprocess.Popen( |
168 file_path_lower = file_path.lower() | 92 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd, env=env) |
169 if IsWhiteListed(file_path_lower): | 93 return p.communicate()[0] |
170 return None | 94 |
171 | 95 |
172 # Not whitelisted, stat the file and check permissions. | 96 class ApiBase(object): |
173 try: | 97 def __init__(self, root_dir, bare_output): |
174 st_mode = os.stat(file_path).st_mode | 98 self.root_dir = root_dir |
175 except IOError, e: | 99 self.bare_output = bare_output |
176 return 'Failed to stat file: %s' % e | 100 |
177 except OSError, e: | 101 @staticmethod |
178 return 'Failed to stat file: %s' % e | 102 def is_whitelisted(rel_path): |
179 | 103 """Returns True if rel_path is in our whitelist of files to ignore.""" |
180 if EXECUTABLE_PERMISSION & st_mode: | 104 rel_path = rel_path.lower() |
181 # Look if the file starts with #!/ | 105 return ( |
182 with open(file_path, 'rb') as f: | 106 os.path.splitext(rel_path)[1][1:] in WHITELIST_EXTENSIONS or |
183 if f.read(3) == '#!/': | 107 os.path.basename(rel_path) in WHITELIST_FILENAMES or |
184 # That's fine. | 108 rel_path.startswith(WHITELIST_PATHS)) |
109 | |
110 @staticmethod | |
111 def has_executable_bit(full_path): | |
112 """Returns if any executable bit is set.""" | |
113 permission = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH | |
114 return bool(permission & os.stat(full_path).st_mode) | |
Lei Zhang
2012/04/24 19:34:01
Are you expecting os.stat() to not throw any excep
M-A Ruel
2012/04/25 01:30:11
Why would a file in the checkout not be readable b
Lei Zhang
2012/04/25 04:01:02
Ideally that would be the case, but the fact that
| |
115 | |
116 @staticmethod | |
117 def has_shebang(full_path): | |
118 """Returns if the file starts with #!/. | |
119 | |
120 file_path is the absolute path to the file. | |
121 """ | |
122 with open(full_path, 'rb') as f: | |
123 return f.read(3) == '#!/' | |
124 | |
125 def check_file(self, rel_path): | |
126 """Checks file_path's permissions and returns an error if it is | |
127 inconsistent. | |
128 """ | |
129 logging.debug('check_file(%s)' % rel_path) | |
130 | |
131 if self.is_whitelisted(rel_path): | |
132 return None | |
133 | |
134 full_path = os.path.join(self.root_dir, rel_path) | |
135 bit = self.has_executable_bit(full_path) | |
136 shebang = self.has_shebang(full_path) | |
Lei Zhang
2012/04/24 19:34:01
I think this is going to slow down checkperms.py s
M-A Ruel
2012/04/25 01:30:11
All the following is on a standard 500gb HD.
linu
Lei Zhang
2012/04/25 04:01:02
How about we only check files with the executable
| |
137 if bit != shebang: | |
138 if self.bare_output: | |
139 return rel_path | |
140 if bit: | |
141 return '%s: Has executable bit but not shebang' % rel_path | |
142 else: | |
143 return '%s: Has shebang but not executable bit' % rel_path | |
144 | |
145 def check(self, start_dir): | |
146 """Check the files in start_dir, recursively check its subdirectories.""" | |
147 errors = [] | |
148 items = self.list_dir(start_dir) | |
149 logging.info('check(%s) -> %d' % (start_dir, len(items))) | |
150 for item in items: | |
151 full_path = os.path.join(self.root_dir, start_dir, item) | |
152 rel_path = full_path[len(self.root_dir) + 1:] | |
153 if os.path.isdir(full_path): | |
154 if self.is_whitelisted(rel_path): | |
155 continue | |
156 # Depth first. | |
157 errors.extend(self.check(rel_path)) | |
158 else: | |
159 error = self.check_file(rel_path) | |
160 if error: | |
161 errors.append(error) | |
162 return errors | |
163 | |
164 def list_dir(self, start_dir): | |
165 """Lists all the files and directory inside start_dir.""" | |
166 return sorted( | |
167 x for x in os.listdir(os.path.join(self.root_dir, start_dir)) | |
168 if not x.startswith('.') | |
169 ) | |
170 | |
171 | |
172 class ApiSvn(ApiBase): | |
173 @staticmethod | |
174 def get_info(dir_path): | |
175 """Returns svn meta-data for a svn checkout.""" | |
176 if not os.path.isdir(dir_path): | |
177 return {} | |
178 out = capture(['svn', 'info', '.'], dir_path) | |
179 return dict(l.split(': ', 1) for l in out.splitlines() if l) | |
180 | |
181 @classmethod | |
182 def get_root(cls, dir_path): | |
183 """Returns the svn checkout root or None.""" | |
184 svn_url = cls.get_info(dir_path).get('Repository Root:') | |
185 if not svn_url: | |
186 return None | |
187 while True: | |
188 parent = os.path.dirname(dir_path) | |
189 if parent == dir_path: | |
185 return None | 190 return None |
186 # TODO(maruel): Check that non-executable file do not start with a shebang. | 191 if svn_url != cls.get_info(parent).get('Repository Root:'): |
187 error = 'Contains executable permission' | 192 return dir_path |
188 if VERBOSE: | 193 dir_path = parent |
189 return '%s: %06o' % (error, st_mode) | 194 |
190 return error | 195 def list_dir(self, start_dir): |
191 return None | 196 """Lists all the files and directory inside start_dir.""" |
192 | 197 return sorted( |
193 | 198 capture( |
194 def ShouldCheckDirectory(dir_path): | 199 ['svn', 'ls'], os.path.join(self.root_dir, start_dir)).splitlines() |
195 """Determine if we should check the content of dir_path.""" | 200 ) |
196 if not IS_SVN: | 201 |
197 return dir_path in GIT_SOURCE_DIRECTORY | 202 |
198 repo_url = GetSvnRepositoryRoot(dir_path) | 203 class ApiGit(ApiBase): |
199 if not repo_url: | 204 _files = None |
200 return False | 205 |
201 return repo_url == SVN_REPO_URL | 206 @staticmethod |
202 | 207 def get_root(dir_path): |
203 | 208 """Returns the git checkout root or None.""" |
204 def CheckDirectory(dir_path): | 209 root = capture(['git', 'rev-parse', '--show-toplevel'], dir_path).strip() |
205 """Check the files in dir_path; recursively check its subdirectories.""" | 210 if root: |
206 # Collect a list of all files and directories to check. | 211 return root |
207 files_to_check = [] | 212 |
208 dirs_to_check = [] | 213 def list_dir(self, start_dir): |
209 success = True | 214 """Lists all the files and directory inside start_dir.""" |
210 contents = os.listdir(dir_path) | 215 if self._files is None: |
211 for cur in contents: | 216 # git has no way to get the list of files in a specific directory without |
212 full_path = os.path.join(dir_path, cur) | 217 # recursing. |
213 if os.path.isdir(full_path) and ShouldCheckDirectory(full_path): | 218 self._files = capture(['git', 'ls-files'], self.root_dir).splitlines() |
214 dirs_to_check.append(full_path) | 219 start_dir = start_dir[len(self.root_dir) + 1:] |
215 elif os.path.isfile(full_path): | 220 return sorted( |
216 files_to_check.append(full_path) | 221 x[len(start_dir):] for x in self._files if x.startswith(start_dir) |
217 | 222 ) |
218 # First check all files in this directory. | 223 |
219 for cur_file in files_to_check: | 224 |
220 file_status = CheckFile(cur_file) | 225 def get_scm(dir_path, log, bare): |
221 if file_status is not None: | 226 """Returns a properly configured ApiBase instance.""" |
222 print 'ERROR in %s\n%s' % (cur_file, file_status) | 227 dir_path = os.path.abspath(dir_path) |
223 success = False | 228 root = ApiSvn.get_root(dir_path) |
224 | 229 if root: |
225 # Next recurse into the subdirectories. | 230 if log: |
226 for cur_dir in dirs_to_check: | 231 logging.info('Found svn at %s' % root) |
227 if not CheckDirectory(cur_dir): | 232 return ApiSvn(root, bare) |
228 success = False | 233 root = ApiGit.get_root(dir_path) |
229 return success | 234 if root: |
230 | 235 if log: |
231 | 236 logging.info('Found git at %s' % root) |
232 def GetGitSourceDirectory(root): | 237 return ApiGit(root, bare) |
233 """Returns a set of the directories to be checked. | 238 |
234 | 239 # Returns a non-scm aware checker. |
235 Args: | 240 if log: |
236 root: The repository root where a .git directory must exist. | 241 logging.warn('Failed to determine the SCM for %s' % dir_path) |
237 | 242 return ApiBase(dir_path, bare) |
238 Returns: | 243 |
239 A set of directories which contain sources managed by git. | 244 |
240 """ | 245 def main(): |
241 git_source_directory = set() | |
242 popen_out = os.popen('cd %s && git ls-files --full-name .' % | |
243 pipes.quote(root)) | |
244 for line in popen_out: | |
245 dir_path = os.path.join(root, os.path.dirname(line)) | |
246 git_source_directory.add(dir_path) | |
247 git_source_directory.add(root) | |
248 return git_source_directory | |
249 | |
250 | |
251 def GetSvnRepositoryRoot(dir_path): | |
252 """Returns the repository root for a directory. | |
253 | |
254 Args: | |
255 dir_path: A directory where a .svn subdirectory should exist. | |
256 | |
257 Returns: | |
258 The svn repository that contains dir_path or None. | |
259 """ | |
260 svn_dir = os.path.join(dir_path, '.svn') | |
261 if not os.path.isdir(svn_dir): | |
262 return None | |
263 popen_out = os.popen('cd %s && svn info' % pipes.quote(dir_path)) | |
264 for line in popen_out: | |
265 if line.startswith('Repository Root: '): | |
266 return line[len('Repository Root: '):].rstrip() | |
267 return None | |
268 | |
269 | |
270 def main(argv): | |
271 usage = """Usage: python %prog [--root <root>] [tocheck] | 246 usage = """Usage: python %prog [--root <root>] [tocheck] |
272 tocheck Specifies the directory, relative to root, to check. This defaults | 247 tocheck Specifies the directory, relative to root, to check. This defaults |
273 to "." so it checks everything. | 248 to "." so it checks everything. |
274 | 249 |
275 Examples: | 250 Examples: |
276 python checkperms.py | 251 python %prog |
277 python checkperms.py --root /path/to/source chrome""" | 252 python %prog --root /path/to/source chrome""" |
278 | 253 |
279 option_parser = optparse.OptionParser(usage=usage) | 254 parser = optparse.OptionParser(usage=usage) |
280 option_parser.add_option('--root', dest='base_directory', | 255 parser.add_option( |
281 default=DEFAULT_BASE_DIRECTORY, | 256 '--root', |
282 help='Specifies the repository root. This defaults ' | 257 default=get_scm('.', False, False).root_dir, |
283 'to %default relative to the script file, which ' | 258 help='Specifies the repository root. This defaults ' |
284 'will normally be the repository root.') | 259 'to "%default" relative to the script file, which ' |
285 option_parser.add_option('-v', '--verbose', action='store_true', | 260 'will normally be the repository root.') |
286 help='Print debug logging') | 261 parser.add_option( |
287 options, args = option_parser.parse_args() | 262 '-v', '--verbose', action='count', default=0, help='Print debug logging') |
288 | 263 parser.add_option( |
289 global VERBOSE | 264 '--bare', |
290 if options.verbose: | 265 action='store_true', |
291 VERBOSE = True | 266 default=False, |
292 | 267 help='Prints the bare filename triggering the checks') |
293 # Optional base directory of the repository. | 268 options, args = parser.parse_args() |
294 global BASE_DIRECTORY | 269 |
295 if (not options.base_directory or | 270 levels = [logging.ERROR, logging.INFO, logging.DEBUG] |
296 options.base_directory == DEFAULT_BASE_DIRECTORY): | 271 logging.basicConfig(level=levels[min(len(levels) - 1, options.verbose)]) |
297 BASE_DIRECTORY = os.path.abspath( | 272 |
298 os.path.join(os.path.abspath(argv[0]), DEFAULT_BASE_DIRECTORY)) | 273 if len(args) > 1: |
274 parser.error('Too many arguments used') | |
275 | |
276 if not options.root: | |
277 parser.error('Must specify --root') | |
278 | |
279 # Guess again the SCM used. | |
280 api = get_scm(options.root, True, options.bare) | |
281 if args: | |
282 start_dir = args[0] | |
299 else: | 283 else: |
300 BASE_DIRECTORY = os.path.abspath(argv[2]) | 284 start_dir = api.root_dir |
301 | 285 |
302 # Figure out which directory we have to check. | 286 errors = api.check(start_dir) |
303 if not args: | 287 if errors: |
304 # No directory to check specified, use the repository root. | 288 if not options.bare: |
305 start_dir = BASE_DIRECTORY | 289 print '\nFAILED\n' |
306 elif len(args) == 1: | 290 print '\n'.join(errors) |
307 # Directory specified. Start here. It's supposed to be relative to the | |
308 # base directory. | |
309 start_dir = os.path.abspath(os.path.join(BASE_DIRECTORY, args[0])) | |
310 else: | |
311 # More than one argument, we don't handle this. | |
312 option_parser.print_help() | |
313 return 1 | 291 return 1 |
314 | 292 if not options.bare: |
315 print 'Using base directory:', BASE_DIRECTORY | 293 print '\nSUCCESS\n' |
316 print 'Checking directory:', start_dir | |
317 | |
318 BASE_DIRECTORY = BASE_DIRECTORY.replace('\\', '/') | |
319 start_dir = start_dir.replace('\\', '/') | |
320 | |
321 success = True | |
322 if os.path.exists(os.path.join(BASE_DIRECTORY, '.svn')): | |
323 global SVN_REPO_URL | |
324 SVN_REPO_URL = GetSvnRepositoryRoot(BASE_DIRECTORY) | |
325 if not SVN_REPO_URL: | |
326 print 'Cannot determine the SVN repo URL' | |
327 success = False | |
328 elif os.path.exists(os.path.join(BASE_DIRECTORY, '.git')): | |
329 global IS_SVN | |
330 IS_SVN = False | |
331 global GIT_SOURCE_DIRECTORY | |
332 GIT_SOURCE_DIRECTORY = GetGitSourceDirectory(BASE_DIRECTORY) | |
333 if not GIT_SOURCE_DIRECTORY: | |
334 print 'Cannot determine the list of GIT directories' | |
335 success = False | |
336 else: | |
337 print 'Cannot determine the SCM used in %s' % BASE_DIRECTORY | |
338 success = False | |
339 | |
340 if success: | |
341 success = CheckDirectory(start_dir) | |
342 if not success: | |
343 print '\nFAILED\n' | |
344 return 1 | |
345 print '\nSUCCESS\n' | |
346 return 0 | 294 return 0 |
347 | 295 |
348 | 296 |
349 if '__main__' == __name__: | 297 if '__main__' == __name__: |
350 sys.exit(main(sys.argv)) | 298 sys.exit(main()) |
OLD | NEW |