1fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot# Copyright (c) 2013 The Chromium Authors. All rights reserved. 2fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot# Use of this source code is governed by a BSD-style license that can be 3fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot# found in the LICENSE file. 4fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 5fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 6fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot"""Top-level presubmit script for Skia. 7fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 8fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSee http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts 9fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotfor more details about the presubmit API built into gcl. 10fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot""" 11fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 12fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotimport collections 13fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotimport csv 14fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotimport fnmatch 15fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotimport os 16fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotimport re 17fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotimport subprocess 18fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotimport sys 19fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotimport traceback 20fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 21fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 22fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotREVERT_CL_SUBJECT_PREFIX = 'Revert ' 23fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 24fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSKIA_TREE_STATUS_URL = 'http://skia-tree-status.appspot.com' 25fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 26fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot# Please add the complete email address here (and not just 'xyz@' or 'xyz'). 27fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotPUBLIC_API_OWNERS = ( 28fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'reed@chromium.org', 29fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'reed@google.com', 30fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'bsalomon@chromium.org', 31fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'bsalomon@google.com', 32fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'djsollen@chromium.org', 33fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'djsollen@google.com', 34fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'hcm@chromium.org', 35fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'hcm@google.com', 36fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot) 37fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 38fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotAUTO_COMMIT_BOTS = ( 39fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'update-docs@skia.org', 40fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'update-skps@skia.org' 41fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot) 42fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 43fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotAUTHORS_FILE_NAME = 'AUTHORS' 44fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 45fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotDOCS_PREVIEW_URL = 'https://skia.org/?cl=' 46fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotGOLD_TRYBOT_URL = 'https://gold.skia.org/search?issue=' 47fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 48fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot# Path to CQ bots feature is described in https://bug.skia.org/4364 49fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotPATH_PREFIX_TO_EXTRA_TRYBOTS = { 50fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'src/opts/': ('skia.primary:' 51fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SKNX_NO_SIMD'), 52fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'include/private/SkAtomics.h': ('skia.primary:' 53fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN,' 54fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-TSAN' 55fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot ), 56fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 57fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Below are examples to show what is possible with this feature. 58fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # 'src/svg/': 'master1:abc;master2:def', 59fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # 'src/svg/parser/': 'master3:ghi,jkl;master4:mno', 60fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # 'src/image/SkImage_Base.h': 'master5:pqr,stu;master1:abc1;master2:def', 61fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} 62fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 63fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSERVICE_ACCOUNT_SUFFIX = '@skia-buildbots.google.com.iam.gserviceaccount.com' 64fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 65fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 66fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotdef _CheckChangeHasEol(input_api, output_api, source_file_filter=None): 67fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """Checks that files end with atleast one \n (LF).""" 68fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot eof_files = [] 69fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for f in input_api.AffectedSourceFiles(source_file_filter): 70fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot contents = input_api.ReadFile(f, 'rb') 71fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Check that the file ends in atleast one newline character. 72fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if len(contents) > 1 and contents[-1:] != '\n': 73fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot eof_files.append(f.LocalPath()) 74fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 75fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if eof_files: 76fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return [output_api.PresubmitPromptWarning( 77fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'These files should end in a newline character:', 78fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot items=eof_files)] 79fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return [] 80fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 81fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 82fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotdef _PythonChecks(input_api, output_api): 83fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """Run checks on any modified Python files.""" 84fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot pylint_disabled_files = ( 85fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'infra/bots/recipes.py', 86fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot ) 87fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot pylint_disabled_warnings = ( 88fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'F0401', # Unable to import. 89fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'E0611', # No name in module. 90fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'W0232', # Class has no __init__ method. 91fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'E1002', # Use of super on an old style class. 92fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'W0403', # Relative import used. 93fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'R0201', # Method could be a function. 94fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'E1003', # Using class name in super. 95fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'W0613', # Unused argument. 96fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'W0105', # String statement has no effect. 97fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot ) 98fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Run Pylint on only the modified python files. Unfortunately it still runs 99fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Pylint on the whole file instead of just the modified lines. 100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot affected_python_files = [] 101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for affected_file in input_api.AffectedSourceFiles(None): 102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot affected_file_path = affected_file.LocalPath() 103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if affected_file_path.endswith('.py'): 104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if affected_file_path not in pylint_disabled_files: 105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot affected_python_files.append(affected_file_path) 106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return input_api.canned_checks.RunPylint( 107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot input_api, output_api, 108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot disabled_warnings=pylint_disabled_warnings, 109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot white_list=affected_python_files) 110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotdef _IfDefChecks(input_api, output_api): 113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """Ensures if/ifdef are not before includes. See skbug/3362 for details.""" 114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot comment_block_start_pattern = re.compile('^\s*\/\*.*$') 115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot comment_block_middle_pattern = re.compile('^\s+\*.*') 116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot comment_block_end_pattern = re.compile('^\s+\*\/.*$') 117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot single_line_comment_pattern = re.compile('^\s*//.*$') 118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot def is_comment(line): 119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return (comment_block_start_pattern.match(line) or 120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot comment_block_middle_pattern.match(line) or 121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot comment_block_end_pattern.match(line) or 122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot single_line_comment_pattern.match(line)) 123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot empty_line_pattern = re.compile('^\s*$') 125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot def is_empty_line(line): 126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return empty_line_pattern.match(line) 127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot failing_files = [] 129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for affected_file in input_api.AffectedSourceFiles(None): 130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot affected_file_path = affected_file.LocalPath() 131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if affected_file_path.endswith('.cpp') or affected_file_path.endswith('.h'): 132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot f = open(affected_file_path) 133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for line in f.xreadlines(): 134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if is_comment(line) or is_empty_line(line): 135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot continue 136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # The below will be the first real line after comments and newlines. 137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if line.startswith('#if 0 '): 138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot pass 139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot elif line.startswith('#if ') or line.startswith('#ifdef '): 140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot failing_files.append(affected_file_path) 141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot break 142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results = [] 144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if failing_files: 145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.append( 146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot output_api.PresubmitError( 147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'The following files have #if or #ifdef before includes:\n%s\n\n' 148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'See https://bug.skia.org/3362 for why this should be fixed.' % 149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot '\n'.join(failing_files))) 150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotdef _CopyrightChecks(input_api, output_api, source_file_filter=None): 154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results = [] 155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot year_pattern = r'\d{4}' 156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot year_range_pattern = r'%s(-%s)?' % (year_pattern, year_pattern) 157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot years_pattern = r'%s(,%s)*,?' % (year_range_pattern, year_range_pattern) 158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot copyright_pattern = ( 159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot r'Copyright (\([cC]\) )?%s \w+' % years_pattern) 160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for affected_file in input_api.AffectedSourceFiles(source_file_filter): 162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if 'third_party' in affected_file.LocalPath(): 163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot continue 164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot contents = input_api.ReadFile(affected_file, 'rb') 165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if not re.search(copyright_pattern, contents): 166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.append(output_api.PresubmitError( 167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot '%s is missing a correct copyright header.' % affected_file)) 168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 170fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 171fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotdef _ToolFlags(input_api, output_api): 172fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """Make sure `{dm,nanobench}_flags.py test` passes if modified.""" 173fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results = [] 174fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot sources = lambda x: ('dm_flags.py' in x.LocalPath() or 175fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'nanobench_flags.py' in x.LocalPath()) 176fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for f in input_api.AffectedSourceFiles(sources): 177fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if 0 != subprocess.call(['python', f.LocalPath(), 'test']): 178fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.append(output_api.PresubmitError('`python %s test` failed' % f)) 179fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 180fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 181fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 182fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotdef _InfraTests(input_api, output_api): 183fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """Run the infra tests.""" 184fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results = [] 185fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if not any(f.LocalPath().startswith('infra') 186fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for f in input_api.AffectedFiles()): 187fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 188fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 189fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot cmd = ['python', os.path.join('infra', 'bots', 'infra_tests.py')] 190fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot try: 191fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot subprocess.check_output(cmd) 192fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot except subprocess.CalledProcessError as e: 193fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.append(output_api.PresubmitError( 194fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot '`%s` failed:\n%s' % (' '.join(cmd), e.output))) 195fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 196fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 197fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 198fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotdef _CheckGNFormatted(input_api, output_api): 199fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """Make sure any .gn files we're changing have been formatted.""" 200fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results = [] 201fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for f in input_api.AffectedFiles(): 202fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (not f.LocalPath().endswith('.gn') and 203fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot not f.LocalPath().endswith('.gni')): 204fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot continue 205fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 206fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot gn = 'gn.bat' if 'win32' in sys.platform else 'gn' 207fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot cmd = [gn, 'format', '--dry-run', f.LocalPath()] 208fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot try: 209fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot subprocess.check_output(cmd) 210fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot except subprocess.CalledProcessError: 211fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fix = 'gn format ' + f.LocalPath() 212fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.append(output_api.PresubmitError( 213fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot '`%s` failed, try\n\t%s' % (' '.join(cmd), fix))) 214fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 215fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 216fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 217fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotdef _CommonChecks(input_api, output_api): 218fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """Presubmit checks common to upload and commit.""" 219fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results = [] 220fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot sources = lambda x: (x.LocalPath().endswith('.h') or 221fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot x.LocalPath().endswith('.py') or 222fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot x.LocalPath().endswith('.sh') or 223fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot x.LocalPath().endswith('.m') or 224fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot x.LocalPath().endswith('.mm') or 225fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot x.LocalPath().endswith('.go') or 226fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot x.LocalPath().endswith('.c') or 227fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot x.LocalPath().endswith('.cc') or 228fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot x.LocalPath().endswith('.cpp')) 229fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.extend( 230fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot _CheckChangeHasEol( 231fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot input_api, output_api, source_file_filter=sources)) 232fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.extend( 233fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot input_api.canned_checks.CheckChangeHasNoCR( 234fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot input_api, output_api, source_file_filter=sources)) 235fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.extend( 236fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot input_api.canned_checks.CheckChangeHasNoStrayWhitespace( 237fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot input_api, output_api, source_file_filter=sources)) 238fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.extend(_PythonChecks(input_api, output_api)) 239fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.extend(_IfDefChecks(input_api, output_api)) 240fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.extend(_CopyrightChecks(input_api, output_api, 241fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot source_file_filter=sources)) 242fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.extend(_ToolFlags(input_api, output_api)) 243fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 244fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 245fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 246fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotdef CheckChangeOnUpload(input_api, output_api): 247fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """Presubmit checks for the change on upload. 248fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 249fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot The following are the presubmit checks: 250fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Check change has one and only one EOL. 251fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """ 252fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results = [] 253fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.extend(_CommonChecks(input_api, output_api)) 254fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Run on upload, not commit, since the presubmit bot apparently doesn't have 255fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # coverage or Go installed. 256fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.extend(_InfraTests(input_api, output_api)) 257fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 258fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.extend(_CheckGNFormatted(input_api, output_api)) 259fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 260fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 261fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 262fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotdef _CheckTreeStatus(input_api, output_api, json_url): 263fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """Check whether to allow commit. 264fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 265fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot Args: 266fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot input_api: input related apis. 267fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot output_api: output related apis. 268fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot json_url: url to download json style status. 269fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """ 270fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot tree_status_results = input_api.canned_checks.CheckTreeIsOpen( 271fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot input_api, output_api, json_url=json_url) 272fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if not tree_status_results: 273fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Check for caution state only if tree is not closed. 274fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot connection = input_api.urllib2.urlopen(json_url) 275fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot status = input_api.json.loads(connection.read()) 276fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot connection.close() 277fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if ('caution' in status['message'].lower() and 278fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot os.isatty(sys.stdout.fileno())): 279fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Display a prompt only if we are in an interactive shell. Without this 280fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # check the commit queue behaves incorrectly because it considers 281fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # prompts to be failures. 282fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot short_text = 'Tree state is: ' + status['general_state'] 283fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot long_text = status['message'] + '\n' + json_url 284fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot tree_status_results.append( 285fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot output_api.PresubmitPromptWarning( 286fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot message=short_text, long_text=long_text)) 287fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot else: 288fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Tree status is closed. Put in message about contacting sheriff. 289fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot connection = input_api.urllib2.urlopen( 290fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SKIA_TREE_STATUS_URL + '/current-sheriff') 291fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot sheriff_details = input_api.json.loads(connection.read()) 292fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if sheriff_details: 293fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot tree_status_results[0]._message += ( 294fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot '\n\nPlease contact the current Skia sheriff (%s) if you are trying ' 295fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'to submit a build fix\nand do not know how to submit because the ' 296fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'tree is closed') % sheriff_details['username'] 297fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return tree_status_results 298fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 299fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 300fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass CodeReview(object): 301fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """Abstracts which codereview tool is used for the specified issue.""" 302fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 303fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot def __init__(self, input_api): 304fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot self._issue = input_api.change.issue 305fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot self._gerrit = input_api.gerrit 306fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 307fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot def GetOwnerEmail(self): 308fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return self._gerrit.GetChangeOwner(self._issue) 309fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 310fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot def GetSubject(self): 311fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return self._gerrit.GetChangeInfo(self._issue)['subject'] 312fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 313fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot def GetDescription(self): 314fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return self._gerrit.GetChangeDescription(self._issue) 315fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 316fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot def IsDryRun(self): 317fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return self._gerrit.GetChangeInfo( 318fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot self._issue)['labels']['Commit-Queue'].get('value', 0) == 1 319fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 320fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot def GetReviewers(self): 321fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot code_review_label = ( 322fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot self._gerrit.GetChangeInfo(self._issue)['labels']['Code-Review']) 323fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return [r['email'] for r in code_review_label.get('all', [])] 324fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 325fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot def GetApprovers(self): 326fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot approvers = [] 327fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot code_review_label = ( 328fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot self._gerrit.GetChangeInfo(self._issue)['labels']['Code-Review']) 329fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for m in code_review_label.get('all', []): 330fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if m.get("value") == 1: 331fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot approvers.append(m["email"]) 332fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return approvers 333fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 334fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 335fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotdef _CheckOwnerIsInAuthorsFile(input_api, output_api): 336fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results = [] 337fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if input_api.change.issue: 338fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot cr = CodeReview(input_api) 339fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 340fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot owner_email = cr.GetOwnerEmail() 341fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 342fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Service accounts don't need to be in AUTHORS. 343fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if owner_email.endswith(SERVICE_ACCOUNT_SUFFIX): 344fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 345fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 346fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot try: 347fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot authors_content = '' 348fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for line in open(AUTHORS_FILE_NAME): 349fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if not line.startswith('#'): 350fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot authors_content += line 351fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot email_fnmatches = re.findall('<(.*)>', authors_content) 352fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for email_fnmatch in email_fnmatches: 353fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if fnmatch.fnmatch(owner_email, email_fnmatch): 354fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Found a match, the user is in the AUTHORS file break out of the loop 355fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot break 356fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot else: 357fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.append( 358fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot output_api.PresubmitError( 359fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'The email %s is not in Skia\'s AUTHORS file.\n' 360fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'Issue owner, this CL must include an addition to the Skia AUTHORS ' 361fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'file.' 362fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot % owner_email)) 363fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot except IOError: 364fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Do not fail if authors file cannot be found. 365fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot traceback.print_exc() 366fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot input_api.logging.error('AUTHORS file not found!') 367fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 368fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 369fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 370fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 371fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotdef _CheckLGTMsForPublicAPI(input_api, output_api): 372fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """Check LGTMs for public API changes. 373fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 374fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot For public API files make sure there is an LGTM from the list of owners in 375fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot PUBLIC_API_OWNERS. 376fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """ 377fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results = [] 378fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot requires_owner_check = False 379fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for affected_file in input_api.AffectedFiles(): 380fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot affected_file_path = affected_file.LocalPath() 381fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot file_path, file_ext = os.path.splitext(affected_file_path) 382fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # We only care about files that end in .h and are under the top-level 383fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # include dir, but not include/private. 384fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (file_ext == '.h' and 385fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'include' == file_path.split(os.path.sep)[0] and 386fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'private' not in file_path): 387fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot requires_owner_check = True 388fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 389fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if not requires_owner_check: 390fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 391fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 392fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot lgtm_from_owner = False 393fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if input_api.change.issue: 394fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot cr = CodeReview(input_api) 395fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 396fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if re.match(REVERT_CL_SUBJECT_PREFIX, cr.GetSubject(), re.I): 397fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # It is a revert CL, ignore the public api owners check. 398fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 399fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 400fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if cr.IsDryRun(): 401fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Ignore public api owners check for dry run CLs since they are not 402fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # going to be committed. 403fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 404fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 405fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if input_api.gerrit: 406fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for reviewer in cr.GetReviewers(): 407fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if reviewer in PUBLIC_API_OWNERS: 408fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # If an owner is specified as an reviewer in Gerrit then ignore the 409fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # public api owners check. 410fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 411fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot else: 412fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot match = re.search(r'^TBR=(.*)$', cr.GetDescription(), re.M) 413fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if match: 414fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot tbr_section = match.group(1).strip().split(' ')[0] 415fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot tbr_entries = tbr_section.split(',') 416fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for owner in PUBLIC_API_OWNERS: 417fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if owner in tbr_entries or owner.split('@')[0] in tbr_entries: 418fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # If an owner is specified in the TBR= line then ignore the public 419fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # api owners check. 420fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 421fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 422fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if cr.GetOwnerEmail() in PUBLIC_API_OWNERS: 423fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # An owner created the CL that is an automatic LGTM. 424fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot lgtm_from_owner = True 425fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 426fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for approver in cr.GetApprovers(): 427fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if approver in PUBLIC_API_OWNERS: 428fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Found an lgtm in a message from an owner. 429fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot lgtm_from_owner = True 430fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot break 431fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 432fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if not lgtm_from_owner: 433fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.append( 434fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot output_api.PresubmitError( 435fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot "If this CL adds to or changes Skia's public API, you need an LGTM " 436fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot "from any of %s. If this CL only removes from or doesn't change " 437fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot "Skia's public API, please add a short note to the CL saying so. " 438fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot "Add one of the owners as a reviewer to your CL as well as to the " 439fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot "TBR= line. If you don't know if this CL affects Skia's public " 440fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot "API, treat it like it does." % str(PUBLIC_API_OWNERS))) 441fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 442fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 443fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 444fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotdef _FooterExists(footers, key, value): 445fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for k, v in footers: 446fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if k == key and v == value: 447fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return True 448fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return False 449fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 450fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 451fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotdef PostUploadHook(cl, change, output_api): 452fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """git cl upload will call this hook after the issue is created/modified. 453fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 454fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot This hook does the following: 455fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Adds a link to preview docs changes if there are any docs changes in the CL. 456fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Adds 'No-Try: true' if the CL contains only docs changes. 457fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Adds 'No-Tree-Checks: true' for non master branch changes since they do not 458fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot need to be gated on the master branch's tree. 459fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Adds 'No-Try: true' for non master branch changes since trybots do not yet 460fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot work on them. 461fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Adds 'No-Presubmit: true' for non master branch changes since those don't 462fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot run the presubmit checks. 463fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Adds extra trybots for the paths defined in PATH_TO_EXTRA_TRYBOTS. 464fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """ 465fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 466fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results = [] 467fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot atleast_one_docs_change = False 468fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot all_docs_changes = True 469fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for affected_file in change.AffectedFiles(): 470fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot affected_file_path = affected_file.LocalPath() 471fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot file_path, _ = os.path.splitext(affected_file_path) 472fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if 'site' == file_path.split(os.path.sep)[0]: 473fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot atleast_one_docs_change = True 474fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot else: 475fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot all_docs_changes = False 476fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if atleast_one_docs_change and not all_docs_changes: 477fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot break 478fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 479fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot issue = cl.issue 480fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if issue: 481fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Skip PostUploadHooks for all auto-commit bots. New patchsets (caused 482fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # due to PostUploadHooks) invalidates the CQ+2 vote from the 483fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # "--use-commit-queue" flag to "git cl upload". 484fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if cl.GetIssueOwner() in AUTO_COMMIT_BOTS: 485fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 486fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 487fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot original_description_lines, footers = cl.GetDescriptionFooters() 488fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot new_description_lines = list(original_description_lines) 489fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 490fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # If the change includes only doc changes then add No-Try: true in the 491fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # CL's description if it does not exist yet. 492fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if all_docs_changes and not _FooterExists(footers, 'No-Try', 'true'): 493fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot new_description_lines.append('No-Try: true') 494fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.append( 495fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot output_api.PresubmitNotifyResult( 496fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'This change has only doc changes. Automatically added ' 497fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot '\'No-Try: true\' to the CL\'s description')) 498fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 499fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # If there is atleast one docs change then add preview link in the CL's 500fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # description if it does not already exist there. 501fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot docs_preview_link = '%s%s' % (DOCS_PREVIEW_URL, issue) 502fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot docs_preview_line = 'Docs-Preview: %s' % docs_preview_link 503fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (atleast_one_docs_change and 504fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot not _FooterExists(footers, 'Docs-Preview', docs_preview_link)): 505fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Automatically add a link to where the docs can be previewed. 506fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot new_description_lines.append(docs_preview_line) 507fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.append( 508fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot output_api.PresubmitNotifyResult( 509fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'Automatically added a link to preview the docs changes to the ' 510fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'CL\'s description')) 511fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 512fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # If the target ref is not master then add 'No-Tree-Checks: true' and 513fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # 'No-Try: true' to the CL's description if it does not already exist there. 514fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot target_ref = cl.GetRemoteBranch()[1] 515fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if target_ref != 'refs/remotes/origin/master': 516fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if not _FooterExists(footers, 'No-Tree-Checks', 'true'): 517fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot new_description_lines.append('No-Tree-Checks: true') 518fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.append( 519fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot output_api.PresubmitNotifyResult( 520fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'Branch changes do not need to rely on the master branch\'s ' 521fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'tree status. Automatically added \'No-Tree-Checks: true\' to ' 522fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'the CL\'s description')) 523fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if not _FooterExists(footers, 'No-Try', 'true'): 524fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot new_description_lines.append('No-Try: true') 525fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.append( 526fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot output_api.PresubmitNotifyResult( 527fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'Trybots do not yet work for non-master branches. ' 528fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'Automatically added \'No-Try: true\' to the CL\'s ' 529fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'description')) 530fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if not _FooterExists(footers, 'No-Presubmit', 'true'): 531fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot new_description_lines.append('No-Presubmit: true') 532fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.append( 533fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot output_api.PresubmitNotifyResult( 534fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'Branch changes do not run the presubmit checks.')) 535fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 536fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Automatically set Cq-Include-Trybots if any of the changed files here 537fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # begin with the paths of interest. 538fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot bots_to_include = [] 539fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for affected_file in change.AffectedFiles(): 540fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot affected_file_path = affected_file.LocalPath() 541fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot for path_prefix, extra_bots in PATH_PREFIX_TO_EXTRA_TRYBOTS.iteritems(): 542fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if affected_file_path.startswith(path_prefix): 543fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.append( 544fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot output_api.PresubmitNotifyResult( 545fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'Your CL modifies the path %s.\nAutomatically adding %s to ' 546fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 'the CL description.' % (affected_file_path, extra_bots))) 547fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot bots_to_include.append(extra_bots) 548fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if bots_to_include: 549fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot output_api.EnsureCQIncludeTrybotsAreAdded( 550fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot cl, bots_to_include, new_description_lines) 551fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 552fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # If the description has changed update it. 553fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if new_description_lines != original_description_lines: 554fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Add a new line separating the new contents from the old contents. 555fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot new_description_lines.insert(len(original_description_lines), '') 556fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot cl.UpdateDescriptionFooters(new_description_lines, footers) 557fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 558fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 559fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 560fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 561fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotdef CheckChangeOnCommit(input_api, output_api): 562fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """Presubmit checks for the change on commit. 563fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 564fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot The following are the presubmit checks: 565fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Check change has one and only one EOL. 566fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Ensures that the Skia tree is open in 567fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot http://skia-tree-status.appspot.com/. Shows a warning if it is in 'Caution' 568fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot state and an error if it is in 'Closed' state. 569fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot """ 570fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results = [] 571fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.extend(_CommonChecks(input_api, output_api)) 572fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.extend( 573fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot _CheckTreeStatus(input_api, output_api, json_url=( 574fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SKIA_TREE_STATUS_URL + '/banner-status?format=json'))) 575fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.extend(_CheckLGTMsForPublicAPI(input_api, output_api)) 576fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.extend(_CheckOwnerIsInAuthorsFile(input_api, output_api)) 577fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # Checks for the presence of 'DO NOT''SUBMIT' in CL description and in 578fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot # content of files. 579fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot results.extend( 580fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot input_api.canned_checks.CheckDoNotSubmit(input_api, output_api)) 581fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return results 582