# Copyright (c) 2010 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Top-level presubmit script for Chromium. See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for more details about the presubmit API built into gcl. """ _EXCLUDED_PATHS = ( r"breakpad[\\\/].*", r"skia[\\\/].*", r"v8[\\\/].*", ) _TEXT_FILES = ( r".*\.txt", r".*\.json", ) _LICENSE_HEADER = ( r".*? Copyright \(c\) 20[0-9\-]{2,7} The Chromium Authors\. All rights " r"reserved\." "\n" r".*? Use of this source code is governed by a BSD-style license that can " "be\n" r".*? found in the LICENSE file\." "\n" ) def _CommonChecks(input_api, output_api): results = [] # What does this code do? # It loads the default black list (e.g. third_party, experimental, etc) and # add our black list (breakpad, skia and v8 are still not following # google style and are not really living this repository). # See presubmit_support.py InputApi.FilterSourceFile for the (simple) usage. black_list = input_api.DEFAULT_BLACK_LIST + _EXCLUDED_PATHS white_list = input_api.DEFAULT_WHITE_LIST + _TEXT_FILES sources = lambda x: input_api.FilterSourceFile(x, black_list=black_list) text_files = lambda x: input_api.FilterSourceFile(x, black_list=black_list, white_list=white_list) results.extend(input_api.canned_checks.CheckLongLines( input_api, output_api, sources)) results.extend(input_api.canned_checks.CheckChangeHasNoTabs( input_api, output_api, sources)) results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace( input_api, output_api, sources)) results.extend(input_api.canned_checks.CheckChangeHasBugField( input_api, output_api)) results.extend(input_api.canned_checks.CheckChangeHasTestField( input_api, output_api)) results.extend(input_api.canned_checks.CheckChangeSvnEolStyle( input_api, output_api, text_files)) results.extend(input_api.canned_checks.CheckSvnForCommonMimeTypes( input_api, output_api)) results.extend(input_api.canned_checks.CheckLicense( input_api, output_api, _LICENSE_HEADER, sources)) return results def CheckChangeOnUpload(input_api, output_api): results = [] results.extend(_CommonChecks(input_api, output_api)) return results def CheckChangeOnCommit(input_api, output_api): results = [] results.extend(_CommonChecks(input_api, output_api)) # TODO(thestig) temporarily disabled, doesn't work in third_party/ #results.extend(input_api.canned_checks.CheckSvnModifiedDirectories( # input_api, output_api, sources)) # Make sure the tree is 'open'. # TODO(maruel): Run it in a separate thread to parallelize checks? results.extend(CheckTreeIsOpen( input_api, output_api, 'http://chromium-status.appspot.com/status', '0', 'http://chromium-status.appspot.com/current?format=raw')) results.extend(CheckTryJobExecution(input_api, output_api)) # These builders are just too slow. IGNORED_BUILDERS = [ 'Chromium XP', 'Chromium Mac', 'Chromium Arm (dbg)', 'Chromium Linux', 'Chromium Linux x64', ] results.extend(CheckPendingBuilds( input_api, output_api, 'http://build.chromium.org/buildbot/waterfall/json/builders', 6, IGNORED_BUILDERS)) return results def CheckTryJobExecution(input_api, output_api): outputs = [] if not input_api.change.issue or not input_api.change.patchset: return outputs url = "http://codereview.chromium.org/%d/get_build_results/%d" % ( input_api.change.issue, input_api.change.patchset) PLATFORMS = ('win', 'linux', 'mac') try: connection = input_api.urllib2.urlopen(url) # platform|status|url values = [item.split('|', 2) for item in connection.read().splitlines()] connection.close() if not values: # It returned an empty list. Probably a private review. return outputs # Reformat as an dict of platform: [status, url] values = dict([[v[0], [v[1], v[2]]] for v in values]) for platform in PLATFORMS: values.setdefault(platform, ['not started', '']) message = None non_success = [k.upper() for k,v in values.iteritems() if v[0] != 'success'] if 'failure' in [v[0] for v in values.itervalues()]: message = 'Try job failures on %s!\n' % ', '.join(non_success) elif non_success: message = ('Unfinished (or not even started) try jobs on ' '%s.\n') % ', '.join(non_success) if message: message += ( 'Is try server wrong or broken? Please notify maruel@chromium.org. ' 'Thanks.\n') outputs.append(output_api.PresubmitPromptWarning(message=message)) except input_api.urllib2.HTTPError, e: if e.code == 404: # Fallback to no try job. # TODO(maruel): Change to a PresubmitPromptWarning once the try server is # stable enough and it seems to work fine. outputs.append(output_api.PresubmitNotifyResult( 'You should try the patch first.')) else: # Another HTTP error happened, warn the user. # TODO(maruel): Change to a PresubmitPromptWarning once it deemed to work # fine. outputs.append(output_api.PresubmitNotifyResult( 'Got %s while looking for try job status.' % str(e))) return outputs def CheckTreeIsOpen(input_api, output_api, url, closed, url_text): """Similar to the one in presubmit_canned_checks except it shows an helpful status text instead. """ assert(input_api.is_committing) try: connection = input_api.urllib2.urlopen(url) status = connection.read() connection.close() if input_api.re.match(closed, status): long_text = status + '\n' + url try: connection = input_api.urllib2.urlopen(url_text) long_text = connection.read().strip() connection.close() except IOError: pass return [output_api.PresubmitError("The tree is closed.", long_text=long_text)] except IOError: pass return [] def CheckPendingBuilds(input_api, output_api, url, max_pendings, ignored): try: connection = input_api.urllib2.urlopen(url) raw_data = connection.read() connection.close() try: import simplejson data = simplejson.loads(raw_data) except ImportError: # simplejson is much safer. But we should be just fine enough with that: data = eval(raw_data.replace('null', 'None')) out = [] for (builder_name, builder) in data.iteritems(): if builder_name in ignored: continue pending_builds_len = len(builder.get('pending_builds', [])) if pending_builds_len > max_pendings: out.append('%s has %d build(s) pending' % (builder_name, pending_builds_len)) if out: return [output_api.PresubmitPromptWarning( 'Build(s) pending. It is suggested to wait that no more than %d ' 'builds are pending.' % max_pendings, long_text='\n'.join(out))] except IOError: # Silently pass. pass return [] def GetPreferredTrySlaves(): return ['win', 'linux', 'mac']