blob: 95f07ca9b1d638dfc20cf47edebbe17f94ffaab8 [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:37 +00001# Copyright (C) 2014 Google Inc. All rights reserved.
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions are
5# met:
6#
7# * Redistributions of source code must retain the above copyright
8# notice, this list of conditions and the following disclaimer.
9# * Redistributions in binary form must reproduce the above
10# copyright notice, this list of conditions and the following disclaimer
11# in the documentation and/or other materials provided with the
12# distribution.
13# * Neither the name of Google Inc. nor the names of its
14# contributors may be used to endorse or promote products derived from
15# this software without specific prior written permission.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Yang Guo75beda92019-10-28 08:29:25 +010028"""
29DevTools presubmit script
Blink Reformat4c46d092018-04-07 15:32:37 +000030
31See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
32for more details about the presubmit API built into gcl.
33"""
34
35import sys
36
Yang Guoa7845d52019-10-31 11:30:23 +010037EXCLUSIVE_CHANGE_DIRECTORIES = [
38 [ 'third_party', 'v8' ],
39 [ 'node_modules' ],
40 [ 'OWNERS' ],
41]
42
Liviu Raufd2e3212019-12-18 16:38:20 +010043AUTOROLL_ACCOUNT = "devtools-ci-autoroll-builder@chops-service-accounts.iam.gserviceaccount.com"
Mathias Bynensa0a6e292019-12-17 13:24:08 +010044
Yang Guoa7845d52019-10-31 11:30:23 +010045def _CheckChangesAreExclusiveToDirectory(input_api, output_api):
Tim van der Lippebc42a632019-11-28 14:22:55 +000046 if input_api.change.DISABLE_THIRD_PARTY_CHECK != None:
47 return []
48
Yang Guoa7845d52019-10-31 11:30:23 +010049 def IsParentDir(file, dir):
50 while file != '':
51 if file == dir:
52 return True
53 file = input_api.os_path.dirname(file)
Yang Guoa7845d52019-10-31 11:30:23 +010054 return False
55
56 def FileIsInDir(file, dirs):
57 for dir in dirs:
58 if IsParentDir(file, dir):
59 return True
60
61 affected_files = input_api.LocalPaths()
Yang Guoa7845d52019-10-31 11:30:23 +010062 num_affected = len(affected_files)
63 for dirs in EXCLUSIVE_CHANGE_DIRECTORIES:
Paul Lewis14effba2019-12-02 14:56:40 +000064 dir_list = ', '.join(dirs)
Yang Guoa7845d52019-10-31 11:30:23 +010065 affected_in_dir = filter(lambda f: FileIsInDir(f, dirs), affected_files)
66 num_in_dir = len(affected_in_dir)
67 if num_in_dir == 0:
68 continue
Tim van der Lippeebb94a92019-11-19 17:07:53 +000069 # Addition of new third_party folders must have a new entry in `.gitignore`
70 if '.gitignore' in affected_files:
71 num_in_dir = num_in_dir + 1
Yang Guoa7845d52019-10-31 11:30:23 +010072 if num_in_dir < num_affected:
73 return [
Tim van der Lippebc42a632019-11-28 14:22:55 +000074 output_api
Paul Lewis14effba2019-12-02 14:56:40 +000075 .PresubmitError(('CLs that affect files in "%s" should be limited to these files/directories.' % dir_list) +
76 ' You can disable this check by adding DISABLE_THIRD_PARTY_CHECK=<reason> to your commit message')
Yang Guoa7845d52019-10-31 11:30:23 +010077 ]
78 return []
79
Blink Reformat4c46d092018-04-07 15:32:37 +000080
81def _CheckBuildGN(input_api, output_api):
Yang Guo75beda92019-10-28 08:29:25 +010082 script_path = input_api.os_path.join(input_api.PresubmitLocalPath(), 'scripts', 'check_gn.js')
Blink Reformat4c46d092018-04-07 15:32:37 +000083 return _checkWithNodeScript(input_api, output_api, script_path)
84
85
Tim van der Lippee4bdd742019-12-17 16:40:16 +010086def _CheckJSON(input_api, output_api):
87 script_path = input_api.os_path.join(input_api.PresubmitLocalPath(), 'scripts', 'json_validator', 'validate_module_json.js')
88 return _checkWithNodeScript(input_api, output_api, script_path)
89
90
Brandon Goddard53faba12020-01-24 16:55:04 +000091def _CheckLicenses(input_api, output_api):
92 script_path = input_api.os_path.join(input_api.PresubmitLocalPath(), 'scripts', 'test', 'run_license_header_check.js')
93 return _checkWithNodeScript(input_api, output_api, script_path)
94
95
Paul Lewis21fb3492020-01-14 15:28:22 +000096def _CheckUnitTests(input_api, output_api):
97 unittest_root = input_api.os_path.join(input_api.PresubmitLocalPath(), 'test')
98 affected_unittest_files = _getAffectedFiles(input_api, [unittest_root], ['D'], ['.ts'])
99 if len(affected_unittest_files) == 0:
100 return []
101
102 script_path = input_api.os_path.join(input_api.PresubmitLocalPath(), 'scripts', 'test', 'check_for_unittest_onlys.js')
103 return _checkWithNodeScript(input_api, output_api, script_path, affected_unittest_files)
104
105
Blink Reformat4c46d092018-04-07 15:32:37 +0000106def _CheckFormat(input_api, output_api):
107
108 def popen(args):
109 return input_api.subprocess.Popen(args=args, stdout=input_api.subprocess.PIPE, stderr=input_api.subprocess.STDOUT)
110
111 affected_files = _getAffectedJSFiles(input_api)
112 if len(affected_files) == 0:
113 return []
114 original_sys_path = sys.path
115 try:
Yang Guo75beda92019-10-28 08:29:25 +0100116 sys.path = sys.path + [input_api.os_path.join(input_api.PresubmitLocalPath(), 'scripts')]
Yang Guod8176982019-10-04 20:30:35 +0000117 import devtools_paths
Blink Reformat4c46d092018-04-07 15:32:37 +0000118 finally:
119 sys.path = original_sys_path
120
121 ignore_files = []
122 eslint_ignore_path = input_api.os_path.join(input_api.PresubmitLocalPath(), '.eslintignore')
123 with open(eslint_ignore_path, 'r') as ignore_manifest:
124 for line in ignore_manifest:
Ingvar Stepanyanb532f3f2019-10-22 12:32:59 +0100125 ignore_files.append(input_api.os_path.normpath(line.strip()))
Mathias Bynens032591d2019-10-21 11:51:31 +0200126 formattable_files = [
127 affected_file for affected_file in affected_files if all(ignore_file not in affected_file for ignore_file in ignore_files)
128 ]
Blink Reformat4c46d092018-04-07 15:32:37 +0000129 if len(formattable_files) == 0:
130 return []
131
132 check_formatting_process = popen(['git', 'cl', 'format', '--js', '--dry-run'] + formattable_files)
133 check_formatting_process.communicate()
134 if check_formatting_process.returncode == 0:
135 return []
136
137 format_args = ['git', 'cl', 'format', '--js'] + formattable_files
138 format_process = popen(format_args)
139 format_out, _ = format_process.communicate()
140 if format_process.returncode != 0:
141 return [output_api.PresubmitError(format_out)]
142
Blink Reformat4c46d092018-04-07 15:32:37 +0000143 return [
Yang Guo5a805ab2019-10-31 13:53:28 +0100144 output_api.PresubmitError('ERROR: Found formatting violations.\n'
Yang Guo75beda92019-10-28 08:29:25 +0100145 'Ran clang-format on diff\n'
146 'Use git status to check the formatting changes'),
Blink Reformat4c46d092018-04-07 15:32:37 +0000147 output_api.PresubmitError(format_out),
148 ]
149
150
e52a82bdfb5106bd658c2c5ea465e200594be1e2019-10-29 16:02:46 -0700151def _CheckDevtoolsLocalization(input_api, output_api, check_all_files=False): # pylint: disable=invalid-name
Mandy Chene997da72019-08-22 23:50:19 +0000152 devtools_root = input_api.PresubmitLocalPath()
vidorteg2b675b02019-11-25 09:51:28 -0800153 script_path = input_api.os_path.join(devtools_root, 'scripts', 'test', 'run_localization_check.py')
Paul Lewis954a5a92019-11-20 15:33:49 +0000154 if check_all_files == True:
vidorteg2b675b02019-11-25 09:51:28 -0800155 # Scan all files and fix any errors
vidorteg75c025e2019-11-25 09:52:43 -0800156 args = ['--autofix', '--all']
Paul Lewis954a5a92019-11-20 15:33:49 +0000157 else:
vidorteg2b675b02019-11-25 09:51:28 -0800158 devtools_front_end = input_api.os_path.join(devtools_root, 'front_end')
159 affected_front_end_files = _getAffectedFiles(input_api, [devtools_front_end], ['D'],
160 ['.js', '.grdp', '.grd', 'module.json'])
161
162 if len(affected_front_end_files) == 0:
163 return []
164 # Scan only added or modified files with specific extensions.
165 args = [
166 '--autofix',
167 '--files',
168 ] + affected_front_end_files
169 process = input_api.subprocess.Popen(
170 [input_api.python_executable, script_path] + args, stdout=input_api.subprocess.PIPE, stderr=input_api.subprocess.STDOUT)
171 out, _ = process.communicate()
172 if process.returncode != 0:
173 return [output_api.PresubmitError(out)]
174 return [output_api.PresubmitNotifyResult(out)]
Mandy Chen465b4f72019-03-21 22:52:54 +0000175
176
Blink Reformat4c46d092018-04-07 15:32:37 +0000177def _CheckDevtoolsStyle(input_api, output_api):
Yang Guo75beda92019-10-28 08:29:25 +0100178 lint_path = input_api.os_path.join(input_api.PresubmitLocalPath(), 'scripts', 'test', 'run_lint_check.py')
Tim van der Lippec85c3122019-09-19 11:27:04 +0000179 process = input_api.subprocess.Popen([input_api.python_executable, lint_path],
180 stdout=input_api.subprocess.PIPE,
181 stderr=input_api.subprocess.STDOUT)
182 out, _ = process.communicate()
183 if process.returncode != 0:
184 return [output_api.PresubmitError(out)]
185 return [output_api.PresubmitNotifyResult(out)]
Blink Reformat4c46d092018-04-07 15:32:37 +0000186
187
Joel Einbinderf6f86b62019-06-10 23:19:12 +0000188def _CheckOptimizeSVGHashes(input_api, output_api):
Blink Reformat4c46d092018-04-07 15:32:37 +0000189 if not input_api.platform.startswith('linux'):
190 return []
191
192 original_sys_path = sys.path
193 try:
194 sys.path = sys.path + [input_api.os_path.join(input_api.PresubmitLocalPath(), 'scripts', 'build')]
195 import devtools_file_hashes
196 finally:
197 sys.path = original_sys_path
198
199 absolute_local_paths = [af.AbsoluteLocalPath() for af in input_api.AffectedFiles(include_deletes=False)]
Yang Guo75beda92019-10-28 08:29:25 +0100200 images_src_path = input_api.os_path.join('devtools', 'front_end', 'Images', 'src')
201 image_source_file_paths = [path for path in absolute_local_paths if images_src_path in path and path.endswith('.svg')]
202 image_sources_path = input_api.os_path.join(input_api.PresubmitLocalPath(), 'front_end', 'Images', 'src')
203 hashes_file_name = 'optimize_svg.hashes'
Blink Reformat4c46d092018-04-07 15:32:37 +0000204 hashes_file_path = input_api.os_path.join(image_sources_path, hashes_file_name)
205 invalid_hash_file_paths = devtools_file_hashes.files_with_invalid_hashes(hashes_file_path, image_source_file_paths)
206 if len(invalid_hash_file_paths) == 0:
207 return []
208 invalid_hash_file_names = [input_api.os_path.basename(file_path) for file_path in invalid_hash_file_paths]
Yang Guo75beda92019-10-28 08:29:25 +0100209 file_paths_str = ', '.join(invalid_hash_file_names)
210 error_message = 'The following SVG files should be optimized using optimize_svg_images script before uploading: \n - %s' % file_paths_str
Blink Reformat4c46d092018-04-07 15:32:37 +0000211 return [output_api.PresubmitError(error_message)]
212
213
214def _CheckCSSViolations(input_api, output_api):
215 results = []
216 for f in input_api.AffectedFiles(include_deletes=False):
Yang Guo75beda92019-10-28 08:29:25 +0100217 if not f.LocalPath().endswith('.css'):
Blink Reformat4c46d092018-04-07 15:32:37 +0000218 continue
219 for line_number, line in f.ChangedContents():
Yang Guo75beda92019-10-28 08:29:25 +0100220 if '/deep/' in line:
221 results.append(output_api.PresubmitError(('%s:%d uses /deep/ selector') % (f.LocalPath(), line_number)))
222 if '::shadow' in line:
223 results.append(output_api.PresubmitError(('%s:%d uses ::shadow selector') % (f.LocalPath(), line_number)))
Blink Reformat4c46d092018-04-07 15:32:37 +0000224 return results
225
Mathias Bynens032591d2019-10-21 11:51:31 +0200226
Tim van der Lippe5279f842020-01-14 16:26:38 +0000227def _CheckNoUncheckedFiles(input_api, output_api):
228 results = []
229 process = input_api.subprocess.Popen(['git', 'diff', '--exit-code'],
230 stdout=input_api.subprocess.PIPE,
231 stderr=input_api.subprocess.STDOUT)
232 out, _ = process.communicate()
233 if process.returncode != 0:
234 return [output_api.PresubmitError('You have changed files that need to be committed.')]
235 return []
236
Tim van der Lippe8fdda112020-01-27 11:27:06 +0000237def _CheckForTooLargeFiles(input_api, output_api):
238 """Avoid large files, especially binary files, in the repository since
239 git doesn't scale well for those. They will be in everyone's repo
240 clones forever, forever making Chromium slower to clone and work
241 with."""
242 # Uploading files to cloud storage is not trivial so we don't want
243 # to set the limit too low, but the upper limit for "normal" large
244 # files seems to be 1-2 MB, with a handful around 5-8 MB, so
245 # anything over 20 MB is exceptional.
246 TOO_LARGE_FILE_SIZE_LIMIT = 20 * 1024 * 1024 # 10 MB
247 too_large_files = []
248 for f in input_api.AffectedFiles():
249 # Check both added and modified files (but not deleted files).
250 if f.Action() in ('A', 'M'):
251 size = input_api.os_path.getsize(f.AbsoluteLocalPath())
252 if size > TOO_LARGE_FILE_SIZE_LIMIT:
253 too_large_files.append("%s: %d bytes" % (f.LocalPath(), size))
254 if too_large_files:
255 message = (
256 'Do not commit large files to git since git scales badly for those.\n' +
257 'Instead put the large files in cloud storage and use DEPS to\n' +
258 'fetch them.\n' + '\n'.join(too_large_files)
259 )
260 return [output_api.PresubmitError(
261 'Too large files found in commit', long_text=message + '\n')]
262 else:
263 return []
264
Tim van der Lippe5279f842020-01-14 16:26:38 +0000265
Yang Guo4fd355c2019-09-19 10:59:03 +0200266def _CommonChecks(input_api, output_api):
Mathias Bynens032591d2019-10-21 11:51:31 +0200267 """Checks common to both upload and commit."""
268 results = []
Liviu Raufd2e3212019-12-18 16:38:20 +0100269 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(input_api, output_api,
270 bot_whitelist=[AUTOROLL_ACCOUNT]
271 ))
Mathias Bynens032591d2019-10-21 11:51:31 +0200272 results.extend(input_api.canned_checks.CheckOwnersFormat(input_api, output_api))
273 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
274 results.extend(input_api.canned_checks.CheckChangeHasNoCrAndHasOnlyOneEol(input_api, output_api))
275 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(input_api, output_api))
276 results.extend(input_api.canned_checks.CheckGenderNeutral(input_api, output_api))
Blink Reformat4c46d092018-04-07 15:32:37 +0000277 results.extend(_CheckBuildGN(input_api, output_api))
Tim van der Lippee4bdd742019-12-17 16:40:16 +0100278 results.extend(_CheckJSON(input_api, output_api))
Brandon Goddard53faba12020-01-24 16:55:04 +0000279 results.extend(_CheckLicenses(input_api, output_api))
Blink Reformat4c46d092018-04-07 15:32:37 +0000280 results.extend(_CheckDevtoolsStyle(input_api, output_api))
Tim van der Lippe5497d482020-01-14 15:27:30 +0000281 results.extend(_CheckFormat(input_api, output_api))
Joel Einbinderf6f86b62019-06-10 23:19:12 +0000282 results.extend(_CheckOptimizeSVGHashes(input_api, output_api))
Blink Reformat4c46d092018-04-07 15:32:37 +0000283 results.extend(_CheckCSSViolations(input_api, output_api))
Yang Guoa7845d52019-10-31 11:30:23 +0100284 results.extend(_CheckChangesAreExclusiveToDirectory(input_api, output_api))
Paul Lewis21fb3492020-01-14 15:28:22 +0000285 results.extend(_CheckUnitTests(input_api, output_api))
Tim van der Lippe5279f842020-01-14 16:26:38 +0000286 results.extend(_CheckNoUncheckedFiles(input_api, output_api))
Tim van der Lippe8fdda112020-01-27 11:27:06 +0000287 results.extend(_CheckForTooLargeFiles(input_api, output_api))
Blink Reformat4c46d092018-04-07 15:32:37 +0000288 return results
289
290
Liviu Raud614e092020-01-08 10:56:33 +0100291def CheckChangeOnUpload(input_api, output_api):
292 results = []
293 results.extend(_CommonChecks(input_api, output_api))
294 results.extend(_CheckDevtoolsLocalization(input_api, output_api))
295 return results
296
297
Blink Reformat4c46d092018-04-07 15:32:37 +0000298def CheckChangeOnCommit(input_api, output_api):
Mandy Chenf0fbdbe2019-08-22 23:58:37 +0000299 results = []
Yang Guo4fd355c2019-09-19 10:59:03 +0200300 results.extend(_CommonChecks(input_api, output_api))
e52a82bdfb5106bd658c2c5ea465e200594be1e2019-10-29 16:02:46 -0700301 results.extend(_CheckDevtoolsLocalization(input_api, output_api, True))
Mathias Bynens032591d2019-10-21 11:51:31 +0200302 results.extend(input_api.canned_checks.CheckChangeHasDescription(input_api, output_api))
Mandy Chenf0fbdbe2019-08-22 23:58:37 +0000303 return results
Blink Reformat4c46d092018-04-07 15:32:37 +0000304
305
Mandy Chena6be46a2019-07-09 17:06:27 +0000306def _getAffectedFiles(input_api, parent_directories, excluded_actions, accepted_endings): # pylint: disable=invalid-name
Yang Guo75beda92019-10-28 08:29:25 +0100307 """Return absolute file paths of affected files (not due to an excluded action)
Mandy Chena6be46a2019-07-09 17:06:27 +0000308 under a parent directory with an accepted file ending.
Yang Guo75beda92019-10-28 08:29:25 +0100309 """
Mandy Chena6be46a2019-07-09 17:06:27 +0000310 local_paths = [
311 f.AbsoluteLocalPath() for f in input_api.AffectedFiles() if all(f.Action() != action for action in excluded_actions)
312 ]
313 affected_files = [
314 file_name for file_name in local_paths
315 if any(parent_directory in file_name for parent_directory in parent_directories) and any(
316 file_name.endswith(accepted_ending) for accepted_ending in accepted_endings)
317 ]
318 return affected_files
319
320
Blink Reformat4c46d092018-04-07 15:32:37 +0000321def _getAffectedFrontEndFiles(input_api):
Blink Reformat4c46d092018-04-07 15:32:37 +0000322 devtools_root = input_api.PresubmitLocalPath()
Yang Guo75beda92019-10-28 08:29:25 +0100323 devtools_front_end = input_api.os_path.join(devtools_root, 'front_end')
324 affected_front_end_files = _getAffectedFiles(input_api, [devtools_front_end], ['D'], ['.js'])
Blink Reformat4c46d092018-04-07 15:32:37 +0000325 return [input_api.os_path.relpath(file_name, devtools_root) for file_name in affected_front_end_files]
326
327
328def _getAffectedJSFiles(input_api):
Blink Reformat4c46d092018-04-07 15:32:37 +0000329 devtools_root = input_api.PresubmitLocalPath()
Yang Guo75beda92019-10-28 08:29:25 +0100330 devtools_front_end = input_api.os_path.join(devtools_root, 'front_end')
331 devtools_scripts = input_api.os_path.join(devtools_root, 'scripts')
332 affected_js_files = _getAffectedFiles(input_api, [devtools_front_end, devtools_scripts], ['D'], ['.js'])
Blink Reformat4c46d092018-04-07 15:32:37 +0000333 return [input_api.os_path.relpath(file_name, devtools_root) for file_name in affected_js_files]
334
335
Lorne Mitchellc56ff2d2019-05-28 23:35:03 +0000336def _checkWithNodeScript(input_api, output_api, script_path, script_arguments=None): # pylint: disable=invalid-name
Blink Reformat4c46d092018-04-07 15:32:37 +0000337 original_sys_path = sys.path
338 try:
Yang Guo75beda92019-10-28 08:29:25 +0100339 sys.path = sys.path + [input_api.os_path.join(input_api.PresubmitLocalPath(), 'scripts')]
Yang Guod8176982019-10-04 20:30:35 +0000340 import devtools_paths
Blink Reformat4c46d092018-04-07 15:32:37 +0000341 finally:
342 sys.path = original_sys_path
343
Yang Guod8176982019-10-04 20:30:35 +0000344 node_path = devtools_paths.node_path()
Blink Reformat4c46d092018-04-07 15:32:37 +0000345
Lorne Mitchellc56ff2d2019-05-28 23:35:03 +0000346 if script_arguments is None:
347 script_arguments = []
Mandy Chen465b4f72019-03-21 22:52:54 +0000348
Blink Reformat4c46d092018-04-07 15:32:37 +0000349 process = input_api.subprocess.Popen(
Lorne Mitchellc56ff2d2019-05-28 23:35:03 +0000350 [node_path, script_path] + script_arguments, stdout=input_api.subprocess.PIPE, stderr=input_api.subprocess.STDOUT)
Blink Reformat4c46d092018-04-07 15:32:37 +0000351 out, _ = process.communicate()
352
353 if process.returncode != 0:
354 return [output_api.PresubmitError(out)]
355 return [output_api.PresubmitNotifyResult(out)]