PRESUBMIT.py revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1# Copyright (c) 2012 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Presubmit script for changes affecting extensions. 6 7See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts 8for more details about the presubmit API built into gcl. 9""" 10import fnmatch 11import os 12import re 13 14EXTENSIONS_PATH = os.path.join('chrome', 'common', 'extensions') 15DOCS_PATH = os.path.join(EXTENSIONS_PATH, 'docs') 16SERVER2_PATH = os.path.join(DOCS_PATH, 'server2') 17API_PATH = os.path.join(EXTENSIONS_PATH, 'api') 18TEMPLATES_PATH = os.path.join(DOCS_PATH, 'templates') 19PRIVATE_TEMPLATES_PATH = os.path.join(TEMPLATES_PATH, 'private') 20PUBLIC_TEMPLATES_PATH = os.path.join(TEMPLATES_PATH, 'public') 21INTROS_PATH = os.path.join(TEMPLATES_PATH, 'intros') 22ARTICLES_PATH = os.path.join(TEMPLATES_PATH, 'articles') 23 24LOCAL_PUBLIC_TEMPLATES_PATH = os.path.join('docs', 25 'templates', 26 'public') 27 28def _ReadFile(filename): 29 with open(filename) as f: 30 return f.read() 31 32def _ListFilesInPublic(): 33 all_files = [] 34 for path, dirs, files in os.walk(LOCAL_PUBLIC_TEMPLATES_PATH): 35 all_files.extend( 36 os.path.join(path, filename)[len(LOCAL_PUBLIC_TEMPLATES_PATH + os.sep):] 37 for filename in files) 38 return all_files 39 40def _UnixName(name): 41 name = os.path.splitext(name)[0] 42 s1 = re.sub('([a-z])([A-Z])', r'\1_\2', name) 43 s2 = re.sub('([A-Z]+)([A-Z][a-z])', r'\1_\2', s1) 44 return s2.replace('.', '_').lower() 45 46def _FindMatchingTemplates(template_name, template_path_list): 47 matches = [] 48 unix_name = _UnixName(template_name) 49 for template in template_path_list: 50 if unix_name == _UnixName(template.split(os.sep)[-1]): 51 # The docserver expects clean (extensionless) template URLs, so we strip 52 # extensions here when generating the list of matches. 53 matches.append(os.path.splitext(template)[0]) 54 return matches 55 56def _SanitizeAPIName(name, api_path): 57 if not api_path.endswith(os.sep): 58 api_path += os.sep 59 filename = os.path.splitext(name)[0][len(api_path):].replace(os.sep, '_') 60 if 'experimental' in filename: 61 filename = 'experimental_' + filename.replace('experimental_', '') 62 return filename 63 64def _CreateIntegrationTestArgs(affected_files): 65 if (any(fnmatch.fnmatch(name, '%s*.py' % SERVER2_PATH) 66 for name in affected_files) or 67 any(fnmatch.fnmatch(name, '%s*' % PRIVATE_TEMPLATES_PATH) 68 for name in affected_files)): 69 return ['-a'] 70 args = [] 71 for name in affected_files: 72 if (fnmatch.fnmatch(name, '%s*' % PUBLIC_TEMPLATES_PATH) or 73 fnmatch.fnmatch(name, '%s*' % INTROS_PATH) or 74 fnmatch.fnmatch(name, '%s*' % ARTICLES_PATH)): 75 args.extend(_FindMatchingTemplates(name.split(os.sep)[-1], 76 _ListFilesInPublic())) 77 if fnmatch.fnmatch(name, '%s*' % API_PATH): 78 args.extend(_FindMatchingTemplates(_SanitizeAPIName(name, API_PATH), 79 _ListFilesInPublic())) 80 return args 81 82def _CheckHeadingIDs(input_api): 83 ids_re = re.compile('<h[23].*id=.*?>') 84 headings_re = re.compile('<h[23].*?>') 85 bad_files = [] 86 for name in input_api.AbsoluteLocalPaths(): 87 if not os.path.exists(name): 88 continue 89 if (fnmatch.fnmatch(name, '*%s*' % INTROS_PATH) or 90 fnmatch.fnmatch(name, '*%s*' % ARTICLES_PATH)): 91 contents = input_api.ReadFile(name) 92 if (len(re.findall(headings_re, contents)) != 93 len(re.findall(ids_re, contents))): 94 bad_files.append(name) 95 return bad_files 96 97def _CheckLinks(input_api, output_api, results): 98 for affected_file in input_api.AffectedFiles(): 99 name = affected_file.LocalPath() 100 absolute_path = affected_file.AbsoluteLocalPath() 101 if not os.path.exists(absolute_path): 102 continue 103 if (fnmatch.fnmatch(name, '%s*' % PUBLIC_TEMPLATES_PATH) or 104 fnmatch.fnmatch(name, '%s*' % INTROS_PATH) or 105 fnmatch.fnmatch(name, '%s*' % ARTICLES_PATH) or 106 fnmatch.fnmatch(name, '%s*' % API_PATH)): 107 contents = _ReadFile(absolute_path) 108 args = [] 109 if input_api.platform == 'win32': 110 args = [input_api.python_executable] 111 args.extend([os.path.join('docs', 'server2', 'link_converter.py'), 112 '-o', 113 '-f', 114 absolute_path]) 115 output = input_api.subprocess.check_output( 116 args, 117 cwd=input_api.PresubmitLocalPath(), 118 universal_newlines=True) 119 if output != contents: 120 changes = '' 121 for i, (line1, line2) in enumerate( 122 zip(contents.split('\n'), output.split('\n'))): 123 if line1 != line2: 124 changes = ('%s\nLine %d:\n-%s\n+%s\n' % 125 (changes, i + 1, line1, line2)) 126 if changes: 127 results.append(output_api.PresubmitPromptWarning( 128 'File %s may have an old-style <a> link to an API page. Please ' 129 'run docs/server2/link_converter.py to convert the link[s], or ' 130 'convert them manually.\n\nSuggested changes are: %s' % 131 (name, changes))) 132 133def _CheckChange(input_api, output_api): 134 results = [ 135 output_api.PresubmitError('File %s needs an id for each heading.' % name) 136 for name in _CheckHeadingIDs(input_api)] 137 try: 138 integration_test = [] 139 # From depot_tools/presubmit_canned_checks.py:529 140 if input_api.platform == 'win32': 141 integration_test = [input_api.python_executable] 142 integration_test.append( 143 os.path.join('docs', 'server2', 'integration_test.py')) 144 integration_test.extend(_CreateIntegrationTestArgs(input_api.LocalPaths())) 145 input_api.subprocess.check_call(integration_test, 146 cwd=input_api.PresubmitLocalPath()) 147 except input_api.subprocess.CalledProcessError: 148 results.append(output_api.PresubmitError('IntegrationTest failed!')) 149 150 # TODO(kalman): Re-enable this check, or decide to delete it forever. Now 151 # that we have multiple directories it no longer works. 152 # See http://crbug.com/297178. 153 #_CheckLinks(input_api, output_api, results) 154 155 return results 156 157def CheckChangeOnUpload(input_api, output_api): 158 results = [] 159 results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api) 160 results += _CheckChange(input_api, output_api) 161 return results 162 163def CheckChangeOnCommit(input_api, output_api): 164 return _CheckChange(input_api, output_api) 165