blob: 5d0d6bbe8c28645fba0bd31ca1f439df0415e541 [file] [log] [blame]
rmistry@google.com8e3ff8c2013-01-17 12:55:34 +00001# Copyright (c) 2013 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
6"""Top-level presubmit script for Skia.
7
8See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
9for more details about the presubmit API built into gcl.
10"""
11
rmistry3cfd1ad2015-03-25 12:53:35 -070012import csv
commit-bot@chromium.org745e08c2014-02-03 14:18:32 +000013import fnmatch
rmistry@google.comf6c5f752013-03-29 17:26:00 +000014import os
commit-bot@chromium.orgcfdc5962014-01-31 17:33:04 +000015import re
rmistryd223fb22015-02-26 10:16:13 -080016import subprocess
rmistry@google.comf6c5f752013-03-29 17:26:00 +000017import sys
commit-bot@chromium.org745e08c2014-02-03 14:18:32 +000018import traceback
rmistry@google.comf6c5f752013-03-29 17:26:00 +000019
rmistry@google.comc2993442013-01-23 14:35:58 +000020
commit-bot@chromium.orgcfdc5962014-01-31 17:33:04 +000021REVERT_CL_SUBJECT_PREFIX = 'Revert '
22
rmistry@google.com547012d2013-04-12 19:45:46 +000023SKIA_TREE_STATUS_URL = 'http://skia-tree-status.appspot.com'
24
rmistry3cfd1ad2015-03-25 12:53:35 -070025CQ_KEYWORDS_THAT_NEED_APPENDING = ('CQ_INCLUDE_TRYBOTS', 'CQ_EXTRA_TRYBOTS',
26 'CQ_EXCLUDE_TRYBOTS', 'CQ_TRYBOTS')
27
rmistryf2d83ca2014-08-26 10:30:29 -070028# Please add the complete email address here (and not just 'xyz@' or 'xyz').
rmistry@google.comfb4a68d2013-08-12 14:51:20 +000029PUBLIC_API_OWNERS = (
30 'reed@chromium.org',
31 'reed@google.com',
32 'bsalomon@chromium.org',
33 'bsalomon@google.com',
rmistry83fab472014-07-18 05:25:56 -070034 'djsollen@chromium.org',
35 'djsollen@google.com',
rmistry@google.comfb4a68d2013-08-12 14:51:20 +000036)
37
commit-bot@chromium.org745e08c2014-02-03 14:18:32 +000038AUTHORS_FILE_NAME = 'AUTHORS'
39
rmistryd223fb22015-02-26 10:16:13 -080040DOCS_PREVIEW_URL = 'https://skia.org/?cl='
41
rmistry@google.com547012d2013-04-12 19:45:46 +000042
rmistry@google.com713276b2013-01-25 18:27:34 +000043def _CheckChangeHasEol(input_api, output_api, source_file_filter=None):
44 """Checks that files end with atleast one \n (LF)."""
45 eof_files = []
46 for f in input_api.AffectedSourceFiles(source_file_filter):
47 contents = input_api.ReadFile(f, 'rb')
48 # Check that the file ends in atleast one newline character.
49 if len(contents) > 1 and contents[-1:] != '\n':
50 eof_files.append(f.LocalPath())
51
52 if eof_files:
53 return [output_api.PresubmitPromptWarning(
54 'These files should end in a newline character:',
55 items=eof_files)]
56 return []
57
58
Eric Borenbb0ef0a2014-06-25 11:13:27 -040059def _PythonChecks(input_api, output_api):
60 """Run checks on any modified Python files."""
61 pylint_disabled_warnings = (
62 'F0401', # Unable to import.
63 'E0611', # No name in module.
64 'W0232', # Class has no __init__ method.
65 'E1002', # Use of super on an old style class.
66 'W0403', # Relative import used.
67 'R0201', # Method could be a function.
68 'E1003', # Using class name in super.
69 'W0613', # Unused argument.
70 )
71 # Run Pylint on only the modified python files. Unfortunately it still runs
72 # Pylint on the whole file instead of just the modified lines.
73 affected_python_files = []
74 for affected_file in input_api.AffectedSourceFiles(None):
75 affected_file_path = affected_file.LocalPath()
76 if affected_file_path.endswith('.py'):
77 affected_python_files.append(affected_file_path)
78 return input_api.canned_checks.RunPylint(
79 input_api, output_api,
80 disabled_warnings=pylint_disabled_warnings,
81 white_list=affected_python_files)
82
83
rmistry01cbf6c2015-03-12 07:48:40 -070084def _IfDefChecks(input_api, output_api):
85 """Ensures if/ifdef are not before includes. See skbug/3362 for details."""
86 comment_block_start_pattern = re.compile('^\s*\/\*.*$')
87 comment_block_middle_pattern = re.compile('^\s+\*.*')
88 comment_block_end_pattern = re.compile('^\s+\*\/.*$')
89 single_line_comment_pattern = re.compile('^\s*//.*$')
90 def is_comment(line):
91 return (comment_block_start_pattern.match(line) or
92 comment_block_middle_pattern.match(line) or
93 comment_block_end_pattern.match(line) or
94 single_line_comment_pattern.match(line))
95
96 empty_line_pattern = re.compile('^\s*$')
97 def is_empty_line(line):
98 return empty_line_pattern.match(line)
99
100 failing_files = []
101 for affected_file in input_api.AffectedSourceFiles(None):
102 affected_file_path = affected_file.LocalPath()
103 if affected_file_path.endswith('.cpp') or affected_file_path.endswith('.h'):
104 f = open(affected_file_path)
105 for line in f.xreadlines():
106 if is_comment(line) or is_empty_line(line):
107 continue
108 # The below will be the first real line after comments and newlines.
109 if line.startswith('#if 0 '):
110 pass
111 elif line.startswith('#if ') or line.startswith('#ifdef '):
112 failing_files.append(affected_file_path)
113 break
114
115 results = []
116 if failing_files:
117 results.append(
118 output_api.PresubmitError(
119 'The following files have #if or #ifdef before includes:\n%s\n\n'
120 'See skbug.com/3362 for why this should be fixed.' %
121 '\n'.join(failing_files)))
122 return results
123
124
borenetc7c91802015-03-25 04:47:02 -0700125def _CopyrightChecks(input_api, output_api, source_file_filter=None):
126 results = []
127 year_pattern = r'\d{4}'
128 year_range_pattern = r'%s(-%s)?' % (year_pattern, year_pattern)
129 years_pattern = r'%s(,%s)*,?' % (year_range_pattern, year_range_pattern)
130 copyright_pattern = (
131 r'Copyright (\([cC]\) )?%s \w+' % years_pattern)
132
133 for affected_file in input_api.AffectedSourceFiles(source_file_filter):
134 if 'third_party' in affected_file.LocalPath():
135 continue
136 contents = input_api.ReadFile(affected_file, 'rb')
137 if not re.search(copyright_pattern, contents):
138 results.append(output_api.PresubmitError(
139 '%s is missing a correct copyright header.' % affected_file))
140 return results
141
142
mtkleine4b19c42015-05-05 10:28:44 -0700143def _ToolFlags(input_api, output_api):
144 """Make sure `{dm,nanobench}_flags.py test` passes if modified."""
145 results = []
146 sources = lambda x: ('dm_flags.py' in x.LocalPath() or
147 'nanobench_flags.py' in x.LocalPath())
148 for f in input_api.AffectedSourceFiles(sources):
149 if 0 != subprocess.call(['python', f.LocalPath(), 'test']):
150 results.append(output_api.PresubmitError('`python %s test` failed' % f))
151 return results
152
153
rmistry@google.com6be0b4c2013-01-17 14:50:59 +0000154def _CommonChecks(input_api, output_api):
155 """Presubmit checks common to upload and commit."""
156 results = []
157 sources = lambda x: (x.LocalPath().endswith('.h') or
158 x.LocalPath().endswith('.gypi') or
159 x.LocalPath().endswith('.gyp') or
160 x.LocalPath().endswith('.py') or
161 x.LocalPath().endswith('.sh') or
mtklein18e55802015-03-25 07:21:20 -0700162 x.LocalPath().endswith('.m') or
163 x.LocalPath().endswith('.mm') or
164 x.LocalPath().endswith('.go') or
165 x.LocalPath().endswith('.c') or
166 x.LocalPath().endswith('.cc') or
rmistry@google.com6be0b4c2013-01-17 14:50:59 +0000167 x.LocalPath().endswith('.cpp'))
168 results.extend(
rmistry@google.com713276b2013-01-25 18:27:34 +0000169 _CheckChangeHasEol(
rmistry@google.com6be0b4c2013-01-17 14:50:59 +0000170 input_api, output_api, source_file_filter=sources))
Eric Borenbb0ef0a2014-06-25 11:13:27 -0400171 results.extend(_PythonChecks(input_api, output_api))
rmistry01cbf6c2015-03-12 07:48:40 -0700172 results.extend(_IfDefChecks(input_api, output_api))
borenetc7c91802015-03-25 04:47:02 -0700173 results.extend(_CopyrightChecks(input_api, output_api,
174 source_file_filter=sources))
mtkleine4b19c42015-05-05 10:28:44 -0700175 results.extend(_ToolFlags(input_api, output_api))
rmistry@google.com6be0b4c2013-01-17 14:50:59 +0000176 return results
177
rmistry@google.com8e3ff8c2013-01-17 12:55:34 +0000178
179def CheckChangeOnUpload(input_api, output_api):
rmistry@google.com6be0b4c2013-01-17 14:50:59 +0000180 """Presubmit checks for the change on upload.
181
182 The following are the presubmit checks:
183 * Check change has one and only one EOL.
184 """
185 results = []
186 results.extend(_CommonChecks(input_api, output_api))
187 return results
rmistry@google.com8e3ff8c2013-01-17 12:55:34 +0000188
189
rmistry@google.comc2993442013-01-23 14:35:58 +0000190def _CheckTreeStatus(input_api, output_api, json_url):
191 """Check whether to allow commit.
192
193 Args:
194 input_api: input related apis.
195 output_api: output related apis.
196 json_url: url to download json style status.
197 """
198 tree_status_results = input_api.canned_checks.CheckTreeIsOpen(
199 input_api, output_api, json_url=json_url)
200 if not tree_status_results:
201 # Check for caution state only if tree is not closed.
202 connection = input_api.urllib2.urlopen(json_url)
203 status = input_api.json.loads(connection.read())
204 connection.close()
rmistry@google.comf6c5f752013-03-29 17:26:00 +0000205 if ('caution' in status['message'].lower() and
206 os.isatty(sys.stdout.fileno())):
207 # Display a prompt only if we are in an interactive shell. Without this
208 # check the commit queue behaves incorrectly because it considers
209 # prompts to be failures.
rmistry@google.comc2993442013-01-23 14:35:58 +0000210 short_text = 'Tree state is: ' + status['general_state']
211 long_text = status['message'] + '\n' + json_url
212 tree_status_results.append(
213 output_api.PresubmitPromptWarning(
214 message=short_text, long_text=long_text))
rmistry@google.com547012d2013-04-12 19:45:46 +0000215 else:
216 # Tree status is closed. Put in message about contacting sheriff.
217 connection = input_api.urllib2.urlopen(
218 SKIA_TREE_STATUS_URL + '/current-sheriff')
219 sheriff_details = input_api.json.loads(connection.read())
220 if sheriff_details:
221 tree_status_results[0]._message += (
222 '\n\nPlease contact the current Skia sheriff (%s) if you are trying '
223 'to submit a build fix\nand do not know how to submit because the '
224 'tree is closed') % sheriff_details['username']
rmistry@google.comc2993442013-01-23 14:35:58 +0000225 return tree_status_results
226
227
commit-bot@chromium.org745e08c2014-02-03 14:18:32 +0000228def _CheckOwnerIsInAuthorsFile(input_api, output_api):
229 results = []
230 issue = input_api.change.issue
231 if issue and input_api.rietveld:
rmistry83fab472014-07-18 05:25:56 -0700232 issue_properties = input_api.rietveld.get_issue_properties(
commit-bot@chromium.org745e08c2014-02-03 14:18:32 +0000233 issue=int(issue), messages=False)
234 owner_email = issue_properties['owner_email']
235
236 try:
237 authors_content = ''
238 for line in open(AUTHORS_FILE_NAME):
239 if not line.startswith('#'):
240 authors_content += line
241 email_fnmatches = re.findall('<(.*)>', authors_content)
242 for email_fnmatch in email_fnmatches:
243 if fnmatch.fnmatch(owner_email, email_fnmatch):
244 # Found a match, the user is in the AUTHORS file break out of the loop
245 break
246 else:
247 # TODO(rmistry): Remove the below CLA messaging once a CLA checker has
248 # been added to the CQ.
249 results.append(
250 output_api.PresubmitError(
251 'The email %s is not in Skia\'s AUTHORS file.\n'
252 'Issue owner, this CL must include an addition to the Skia AUTHORS '
253 'file.\n'
254 'Googler reviewers, please check that the AUTHORS entry '
255 'corresponds to an email address in http://goto/cla-signers. If it '
256 'does not then ask the issue owner to sign the CLA at '
257 'https://developers.google.com/open-source/cla/individual '
258 '(individual) or '
259 'https://developers.google.com/open-source/cla/corporate '
260 '(corporate).'
rmistry83fab472014-07-18 05:25:56 -0700261 % owner_email))
commit-bot@chromium.org745e08c2014-02-03 14:18:32 +0000262 except IOError:
263 # Do not fail if authors file cannot be found.
264 traceback.print_exc()
265 input_api.logging.error('AUTHORS file not found!')
266
267 return results
268
269
rmistry@google.comfb4a68d2013-08-12 14:51:20 +0000270def _CheckLGTMsForPublicAPI(input_api, output_api):
271 """Check LGTMs for public API changes.
272
273 For public API files make sure there is an LGTM from the list of owners in
274 PUBLIC_API_OWNERS.
275 """
276 results = []
277 requires_owner_check = False
rmistry9407ece2014-08-26 14:00:54 -0700278 for affected_file in input_api.AffectedFiles():
279 affected_file_path = affected_file.LocalPath()
rmistry@google.comfb4a68d2013-08-12 14:51:20 +0000280 file_path, file_ext = os.path.splitext(affected_file_path)
rmistry9407ece2014-08-26 14:00:54 -0700281 # We only care about files that end in .h and are under the top-level
mtkleinbda12672015-07-28 08:54:12 -0700282 # include dir, but not include/private.
283 if (file_ext == '.h' and
284 'include' == file_path.split(os.path.sep)[0] and
285 'private' not in file_path):
rmistry@google.comfb4a68d2013-08-12 14:51:20 +0000286 requires_owner_check = True
287
288 if not requires_owner_check:
289 return results
290
291 lgtm_from_owner = False
292 issue = input_api.change.issue
293 if issue and input_api.rietveld:
294 issue_properties = input_api.rietveld.get_issue_properties(
295 issue=int(issue), messages=True)
commit-bot@chromium.orgcfdc5962014-01-31 17:33:04 +0000296 if re.match(REVERT_CL_SUBJECT_PREFIX, issue_properties['subject'], re.I):
297 # It is a revert CL, ignore the public api owners check.
298 return results
rmistryf2d83ca2014-08-26 10:30:29 -0700299
rmistry04abf132015-04-07 07:41:51 -0700300 # TODO(rmistry): Stop checking for COMMIT=false once crbug/470609 is
301 # resolved.
302 if issue_properties['cq_dry_run'] or re.search(
303 r'^COMMIT=false$', issue_properties['description'], re.M):
304 # Ignore public api owners check for dry run CLs since they are not
rmistryf91b7172015-03-12 09:48:10 -0700305 # going to be committed.
306 return results
307
rmistryf2d83ca2014-08-26 10:30:29 -0700308 match = re.search(r'^TBR=(.*)$', issue_properties['description'], re.M)
309 if match:
310 tbr_entries = match.group(1).strip().split(',')
311 for owner in PUBLIC_API_OWNERS:
312 if owner in tbr_entries or owner.split('@')[0] in tbr_entries:
313 # If an owner is specified in the TBR= line then ignore the public
314 # api owners check.
315 return results
316
rmistry@google.comfb4a68d2013-08-12 14:51:20 +0000317 if issue_properties['owner_email'] in PUBLIC_API_OWNERS:
318 # An owner created the CL that is an automatic LGTM.
319 lgtm_from_owner = True
320
321 messages = issue_properties.get('messages')
322 if messages:
323 for message in messages:
324 if (message['sender'] in PUBLIC_API_OWNERS and
325 'lgtm' in message['text'].lower()):
326 # Found an lgtm in a message from an owner.
327 lgtm_from_owner = True
Eric Borenbb0ef0a2014-06-25 11:13:27 -0400328 break
commit-bot@chromium.orgcfdc5962014-01-31 17:33:04 +0000329
rmistry@google.comfb4a68d2013-08-12 14:51:20 +0000330 if not lgtm_from_owner:
331 results.append(
332 output_api.PresubmitError(
mtkleinbda12672015-07-28 08:54:12 -0700333 "If this CL adds to or changes Skia's public API, you need an LGTM "
334 "from any of %s. If this CL only removes from or doesn't change "
335 "Skia's public API, please add a short note to the CL saying so "
336 "and add one of those reviewers on a TBR= line. If you don't know "
337 "if this CL affects Skia's public API, treat it like it does."
338 % str(PUBLIC_API_OWNERS)))
rmistry@google.comfb4a68d2013-08-12 14:51:20 +0000339 return results
340
341
rmistryd223fb22015-02-26 10:16:13 -0800342def PostUploadHook(cl, change, output_api):
343 """git cl upload will call this hook after the issue is created/modified.
344
345 This hook does the following:
346 * Adds a link to preview docs changes if there are any docs changes in the CL.
347 * Adds 'NOTRY=true' if the CL contains only docs changes.
rmistry896f3932015-02-26 11:52:05 -0800348 * Adds 'NOTREECHECKS=true' for non master branch changes since they do not
349 need to be gated on the master branch's tree.
350 * Adds 'NOTRY=true' for non master branch changes since trybots do not yet
351 work on them.
borenetaf6005c2015-06-04 15:15:42 -0700352 * Adds 'NOPRESUBMIT=true' for non master branch changes since those don't
353 run the presubmit checks.
rmistryd223fb22015-02-26 10:16:13 -0800354 """
355
356 results = []
357 atleast_one_docs_change = False
358 all_docs_changes = True
359 for affected_file in change.AffectedFiles():
360 affected_file_path = affected_file.LocalPath()
361 file_path, _ = os.path.splitext(affected_file_path)
362 if 'site' == file_path.split(os.path.sep)[0]:
363 atleast_one_docs_change = True
364 else:
365 all_docs_changes = False
366 if atleast_one_docs_change and not all_docs_changes:
367 break
368
369 issue = cl.issue
370 rietveld_obj = cl.RpcServer()
371 if issue and rietveld_obj:
372 original_description = rietveld_obj.get_description(issue)
373 new_description = original_description
374
375 # If the change includes only doc changes then add NOTRY=true in the
376 # CL's description if it does not exist yet.
377 if all_docs_changes and not re.search(
rmistry896f3932015-02-26 11:52:05 -0800378 r'^NOTRY=true$', new_description, re.M | re.I):
rmistryd223fb22015-02-26 10:16:13 -0800379 new_description += '\nNOTRY=true'
380 results.append(
381 output_api.PresubmitNotifyResult(
382 'This change has only doc changes. Automatically added '
383 '\'NOTRY=true\' to the CL\'s description'))
384
385 # If there is atleast one docs change then add preview link in the CL's
386 # description if it does not already exist there.
387 if atleast_one_docs_change and not re.search(
rmistry896f3932015-02-26 11:52:05 -0800388 r'^DOCS_PREVIEW=.*', new_description, re.M | re.I):
rmistryd223fb22015-02-26 10:16:13 -0800389 # Automatically add a link to where the docs can be previewed.
390 new_description += '\nDOCS_PREVIEW= %s%s' % (DOCS_PREVIEW_URL, issue)
391 results.append(
392 output_api.PresubmitNotifyResult(
393 'Automatically added a link to preview the docs changes to the '
394 'CL\'s description'))
395
rmistry896f3932015-02-26 11:52:05 -0800396 # If the target ref is not master then add NOTREECHECKS=true and NOTRY=true
397 # to the CL's description if it does not already exist there.
398 target_ref = rietveld_obj.get_issue_properties(issue, False).get(
399 'target_ref', '')
400 if target_ref != 'refs/heads/master':
401 if not re.search(
402 r'^NOTREECHECKS=true$', new_description, re.M | re.I):
403 new_description += "\nNOTREECHECKS=true"
404 results.append(
405 output_api.PresubmitNotifyResult(
406 'Branch changes do not need to rely on the master branch\'s '
407 'tree status. Automatically added \'NOTREECHECKS=true\' to the '
408 'CL\'s description'))
409 if not re.search(
410 r'^NOTRY=true$', new_description, re.M | re.I):
411 new_description += "\nNOTRY=true"
412 results.append(
413 output_api.PresubmitNotifyResult(
414 'Trybots do not yet work for non-master branches. '
415 'Automatically added \'NOTRY=true\' to the CL\'s description'))
borenetaf6005c2015-06-04 15:15:42 -0700416 if not re.search(
417 r'^NOPRESUBMIT=true$', new_description, re.M | re.I):
418 new_description += "\nNOPRESUBMIT=true"
419 results.append(
420 output_api.PresubmitNotifyResult(
421 'Branch changes do not run the presubmit checks.'))
rmistry896f3932015-02-26 11:52:05 -0800422
rmistry3cfd1ad2015-03-25 12:53:35 -0700423 # Read and process the HASHTAGS file.
rmistry57291dc2015-04-01 09:12:51 -0700424 hashtags_fullpath = os.path.join(change._local_root, 'HASHTAGS')
425 with open(hashtags_fullpath, 'rb') as hashtags_csv:
rmistry3cfd1ad2015-03-25 12:53:35 -0700426 hashtags_reader = csv.reader(hashtags_csv, delimiter=',')
427 for row in hashtags_reader:
428 if not row or row[0].startswith('#'):
429 # Ignore empty lines and comments
430 continue
431 hashtag = row[0]
432 # Search for the hashtag in the description.
433 if re.search('#%s' % hashtag, new_description, re.M | re.I):
434 for mapped_text in row[1:]:
435 # Special case handling for CQ_KEYWORDS_THAT_NEED_APPENDING.
436 appended_description = _HandleAppendingCQKeywords(
437 hashtag, mapped_text, new_description, results, output_api)
438 if appended_description:
439 new_description = appended_description
440 continue
441
442 # Add the mapped text if it does not already exist in the
443 # CL's description.
444 if not re.search(
445 r'^%s$' % mapped_text, new_description, re.M | re.I):
446 new_description += '\n%s' % mapped_text
447 results.append(
448 output_api.PresubmitNotifyResult(
449 'Found \'#%s\', automatically added \'%s\' to the CL\'s '
450 'description' % (hashtag, mapped_text)))
rmistry896f3932015-02-26 11:52:05 -0800451
rmistryd223fb22015-02-26 10:16:13 -0800452 # If the description has changed update it.
453 if new_description != original_description:
454 rietveld_obj.update_description(issue, new_description)
455
456 return results
457
458
rmistry3cfd1ad2015-03-25 12:53:35 -0700459def _HandleAppendingCQKeywords(hashtag, keyword_and_value, description,
460 results, output_api):
461 """Handles the CQ keywords that need appending if specified in hashtags."""
462 keyword = keyword_and_value.split('=')[0]
463 if keyword in CQ_KEYWORDS_THAT_NEED_APPENDING:
464 # If the keyword is already in the description then append to it.
465 match = re.search(
466 r'^%s=(.*)$' % keyword, description, re.M | re.I)
467 if match:
468 old_values = match.group(1).split(';')
469 new_value = keyword_and_value.split('=')[1]
470 if new_value in old_values:
471 # Do not need to do anything here.
472 return description
473 # Update the description with the new values.
474 new_description = description.replace(
475 match.group(0), "%s;%s" % (match.group(0), new_value))
476 results.append(
477 output_api.PresubmitNotifyResult(
478 'Found \'#%s\', automatically appended \'%s\' to %s in '
479 'the CL\'s description' % (hashtag, new_value, keyword)))
480 return new_description
481 return None
482
483
rmistry@google.com8e3ff8c2013-01-17 12:55:34 +0000484def CheckChangeOnCommit(input_api, output_api):
485 """Presubmit checks for the change on commit.
486
487 The following are the presubmit checks:
rmistry@google.com6be0b4c2013-01-17 14:50:59 +0000488 * Check change has one and only one EOL.
rmistry@google.comc2993442013-01-23 14:35:58 +0000489 * Ensures that the Skia tree is open in
490 http://skia-tree-status.appspot.com/. Shows a warning if it is in 'Caution'
491 state and an error if it is in 'Closed' state.
rmistry@google.com8e3ff8c2013-01-17 12:55:34 +0000492 """
493 results = []
rmistry@google.com6be0b4c2013-01-17 14:50:59 +0000494 results.extend(_CommonChecks(input_api, output_api))
rmistry@google.com8e3ff8c2013-01-17 12:55:34 +0000495 results.extend(
rmistry@google.comc2993442013-01-23 14:35:58 +0000496 _CheckTreeStatus(input_api, output_api, json_url=(
rmistry@google.com547012d2013-04-12 19:45:46 +0000497 SKIA_TREE_STATUS_URL + '/banner-status?format=json')))
rmistry@google.comfb4a68d2013-08-12 14:51:20 +0000498 results.extend(_CheckLGTMsForPublicAPI(input_api, output_api))
commit-bot@chromium.org745e08c2014-02-03 14:18:32 +0000499 results.extend(_CheckOwnerIsInAuthorsFile(input_api, output_api))
rmistry@google.com8e3ff8c2013-01-17 12:55:34 +0000500 return results