# Copyright 2011 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import os PRESUBMIT_VERSION = '2.0.0' def CheckThirdPartyMetadataFiles(input_api, output_api): """Checks that third party metadata files are correctly formatted and valid. """ def readme_filter(f): local_path = f.LocalPath() # Limit to README.chromium files within //third_party/. if (not local_path.endswith('README.chromium') or not local_path.startswith('third_party' + input_api.os_path.sep)): return False # Some folders are currently exempt from being checked. skip_dirs = ( ('third_party', 'blink'), ('third_party', 'boringssl'), ('third_party', 'closure_compiler', 'externs'), ('third_party', 'closure_compiler', 'interfaces'), ('third_party', 'feed_library'), ('third_party', 'ipcz'), ('third_party', 'jni_zero'), # TODO(danakj): We should look for the README.chromium file in # third_party/rust/CRATE_NAME/vVERSION/. ('third_party', 'rust'), ('third_party', 'webxr_test_pages'), ) for path in skip_dirs: prefix = ''.join([dir_name + input_api.os_path.sep for dir_name in path]) if local_path.startswith(prefix): return False return True return input_api.canned_checks.CheckChromiumDependencyMetadata( input_api, output_api, file_filter=readme_filter) def CheckThirdPartyReadmesUpdated(input_api, output_api): """ Checks to make sure that README.chromium files are properly updated when dependencies in third_party are modified. """ readmes = [] files = [] errors = [] for f in input_api.AffectedFiles(): local_path = f.LocalPath() if input_api.os_path.dirname(local_path) == 'third_party': continue # TODO: Autorolled DEPS should be automatically excluded exclusions = [ 'third_party/android_deps/', 'third_party/androidx/', 'third_party/android_build_tools/', 'third_party/blink/', 'third_party/boringssl/', 'third_party/closure_compiler/externs/', 'third_party/closure_compiler/interfaces/', 'third_party/feed_library/', 'third_party/ipcz/', 'third_party/jni_zero/', # TODO(danakj): We should look for the README.chromium file in # third_party/rust/CRATE_NAME/vVERSION/. 'third_party/rust/', 'third_party/webxr_test_pages/', ] exclusions = [e.replace('/', input_api.os_path.sep) for e in exclusions] if (local_path.startswith('third_party' + input_api.os_path.sep) and not any(local_path.startswith(prefix) for prefix in exclusions)): files.append(f) if local_path.endswith("README.chromium"): readmes.append(f) if files and not readmes: errors.append(output_api.PresubmitPromptWarning( 'When updating or adding third party code the appropriate\n' '\'README.chromium\' file should also be updated with the correct\n' 'version and package information.', files)) if not readmes: return errors name_pattern = input_api.re.compile( r'^Name: [a-zA-Z0-9_\-\. \(\)]+\r?$', input_api.re.IGNORECASE | input_api.re.MULTILINE) shortname_pattern = input_api.re.compile( r'^Short Name: [a-zA-Z0-9_\-\.]+\r?$', input_api.re.IGNORECASE | input_api.re.MULTILINE) version_pattern = input_api.re.compile( r'^Version: [a-zA-Z0-9_\-\+\.:/]+\r?$', input_api.re.IGNORECASE | input_api.re.MULTILINE) release_pattern = input_api.re.compile( r'^Security Critical: (yes|no)\r?$', input_api.re.IGNORECASE | input_api.re.MULTILINE) license_pattern = input_api.re.compile( r'^License: (.+)\r?$', input_api.re.IGNORECASE | input_api.re.MULTILINE) # Using NOT_SHIPPED in a License File field is deprecated. # Please use the 'Shipped' field instead. old_shipped_pattern = input_api.re.compile( r'^License File: NOT_SHIPPED\r?$', input_api.re.IGNORECASE | input_api.re.MULTILINE) license_file_pattern = input_api.re.compile( r'^License File: (.+)\r?$', input_api.re.IGNORECASE | input_api.re.MULTILINE) # Matches both 'Shipped' (preferred) and # 'Shipped in Chromium' (for deps that are also standalone projects). shipped_pattern = input_api.re.compile( r'^Shipped(?: in Chromium)?: (yes|no)\r?$', input_api.re.IGNORECASE | input_api.re.MULTILINE) for f in readmes: if 'D' in f.Action(): _IgnoreIfDeleting(input_api, output_api, f, errors) continue # TODO(aredulla): Delete this rudimentary README.chromium checking # once full metadata validation is enforced. The presubmit check # above (CheckThirdPartyMetadataFiles) will return only warnings # until all existing issues have been addressed. To prevent third # party metadata degrading while the data quality is being uplifted, # the below content checking for README.chromium files that return # presubmit errors will be retained. # # Note: This may result in a presubmit error from below being # repeated as a presubmit warning. contents = input_api.ReadFile(f) if (not shortname_pattern.search(contents) and not name_pattern.search(contents)): errors.append(output_api.PresubmitError( 'Third party README files should contain either a \'Short Name\' or\n' 'a \'Name\' which is the name under which the package is\n' 'distributed. Check README.chromium.template for details.', [f])) if not version_pattern.search(contents): errors.append(output_api.PresubmitError( 'Third party README files should contain a \'Version\' field.\n' 'If the package is not versioned, list the version as \'N/A\'\n' 'and provide at least the revision or package update date.\n' 'Check README.chromium.template for details.', [f])) if not release_pattern.search(contents): errors.append(output_api.PresubmitError( 'Third party README files should contain a \'Security Critical\'\n' 'field. This field specifies the security impact of vulnerabilities.\n' 'Check README.chromium.template for details.', [f])) license_match = license_pattern.search(contents) if not license_match: errors.append(output_api.PresubmitError( 'Third party README files should contain a \'License\' field.\n' 'This field specifies the license used by the package. Check\n' 'README.chromium.template for details.', [f])) shipped_match = shipped_pattern.search(contents) # Default to Shipped if not specified. This will flag a license check or # be overwritten if the README is still using the deprecated NOT_SHIPPED # value. is_shipped = (shipped_match is None) or ("yes" in shipped_match.group(1)) # Check for dependencies using the old NOT_SHIPPED pattern and issue a warning deprecated_not_shipped = old_shipped_pattern.search(contents) if deprecated_not_shipped: is_shipped = False license_file_match = license_file_pattern.search(contents) if is_shipped and not license_file_match: errors.append(output_api.PresubmitError( 'Packages marked as shipped must provide a path to a license file.\n' 'Check README.chromium.template for details.', [f])) return errors def _IgnoreIfDeleting(input_api, output_api, affected_file, errors): third_party_dir = input_api.os_path.dirname(affected_file.LocalPath()) + \ os.path.sep for f in input_api.AffectedFiles(): if f.LocalPath().startswith(third_party_dir): if 'D' not in f.Action(): errors.append(output_api.PresubmitError( 'Third party README should only be removed when the whole\n' 'directory is being removed.\n', [f, affected_file]))