Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1051)

Side by Side Diff: PRESUBMIT.py

Issue 1383743002: Automatically add extra CQ trybots for predetermined paths (Closed) Base URL: https://skia.googlesource.com/skia@master
Patch Set: Rebase Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « HASHTAGS ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 # 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 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 5
6 """Top-level presubmit script for Skia. 6 """Top-level presubmit script for Skia.
7 7
8 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts 8 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
9 for more details about the presubmit API built into gcl. 9 for more details about the presubmit API built into gcl.
10 """ 10 """
11 11
12 import collections
12 import csv 13 import csv
13 import fnmatch 14 import fnmatch
14 import os 15 import os
15 import re 16 import re
16 import subprocess 17 import subprocess
17 import sys 18 import sys
18 import traceback 19 import traceback
19 20
20 21
21 REVERT_CL_SUBJECT_PREFIX = 'Revert ' 22 REVERT_CL_SUBJECT_PREFIX = 'Revert '
22 23
23 SKIA_TREE_STATUS_URL = 'http://skia-tree-status.appspot.com' 24 SKIA_TREE_STATUS_URL = 'http://skia-tree-status.appspot.com'
24 25
25 CQ_KEYWORDS_THAT_NEED_APPENDING = ('CQ_INCLUDE_TRYBOTS', 'CQ_EXTRA_TRYBOTS',
26 'CQ_EXCLUDE_TRYBOTS', 'CQ_TRYBOTS')
27
28 # Please add the complete email address here (and not just 'xyz@' or 'xyz'). 26 # Please add the complete email address here (and not just 'xyz@' or 'xyz').
29 PUBLIC_API_OWNERS = ( 27 PUBLIC_API_OWNERS = (
30 'reed@chromium.org', 28 'reed@chromium.org',
31 'reed@google.com', 29 'reed@google.com',
32 'bsalomon@chromium.org', 30 'bsalomon@chromium.org',
33 'bsalomon@google.com', 31 'bsalomon@google.com',
34 'djsollen@chromium.org', 32 'djsollen@chromium.org',
35 'djsollen@google.com', 33 'djsollen@google.com',
36 ) 34 )
37 35
38 AUTHORS_FILE_NAME = 'AUTHORS' 36 AUTHORS_FILE_NAME = 'AUTHORS'
39 37
40 DOCS_PREVIEW_URL = 'https://skia.org/?cl=' 38 DOCS_PREVIEW_URL = 'https://skia.org/?cl='
41 39
40 # Path to CQ bots feature is described in skbug.com/4364
41 PATH_PREFIX_TO_EXTRA_TRYBOTS = {
42 # pylint: disable=line-too-long
43 'cmake/': 'client.skia.compile:Build-Mac10.9-Clang-x86_64-Release-CMake-Tryb ot,Build-Ubuntu-GCC-x86_64-Release-CMake-Trybot',
44 # pylint: disable=line-too-long
45 'src/opts/': 'client.skia:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_N O_SIMD-Trybot',
46
47 # Below are examples to show what is possible with this feature.
48 # 'src/svg/': 'master1:abc;master2:def',
49 # 'src/svg/parser/': 'master3:ghi,jkl;master4:mno',
50 # 'src/image/SkImage_Base.h': 'master5:pqr,stu;master1:abc1;master2:def',
51 }
52
42 53
43 def _CheckChangeHasEol(input_api, output_api, source_file_filter=None): 54 def _CheckChangeHasEol(input_api, output_api, source_file_filter=None):
44 """Checks that files end with atleast one \n (LF).""" 55 """Checks that files end with atleast one \n (LF)."""
45 eof_files = [] 56 eof_files = []
46 for f in input_api.AffectedSourceFiles(source_file_filter): 57 for f in input_api.AffectedSourceFiles(source_file_filter):
47 contents = input_api.ReadFile(f, 'rb') 58 contents = input_api.ReadFile(f, 'rb')
48 # Check that the file ends in atleast one newline character. 59 # Check that the file ends in atleast one newline character.
49 if len(contents) > 1 and contents[-1:] != '\n': 60 if len(contents) > 1 and contents[-1:] != '\n':
50 eof_files.append(f.LocalPath()) 61 eof_files.append(f.LocalPath())
51 62
(...skipping 280 matching lines...) Expand 10 before | Expand all | Expand 10 after
332 343
333 This hook does the following: 344 This hook does the following:
334 * Adds a link to preview docs changes if there are any docs changes in the CL. 345 * Adds a link to preview docs changes if there are any docs changes in the CL.
335 * Adds 'NOTRY=true' if the CL contains only docs changes. 346 * Adds 'NOTRY=true' if the CL contains only docs changes.
336 * Adds 'NOTREECHECKS=true' for non master branch changes since they do not 347 * Adds 'NOTREECHECKS=true' for non master branch changes since they do not
337 need to be gated on the master branch's tree. 348 need to be gated on the master branch's tree.
338 * Adds 'NOTRY=true' for non master branch changes since trybots do not yet 349 * Adds 'NOTRY=true' for non master branch changes since trybots do not yet
339 work on them. 350 work on them.
340 * Adds 'NOPRESUBMIT=true' for non master branch changes since those don't 351 * Adds 'NOPRESUBMIT=true' for non master branch changes since those don't
341 run the presubmit checks. 352 run the presubmit checks.
353 * Adds extra trybots for the paths defined in PATH_TO_EXTRA_TRYBOTS.
342 """ 354 """
343 355
344 results = [] 356 results = []
345 atleast_one_docs_change = False 357 atleast_one_docs_change = False
346 all_docs_changes = True 358 all_docs_changes = True
347 for affected_file in change.AffectedFiles(): 359 for affected_file in change.AffectedFiles():
348 affected_file_path = affected_file.LocalPath() 360 affected_file_path = affected_file.LocalPath()
349 file_path, _ = os.path.splitext(affected_file_path) 361 file_path, _ = os.path.splitext(affected_file_path)
350 if 'site' == file_path.split(os.path.sep)[0]: 362 if 'site' == file_path.split(os.path.sep)[0]:
351 atleast_one_docs_change = True 363 atleast_one_docs_change = True
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
401 output_api.PresubmitNotifyResult( 413 output_api.PresubmitNotifyResult(
402 'Trybots do not yet work for non-master branches. ' 414 'Trybots do not yet work for non-master branches. '
403 'Automatically added \'NOTRY=true\' to the CL\'s description')) 415 'Automatically added \'NOTRY=true\' to the CL\'s description'))
404 if not re.search( 416 if not re.search(
405 r'^NOPRESUBMIT=true$', new_description, re.M | re.I): 417 r'^NOPRESUBMIT=true$', new_description, re.M | re.I):
406 new_description += "\nNOPRESUBMIT=true" 418 new_description += "\nNOPRESUBMIT=true"
407 results.append( 419 results.append(
408 output_api.PresubmitNotifyResult( 420 output_api.PresubmitNotifyResult(
409 'Branch changes do not run the presubmit checks.')) 421 'Branch changes do not run the presubmit checks.'))
410 422
411 # Read and process the HASHTAGS file. 423 # Automatically set CQ_EXTRA_TRYBOTS if any of the changed files here begin
412 hashtags_fullpath = os.path.join(change._local_root, 'HASHTAGS') 424 # with the paths of interest.
413 with open(hashtags_fullpath, 'rb') as hashtags_csv: 425 cq_master_to_trybots = collections.defaultdict(set)
414 hashtags_reader = csv.reader(hashtags_csv, delimiter=',') 426 for affected_file in change.AffectedFiles():
415 for row in hashtags_reader: 427 affected_file_path = affected_file.LocalPath()
416 if not row or row[0].startswith('#'): 428 for path_prefix, extra_bots in PATH_PREFIX_TO_EXTRA_TRYBOTS.iteritems():
417 # Ignore empty lines and comments 429 if affected_file_path.startswith(path_prefix):
418 continue 430 results.append(
419 hashtag = row[0] 431 output_api.PresubmitNotifyResult(
420 # Search for the hashtag in the description. 432 'Your CL modifies the path %s.\nAutomatically adding %s to '
421 if re.search('#%s' % hashtag, new_description, re.M | re.I): 433 'the CL description.' % (affected_file_path, extra_bots)))
422 for mapped_text in row[1:]: 434 _MergeCQExtraTrybotsMaps(
423 # Special case handling for CQ_KEYWORDS_THAT_NEED_APPENDING. 435 cq_master_to_trybots, _GetCQExtraTrybotsMap(extra_bots))
424 appended_description = _HandleAppendingCQKeywords( 436 if cq_master_to_trybots:
425 hashtag, mapped_text, new_description, results, output_api) 437 new_description = _AddCQExtraTrybotsToDesc(
426 if appended_description: 438 cq_master_to_trybots, new_description)
427 new_description = appended_description
428 continue
429
430 # Add the mapped text if it does not already exist in the
431 # CL's description.
432 if not re.search(
433 r'^%s$' % mapped_text, new_description, re.M | re.I):
434 new_description += '\n%s' % mapped_text
435 results.append(
436 output_api.PresubmitNotifyResult(
437 'Found \'#%s\', automatically added \'%s\' to the CL\'s '
438 'description' % (hashtag, mapped_text)))
439 439
440 # If the description has changed update it. 440 # If the description has changed update it.
441 if new_description != original_description: 441 if new_description != original_description:
442 rietveld_obj.update_description(issue, new_description) 442 rietveld_obj.update_description(issue, new_description)
443 443
444 return results 444 return results
445 445
446 446
447 def _HandleAppendingCQKeywords(hashtag, keyword_and_value, description, 447 def _AddCQExtraTrybotsToDesc(cq_master_to_trybots, description):
448 results, output_api): 448 """Adds the specified master and trybots to the CQ_EXTRA_TRYBOTS keyword.
449 """Handles the CQ keywords that need appending if specified in hashtags.""" 449
450 keyword = keyword_and_value.split('=')[0] 450 If the keyword already exists in the description then it appends to it only
451 if keyword in CQ_KEYWORDS_THAT_NEED_APPENDING: 451 if the specified values do not already exist.
452 # If the keyword is already in the description then append to it. 452 If the keyword does not exist then it creates a new section in the
453 match = re.search( 453 description.
454 r'^%s=(.*)$' % keyword, description, re.M | re.I) 454 """
455 if match: 455 match = re.search(r'^CQ_EXTRA_TRYBOTS=(.*)$', description, re.M | re.I)
456 old_values = match.group(1).split(';') 456 if match:
457 new_value = keyword_and_value.split('=')[1] 457 original_trybots_map = _GetCQExtraTrybotsMap(match.group(1))
458 if new_value in old_values: 458 _MergeCQExtraTrybotsMaps(cq_master_to_trybots, original_trybots_map)
459 # Do not need to do anything here. 459 new_description = description.replace(
460 return description 460 match.group(0), _GetCQExtraTrybotsStr(cq_master_to_trybots))
461 # Update the description with the new values. 461 else:
462 new_description = description.replace( 462 new_description = description + "\n%s" % (
463 match.group(0), "%s;%s" % (match.group(0), new_value)) 463 _GetCQExtraTrybotsStr(cq_master_to_trybots))
464 results.append( 464 return new_description
465 output_api.PresubmitNotifyResult( 465
466 'Found \'#%s\', automatically appended \'%s\' to %s in ' 466
467 'the CL\'s description' % (hashtag, new_value, keyword))) 467 def _MergeCQExtraTrybotsMaps(dest_map, map_to_be_consumed):
468 return new_description 468 """Merges two maps of masters to trybots into one."""
469 return None 469 for master, trybots in map_to_be_consumed.iteritems():
470 dest_map[master].update(trybots)
471 return dest_map
472
473
474 def _GetCQExtraTrybotsMap(cq_extra_trybots_str):
475 """Parses the CQ_EXTRA_TRYBOTS str and returns a map of masters to trybots."""
476 cq_master_to_trybots = collections.defaultdict(set)
477 for section in cq_extra_trybots_str.split(';'):
478 if section:
479 master, bots = section.split(':')
480 cq_master_to_trybots[master].update(bots.split(','))
481 return cq_master_to_trybots
482
483
484 def _GetCQExtraTrybotsStr(cq_master_to_trybots):
485 """Constructs the CQ_EXTRA_TRYBOTS str from a map of masters to trybots."""
486 sections = []
487 for master, trybots in cq_master_to_trybots.iteritems():
488 sections.append('%s:%s' % (master, ','.join(trybots)))
489 return 'CQ_EXTRA_TRYBOTS=%s' % ';'.join(sections)
470 490
471 491
472 def CheckChangeOnCommit(input_api, output_api): 492 def CheckChangeOnCommit(input_api, output_api):
473 """Presubmit checks for the change on commit. 493 """Presubmit checks for the change on commit.
474 494
475 The following are the presubmit checks: 495 The following are the presubmit checks:
476 * Check change has one and only one EOL. 496 * Check change has one and only one EOL.
477 * Ensures that the Skia tree is open in 497 * Ensures that the Skia tree is open in
478 http://skia-tree-status.appspot.com/. Shows a warning if it is in 'Caution' 498 http://skia-tree-status.appspot.com/. Shows a warning if it is in 'Caution'
479 state and an error if it is in 'Closed' state. 499 state and an error if it is in 'Closed' state.
480 """ 500 """
481 results = [] 501 results = []
482 results.extend(_CommonChecks(input_api, output_api)) 502 results.extend(_CommonChecks(input_api, output_api))
483 results.extend( 503 results.extend(
484 _CheckTreeStatus(input_api, output_api, json_url=( 504 _CheckTreeStatus(input_api, output_api, json_url=(
485 SKIA_TREE_STATUS_URL + '/banner-status?format=json'))) 505 SKIA_TREE_STATUS_URL + '/banner-status?format=json')))
486 results.extend(_CheckLGTMsForPublicAPI(input_api, output_api)) 506 results.extend(_CheckLGTMsForPublicAPI(input_api, output_api))
487 results.extend(_CheckOwnerIsInAuthorsFile(input_api, output_api)) 507 results.extend(_CheckOwnerIsInAuthorsFile(input_api, output_api))
488 return results 508 return results
OLDNEW
« no previous file with comments | « HASHTAGS ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698