123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- #!/usr/bin/env python3
- # Copyright 2023 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.
- import os
- import sys
- from typing import Callable, List, Tuple, Union
- _THIS_DIR = os.path.abspath(os.path.dirname(__file__))
- # The repo's root directory.
- _ROOT_DIR = os.path.abspath(os.path.join(_THIS_DIR, ".."))
- # Add the repo's root directory for clearer imports.
- sys.path.insert(0, _ROOT_DIR)
- import gclient_utils
- import metadata.parse
- import metadata.validation_result as vr
- _TRANSITION_PRESCRIPT = (
- "The following issue should be addressed now, as it will become a "
- "presubmit error (instead of warning) once third party metadata "
- "validation is enforced.\nThird party metadata issue:")
- def validate_content(content: str,
- source_file_dir: str,
- repo_root_dir: str,
- is_open_source_project: bool = False) -> List[vr.ValidationResult]:
- """Validate the content as a metadata file.
- Args:
- content: the entire content of a file to be validated as a
- metadata file.
- source_file_dir: the directory of the metadata file that the
- license file value is from; this is needed to
- construct file paths to license files.
- repo_root_dir: the repository's root directory; this is needed
- to construct file paths to license files.
- is_open_source_project: whether the project is open source.
- Returns: the validation results for the given content, sorted based
- severity then message.
- """
- results = []
- dependencies = metadata.parse.parse_content(content)
- if not dependencies:
- result = vr.ValidationError(reason="No dependency metadata found.")
- return [result]
- for dependency in dependencies:
- dependency_results = dependency.validate(
- source_file_dir=source_file_dir,
- repo_root_dir=repo_root_dir,
- is_open_source_project=is_open_source_project,
- )
- results.extend(dependency_results)
- return sorted(results)
- def _construct_file_read_error(filepath: str, cause: str) -> vr.ValidationError:
- """Helper function to create a validation error for a
- file reading issue.
- """
- result = vr.ValidationError(
- reason="Cannot read metadata file.",
- additional=[f"Attempted to read '{filepath}' but {cause}."])
- return result
- def validate_file(
- filepath: str,
- repo_root_dir: str,
- reader: Callable[[str], Union[str, bytes]] = None,
- is_open_source_project: bool = False,
- ) -> List[vr.ValidationResult]:
- """Validate the item located at the given filepath is a valid
- dependency metadata file.
- Args:
- filepath: the path to validate, e.g.
- "/chromium/src/third_party/libname/README.chromium"
- repo_root_dir: the repository's root directory; this is needed
- to construct file paths to license files.
- reader (optional): callable function/method to read the content
- of the file.
- is_open_source_project: whether to allow reciprocal licenses.
- This should only be True for open source projects.
- Returns: the validation results for the given filepath and its
- contents, if it exists.
- """
- if reader is None:
- reader = gclient_utils.FileRead
- try:
- content = reader(filepath)
- except FileNotFoundError:
- return [_construct_file_read_error(filepath, "file not found")]
- except PermissionError:
- return [_construct_file_read_error(filepath, "access denied")]
- except Exception as e:
- return [
- _construct_file_read_error(filepath, f"unexpected error: '{e}'")
- ]
- else:
- if not isinstance(content, str):
- return [_construct_file_read_error(filepath, "decoding failed")]
- # Get the directory the metadata file is in.
- source_file_dir = os.path.dirname(filepath)
- return validate_content(content=content,
- source_file_dir=source_file_dir,
- repo_root_dir=repo_root_dir,
- is_open_source_project=is_open_source_project)
- def check_file(
- filepath: str,
- repo_root_dir: str,
- reader: Callable[[str], Union[str, bytes]] = None,
- is_open_source_project: bool = False,
- ) -> Tuple[List[str], List[str]]:
- """Run metadata validation on the given filepath, and return all
- validation errors and validation warnings.
- Args:
- filepath: the path to a metadata file, e.g.
- "/chromium/src/third_party/libname/README.chromium"
- repo_root_dir: the repository's root directory; this is needed
- to construct file paths to license files.
- reader (optional): callable function/method to read the content
- of the file.
- is_open_source_project: whether to allow reciprocal licenses.
- This should only be True for open source projects.
- Returns:
- error_messages: the fatal validation issues present in the file;
- i.e. presubmit should fail.
- warning_messages: the non-fatal validation issues present in the
- file; i.e. presubmit should still pass.
- """
- results = validate_file(filepath=filepath,
- repo_root_dir=repo_root_dir,
- reader=reader,
- is_open_source_project=is_open_source_project)
- error_messages = []
- warning_messages = []
- for result in results:
- target = error_messages if result.is_fatal() else warning_messages
- target.append(result.get_message(width=60))
- return error_messages, warning_messages
|