blob: 27ea1ba50d7bcd28b2cb11009052b72d241da8bc [file] [log] [blame]
# 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
exclusions = [
'third_party/android_deps/',
'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]))