@ -5,7 +5,7 @@
import os
import sys
from typing import List, Tuple
from typing import Callable, List, Tuple , Union
_THIS_DIR = os . path . abspath ( os . path . dirname ( __file__ ) )
# The repo's root directory.
@ -14,36 +14,92 @@ _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
def validate_file ( filepath : str ,
repo_root_dir : str ) - > List [ vr . ValidationResult ] :
""" Validate the metadata file. """
if not os . path . exists ( filepath ) :
result = vr . ValidationError ( f " ' { filepath } ' not found. " )
result . set_tag ( tag = " reason " , value = " file not found " )
return [ result ]
def validate_content ( content : str , source_file_dir : str ,
repo_root_dir : str ) - > List [ vr . ValidationResult ] :
""" Validate the content as a metadata file.
# Get the directory the metadata file is in.
parent_dir = os . path . dirname ( filepath )
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 .
Returns : the validation results for the given content .
"""
results = [ ]
dependencies = metadata . parse . parse_file ( filepath )
for dependency in dependencies :
results . extend (
dependency . validate (
source_file_dir = parent_dir ,
repo_root_dir = repo_root_dir ,
) )
dependencies = metadata . parse . parse_content ( content )
if not dependencies :
result = vr . ValidationError ( " No dependency metadata found. " )
result . set_tag ( " reason " , " no metadata " )
return [ result ]
for dependency in dependencies :
dependency_results = dependency . validate ( source_file_dir = source_file_dir ,
repo_root_dir = repo_root_dir )
results . extend ( dependency_results )
return results
def check_file ( filepath : str ,
repo_root_dir : str ) - > Tuple [ List [ str ] , List [ str ] ] :
""" Run metadata validation on the given file, and return all validation
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 ( f " Cannot read ' { filepath } ' - { cause } . " )
result . set_tag ( tag = " reason " , value = " read error " )
return result
def validate_file (
filepath : str ,
repo_root_dir : str ,
reader : Callable [ [ str ] , Union [ str , bytes ] ] = None ,
) - > 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 .
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 )
def check_file (
filepath : str ,
repo_root_dir : str ,
reader : Callable [ [ str ] , Union [ str , bytes ] ] = None ,
) - > Tuple [ List [ str ] , List [ str ] ] :
""" Run metadata validation on the given filepath, and return all validation
errors and validation warnings .
Args :
@ -51,6 +107,7 @@ def check_file(filepath: str,
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 .
Returns :
error_messages : the fatal validation issues present in the file ;
@ -58,20 +115,20 @@ def check_file(filepath: str,
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 )
error_messages = [ ]
warning_messages = [ ]
for result in validate_file ( filepath , repo_root_dir ) :
# Construct the message.
if result . get_tag ( " reason " ) == " file not found " :
message = result . get_message ( postscript = " " , width = 60 )
else :
message = result . get_message ( width = 60 )
for result in results :
message = result . get_message ( width = 60 )
# TODO(aredulla): Actually distinguish between validation errors and
# warnings. The quality of metadata is currently being uplifted, but is not
# yet guaranteed to pass validation. So for now, all validation results will
# be returned as warnings so CLs are not blocked by invalid metadata in
# presubmits yet.
# presubmits yet. Bug: b/285453019.
# if result.is_fatal():
# error_messages.append(message)
# else: