Anne Redulla | c7aca34 | 2023-08-25 01:09:08 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # Copyright 2023 The Chromium Authors. All rights reserved. |
| 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
| 6 | import os |
| 7 | import sys |
Anne Redulla | b9d7c85 | 2023-08-29 23:54:27 +0000 | [diff] [blame] | 8 | from typing import Callable, List, Tuple, Union |
Anne Redulla | c7aca34 | 2023-08-25 01:09:08 +0000 | [diff] [blame] | 9 | |
| 10 | _THIS_DIR = os.path.abspath(os.path.dirname(__file__)) |
| 11 | # The repo's root directory. |
| 12 | _ROOT_DIR = os.path.abspath(os.path.join(_THIS_DIR, "..")) |
| 13 | |
| 14 | # Add the repo's root directory for clearer imports. |
| 15 | sys.path.insert(0, _ROOT_DIR) |
| 16 | |
Anne Redulla | b9d7c85 | 2023-08-29 23:54:27 +0000 | [diff] [blame] | 17 | import gclient_utils |
Anne Redulla | c7aca34 | 2023-08-25 01:09:08 +0000 | [diff] [blame] | 18 | import metadata.parse |
| 19 | import metadata.validation_result as vr |
| 20 | |
| 21 | |
Anne Redulla | ed9a081 | 2023-09-20 03:08:25 +0000 | [diff] [blame] | 22 | _TRANSITION_PRESCRIPT = ( |
| 23 | "The following issue should be addressed now, as it will become a " |
| 24 | "presubmit error (instead of warning) once third party metadata " |
| 25 | "validation is enforced.\nThird party metadata issue:") |
| 26 | |
| 27 | |
Anne Redulla | b9d7c85 | 2023-08-29 23:54:27 +0000 | [diff] [blame] | 28 | def validate_content(content: str, source_file_dir: str, |
| 29 | repo_root_dir: str) -> List[vr.ValidationResult]: |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 30 | """Validate the content as a metadata file. |
Anne Redulla | b9d7c85 | 2023-08-29 23:54:27 +0000 | [diff] [blame] | 31 | |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 32 | Args: |
| 33 | content: the entire content of a file to be validated as a |
| 34 | metadata file. |
| 35 | source_file_dir: the directory of the metadata file that the |
| 36 | license file value is from; this is needed to |
| 37 | construct file paths to license files. |
| 38 | repo_root_dir: the repository's root directory; this is needed |
| 39 | to construct file paths to license files. |
Anne Redulla | b9d7c85 | 2023-08-29 23:54:27 +0000 | [diff] [blame] | 40 | |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 41 | Returns: the validation results for the given content. |
| 42 | """ |
| 43 | results = [] |
| 44 | dependencies = metadata.parse.parse_content(content) |
| 45 | if not dependencies: |
| 46 | result = vr.ValidationError(reason="No dependency metadata found.") |
| 47 | return [result] |
Anne Redulla | c7aca34 | 2023-08-25 01:09:08 +0000 | [diff] [blame] | 48 | |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 49 | for dependency in dependencies: |
| 50 | dependency_results = dependency.validate( |
| 51 | source_file_dir=source_file_dir, repo_root_dir=repo_root_dir) |
| 52 | results.extend(dependency_results) |
| 53 | return results |
Anne Redulla | c7aca34 | 2023-08-25 01:09:08 +0000 | [diff] [blame] | 54 | |
| 55 | |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 56 | def _construct_file_read_error(filepath: str, cause: str) -> vr.ValidationError: |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 57 | """Helper function to create a validation error for a |
| 58 | file reading issue. |
| 59 | """ |
| 60 | result = vr.ValidationError( |
| 61 | reason="Cannot read metadata file.", |
| 62 | additional=[f"Attempted to read '{filepath}' but {cause}."]) |
| 63 | return result |
Anne Redulla | b9d7c85 | 2023-08-29 23:54:27 +0000 | [diff] [blame] | 64 | |
| 65 | |
| 66 | def validate_file( |
| 67 | filepath: str, |
| 68 | repo_root_dir: str, |
| 69 | reader: Callable[[str], Union[str, bytes]] = None, |
| 70 | ) -> List[vr.ValidationResult]: |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 71 | """Validate the item located at the given filepath is a valid |
| 72 | dependency metadata file. |
Anne Redulla | b9d7c85 | 2023-08-29 23:54:27 +0000 | [diff] [blame] | 73 | |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 74 | Args: |
| 75 | filepath: the path to validate, e.g. |
| 76 | "/chromium/src/third_party/libname/README.chromium" |
| 77 | repo_root_dir: the repository's root directory; this is needed |
| 78 | to construct file paths to license files. |
| 79 | reader (optional): callable function/method to read the content |
| 80 | of the file. |
Anne Redulla | b9d7c85 | 2023-08-29 23:54:27 +0000 | [diff] [blame] | 81 | |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 82 | Returns: the validation results for the given filepath and its |
| 83 | contents, if it exists. |
| 84 | """ |
| 85 | if reader is None: |
| 86 | reader = gclient_utils.FileRead |
Anne Redulla | b9d7c85 | 2023-08-29 23:54:27 +0000 | [diff] [blame] | 87 | |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 88 | try: |
| 89 | content = reader(filepath) |
| 90 | except FileNotFoundError: |
| 91 | return [_construct_file_read_error(filepath, "file not found")] |
| 92 | except PermissionError: |
| 93 | return [_construct_file_read_error(filepath, "access denied")] |
| 94 | except Exception as e: |
| 95 | return [ |
| 96 | _construct_file_read_error(filepath, f"unexpected error: '{e}'") |
| 97 | ] |
| 98 | else: |
| 99 | if not isinstance(content, str): |
| 100 | return [_construct_file_read_error(filepath, "decoding failed")] |
Anne Redulla | b9d7c85 | 2023-08-29 23:54:27 +0000 | [diff] [blame] | 101 | |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 102 | # Get the directory the metadata file is in. |
| 103 | source_file_dir = os.path.dirname(filepath) |
| 104 | return validate_content(content=content, |
| 105 | source_file_dir=source_file_dir, |
| 106 | repo_root_dir=repo_root_dir) |
Anne Redulla | b9d7c85 | 2023-08-29 23:54:27 +0000 | [diff] [blame] | 107 | |
| 108 | |
| 109 | def check_file( |
| 110 | filepath: str, |
| 111 | repo_root_dir: str, |
| 112 | reader: Callable[[str], Union[str, bytes]] = None, |
| 113 | ) -> Tuple[List[str], List[str]]: |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 114 | """Run metadata validation on the given filepath, and return all |
| 115 | validation errors and validation warnings. |
Anne Redulla | c7aca34 | 2023-08-25 01:09:08 +0000 | [diff] [blame] | 116 | |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 117 | Args: |
| 118 | filepath: the path to a metadata file, e.g. |
| 119 | "/chromium/src/third_party/libname/README.chromium" |
| 120 | repo_root_dir: the repository's root directory; this is needed |
| 121 | to construct file paths to license files. |
| 122 | reader (optional): callable function/method to read the content |
| 123 | of the file. |
Anne Redulla | c7aca34 | 2023-08-25 01:09:08 +0000 | [diff] [blame] | 124 | |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 125 | Returns: |
| 126 | error_messages: the fatal validation issues present in the file; |
| 127 | i.e. presubmit should fail. |
| 128 | warning_messages: the non-fatal validation issues present in the |
| 129 | file; i.e. presubmit should still pass. |
| 130 | """ |
| 131 | results = validate_file(filepath=filepath, |
| 132 | repo_root_dir=repo_root_dir, |
| 133 | reader=reader) |
Anne Redulla | b9d7c85 | 2023-08-29 23:54:27 +0000 | [diff] [blame] | 134 | |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 135 | error_messages = [] |
| 136 | warning_messages = [] |
| 137 | for result in results: |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 138 | # TODO(aredulla): Actually distinguish between validation errors |
| 139 | # and warnings. The quality of metadata is currently being |
| 140 | # uplifted, but is not yet guaranteed to pass validation. So for |
| 141 | # now, all validation results will be returned as warnings so |
| 142 | # CLs are not blocked by invalid metadata in presubmits yet. |
| 143 | # Bug: b/285453019. |
Anne Redulla | ed9a081 | 2023-09-20 03:08:25 +0000 | [diff] [blame] | 144 | if result.is_fatal(): |
| 145 | message = result.get_message(prescript=_TRANSITION_PRESCRIPT, |
| 146 | width=60) |
| 147 | else: |
| 148 | message = result.get_message(width=60) |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 149 | warning_messages.append(message) |
Anne Redulla | c7aca34 | 2023-08-25 01:09:08 +0000 | [diff] [blame] | 150 | |
Anne Redulla | 6715758 | 2023-09-04 22:02:36 +0000 | [diff] [blame] | 151 | return error_messages, warning_messages |