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.
5 """Presubmit script for changes affecting extensions.
7 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
8 for more details about the presubmit API built into gcl.
14 EXTENSIONS_PATH
= os
.path
.join('chrome', 'common', 'extensions')
15 DOCS_PATH
= os
.path
.join(EXTENSIONS_PATH
, 'docs')
16 SERVER2_PATH
= os
.path
.join(DOCS_PATH
, 'server2')
17 API_PATH
= os
.path
.join(EXTENSIONS_PATH
, 'api')
18 TEMPLATES_PATH
= os
.path
.join(DOCS_PATH
, 'templates')
19 PRIVATE_TEMPLATES_PATH
= os
.path
.join(TEMPLATES_PATH
, 'private')
20 PUBLIC_TEMPLATES_PATH
= os
.path
.join(TEMPLATES_PATH
, 'public')
21 INTROS_PATH
= os
.path
.join(TEMPLATES_PATH
, 'intros')
22 ARTICLES_PATH
= os
.path
.join(TEMPLATES_PATH
, 'articles')
24 LOCAL_PUBLIC_TEMPLATES_PATH
= os
.path
.join('docs',
28 EXTENSIONS_TO_REMOVE_FOR_CLEAN_URLS
= ('.md', '.html')
30 def _ReadFile(filename
):
31 with
open(filename
) as f
:
34 def _ListFilesInPublic():
36 for path
, dirs
, files
in os
.walk(LOCAL_PUBLIC_TEMPLATES_PATH
):
38 os
.path
.join(path
, filename
)[len(LOCAL_PUBLIC_TEMPLATES_PATH
+ os
.sep
):]
39 for filename
in files
)
43 name
= os
.path
.splitext(name
)[0]
44 s1
= re
.sub('([a-z])([A-Z])', r
'\1_\2', name
)
45 s2
= re
.sub('([A-Z]+)([A-Z][a-z])', r
'\1_\2', s1
)
46 return s2
.replace('.', '_').lower()
48 def _FindMatchingTemplates(template_name
, template_path_list
):
50 unix_name
= _UnixName(template_name
)
51 for template
in template_path_list
:
52 if unix_name
== _UnixName(template
.split(os
.sep
)[-1]):
53 basename
, ext
= os
.path
.splitext(template
)
54 # The docserver expects clean (extensionless) template URLs, so we
55 # strip some extensions here when generating the list of matches.
56 if ext
in EXTENSIONS_TO_REMOVE_FOR_CLEAN_URLS
:
57 matches
.append(basename
)
59 matches
.append(template
)
62 def _SanitizeAPIName(name
, api_path
):
63 if not api_path
.endswith(os
.sep
):
65 filename
= os
.path
.splitext(name
)[0][len(api_path
):].replace(os
.sep
, '_')
66 if 'experimental' in filename
:
67 filename
= 'experimental_' + filename
.replace('experimental_', '')
70 def _CreateIntegrationTestArgs(affected_files
):
71 if (any(fnmatch
.fnmatch(name
, '%s*.py' % SERVER2_PATH
)
72 for name
in affected_files
) or
73 any(fnmatch
.fnmatch(name
, '%s*' % PRIVATE_TEMPLATES_PATH
)
74 for name
in affected_files
)):
77 for name
in affected_files
:
78 if (fnmatch
.fnmatch(name
, '%s*' % PUBLIC_TEMPLATES_PATH
) or
79 fnmatch
.fnmatch(name
, '%s*' % INTROS_PATH
) or
80 fnmatch
.fnmatch(name
, '%s*' % ARTICLES_PATH
)):
81 args
.extend(_FindMatchingTemplates(name
.split(os
.sep
)[-1],
82 _ListFilesInPublic()))
83 if fnmatch
.fnmatch(name
, '%s*' % API_PATH
):
84 args
.extend(_FindMatchingTemplates(_SanitizeAPIName(name
, API_PATH
),
85 _ListFilesInPublic()))
88 def _CheckHeadingIDs(input_api
):
89 ids_re
= re
.compile('<h[23].*id=.*?>')
90 headings_re
= re
.compile('<h[23].*?>')
92 for name
in input_api
.AbsoluteLocalPaths():
93 if not os
.path
.exists(name
):
95 if (fnmatch
.fnmatch(name
, '*%s*' % INTROS_PATH
) or
96 fnmatch
.fnmatch(name
, '*%s*' % ARTICLES_PATH
)):
97 contents
= input_api
.ReadFile(name
)
98 if (len(re
.findall(headings_re
, contents
)) !=
99 len(re
.findall(ids_re
, contents
))):
100 bad_files
.append(name
)
103 def _CheckLinks(input_api
, output_api
, results
):
104 for affected_file
in input_api
.AffectedFiles():
105 name
= affected_file
.LocalPath()
106 absolute_path
= affected_file
.AbsoluteLocalPath()
107 if not os
.path
.exists(absolute_path
):
109 if (fnmatch
.fnmatch(name
, '%s*' % PUBLIC_TEMPLATES_PATH
) or
110 fnmatch
.fnmatch(name
, '%s*' % INTROS_PATH
) or
111 fnmatch
.fnmatch(name
, '%s*' % ARTICLES_PATH
) or
112 fnmatch
.fnmatch(name
, '%s*' % API_PATH
)):
113 contents
= _ReadFile(absolute_path
)
115 if input_api
.platform
== 'win32':
116 args
= [input_api
.python_executable
]
117 args
.extend([os
.path
.join('docs', 'server2', 'link_converter.py'),
121 output
= input_api
.subprocess
.check_output(
123 cwd
=input_api
.PresubmitLocalPath(),
124 universal_newlines
=True)
125 if output
!= contents
:
127 for i
, (line1
, line2
) in enumerate(
128 zip(contents
.split('\n'), output
.split('\n'))):
130 changes
= ('%s\nLine %d:\n-%s\n+%s\n' %
131 (changes
, i
+ 1, line1
, line2
))
133 results
.append(output_api
.PresubmitPromptWarning(
134 'File %s may have an old-style <a> link to an API page. Please '
135 'run docs/server2/link_converter.py to convert the link[s], or '
136 'convert them manually.\n\nSuggested changes are: %s' %
139 def _CheckChange(input_api
, output_api
):
141 output_api
.PresubmitError('File %s needs an id for each heading.' % name
)
142 for name
in _CheckHeadingIDs(input_api
)]
144 integration_test
= []
145 # From depot_tools/presubmit_canned_checks.py:529
146 if input_api
.platform
== 'win32':
147 integration_test
= [input_api
.python_executable
]
148 integration_test
.append(
149 os
.path
.join('docs', 'server2', 'integration_test.py'))
150 integration_test
.extend(_CreateIntegrationTestArgs(input_api
.LocalPaths()))
151 input_api
.subprocess
.check_call(integration_test
,
152 cwd
=input_api
.PresubmitLocalPath())
153 except input_api
.subprocess
.CalledProcessError
:
154 results
.append(output_api
.PresubmitError('IntegrationTest failed!'))
156 # TODO(kalman): Re-enable this check, or decide to delete it forever. Now
157 # that we have multiple directories it no longer works.
158 # See http://crbug.com/297178.
159 #_CheckLinks(input_api, output_api, results)
163 def CheckChangeOnUpload(input_api
, output_api
):
165 results
+= input_api
.canned_checks
.CheckPatchFormatted(input_api
, output_api
)
166 results
+= _CheckChange(input_api
, output_api
)
169 def CheckChangeOnCommit(input_api
, output_api
):
170 return _CheckChange(input_api
, output_api
)