scan.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  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. import argparse
  6. from collections import defaultdict
  7. import os
  8. import sys
  9. _THIS_DIR = os.path.abspath(os.path.dirname(__file__))
  10. # The repo's root directory.
  11. _ROOT_DIR = os.path.abspath(os.path.join(_THIS_DIR, ".."))
  12. # Add the repo's root directory for clearer imports.
  13. sys.path.insert(0, _ROOT_DIR)
  14. import metadata.discover
  15. import metadata.validate
  16. def parse_args() -> argparse.Namespace:
  17. """Helper to parse args to this script."""
  18. parser = argparse.ArgumentParser()
  19. repo_root_dir = parser.add_argument(
  20. "repo_root_dir",
  21. help=("The path to the repository's root directory, which will be "
  22. "scanned for Chromium metadata files, e.g. '~/chromium/src'."),
  23. )
  24. args = parser.parse_args()
  25. # Check the repo root directory exists.
  26. src_dir = os.path.abspath(args.repo_root_dir)
  27. if not os.path.exists(src_dir) or not os.path.isdir(src_dir):
  28. raise argparse.ArgumentError(
  29. repo_root_dir,
  30. f"Invalid repository root directory '{src_dir}' - not found",
  31. )
  32. return args
  33. def main() -> None:
  34. """Runs validation on all metadata files within the directory
  35. specified by the repo_root_dir arg.
  36. """
  37. config = parse_args()
  38. src_dir = os.path.abspath(config.repo_root_dir)
  39. metadata_files = metadata.discover.find_metadata_files(src_dir)
  40. file_count = len(metadata_files)
  41. print(f"Found {file_count} metadata files.")
  42. invalid_file_count = 0
  43. # Key is constructed from the result severity and reason;
  44. # Value is a dict for:
  45. # * list of files affected by that reason at that severity; and
  46. # * list of validation result strings for that reason and severity.
  47. all_reasons = defaultdict(lambda: {"files": [], "results": set()})
  48. for filepath in metadata_files:
  49. file_results = metadata.validate.validate_file(filepath,
  50. repo_root_dir=src_dir)
  51. invalid = False
  52. if file_results:
  53. relpath = os.path.relpath(filepath, start=src_dir)
  54. print(f"\n{len(file_results)} problem(s) in {relpath}:")
  55. for result in file_results:
  56. print(f" {result}")
  57. summary_key = "{severity} - {reason}".format(
  58. severity=result.get_severity_prefix(),
  59. reason=result.get_reason())
  60. all_reasons[summary_key]["files"].append(relpath)
  61. all_reasons[summary_key]["results"].add(str(result))
  62. if result.is_fatal():
  63. invalid = True
  64. if invalid:
  65. invalid_file_count += 1
  66. print("\n\nDone.")
  67. print("\nSummary of files:")
  68. for summary_key, data in all_reasons.items():
  69. affected_files = data["files"]
  70. count = len(affected_files)
  71. plural = "s" if count > 1 else ""
  72. print(f"\n {count} file{plural}: {summary_key}")
  73. for affected_file in affected_files:
  74. print(f" {affected_file}")
  75. print("\nSummary of results:")
  76. for summary_key, data in all_reasons.items():
  77. results = data["results"]
  78. count = len(results)
  79. plural = "s" if count > 1 else ""
  80. print(f"\n {count} issue{plural}: {summary_key}")
  81. for result in sorted(results):
  82. print(f" {result}")
  83. print(f"\n\n{invalid_file_count} / {file_count} metadata files are "
  84. "invalid, i.e. the file has at least one fatal validation issue.")
  85. if __name__ == "__main__":
  86. main()