run.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. #!/usr/bin/python3
  2. import subprocess
  3. import re
  4. import os
  5. import argparse
  6. from collections import defaultdict
  7. from enum import Enum
  8. from glob import glob
  9. class CheckType(Enum):
  10. NEW_FAMILY = 1
  11. MODIFIED_FAMILY = 2
  12. MODIFIED_FAMILY_METADATA = 3
  13. DESIGNER = 4
  14. class CheckToken(Enum):
  15. NEW_FONT = 1
  16. DESIGNER = 2
  17. FONT_FAMILY = 3
  18. MODIFIED_FONTS = 4
  19. MODIFIED_METADATA = 5
  20. def _parse_git_diff(diff_info: str) -> list[tuple[str, str]]:
  21. """
  22. '''
  23. A ofl/mavenpro/MavenPro[wght].ttf
  24. M ofl/mavenpro/METADATA.pb
  25. ''' -> [
  26. ("A", "ofl/mavenpro/MavenPro[wght].ttf"),
  27. ("M", "ofl/mavenpro/METADATA.pb")
  28. ]
  29. """
  30. parsed = re.findall(r"([A|M|D])(\t)(.*)", diff_info)
  31. return [(s, p) for s, _, p in parsed]
  32. def directory_check_types(branch="origin/main"):
  33. git_diff_text = subprocess.run(
  34. ["git", "diff", branch, "--name-status"], capture_output=True
  35. ).stdout.decode("utf-8")
  36. git_diff = _parse_git_diff(git_diff_text)
  37. # Tokenize each directory git diff has reported
  38. directories_to_check = defaultdict(set)
  39. for state, path in git_diff:
  40. dirpath = (
  41. os.path.dirname(path)
  42. if path not in ("to_sandbox.txt", "to_production.txt")
  43. else path
  44. )
  45. dir_tokens = directories_to_check[dirpath]
  46. if path.startswith("catalog"):
  47. dir_tokens.add(CheckToken.DESIGNER)
  48. if path.startswith(("ofl", "ufl", "apache")):
  49. dir_tokens.add(CheckToken.FONT_FAMILY)
  50. if path.endswith((".ttf", ".otf")) and state == "A":
  51. dir_tokens.add(CheckToken.NEW_FONT)
  52. if path.endswith((".ttf", ".otf")) and (state == "M" or state == "D"):
  53. dir_tokens.add(CheckToken.MODIFIED_FONTS)
  54. if path.endswith((".txt", ".pb", ".html")) and state == "M":
  55. dir_tokens.add(CheckToken.MODIFIED_METADATA)
  56. # Set each directory's check type
  57. results = []
  58. for path, tokens in directories_to_check.items():
  59. if CheckToken.FONT_FAMILY in tokens:
  60. if CheckToken.MODIFIED_FONTS in tokens:
  61. results.append((path, CheckType.MODIFIED_FAMILY))
  62. elif CheckToken.MODIFIED_METADATA in tokens:
  63. results.append((path, CheckType.MODIFIED_FAMILY_METADATA))
  64. else:
  65. results.append((path, CheckType.NEW_FAMILY))
  66. if CheckToken.DESIGNER in tokens:
  67. results.append((path, CheckType.DESIGNER))
  68. return results
  69. def main():
  70. parser = argparse.ArgumentParser()
  71. parser.add_argument(
  72. "--branch", default="origin/main", help="branch to compare current head against"
  73. )
  74. parser.add_argument(
  75. "--render", action="store_true", help="Check rendering of families only"
  76. )
  77. parser.add_argument("--pr-number", help="PR to output fontbakery report to")
  78. parser.add_argument("--pr-url-body", default="https://www.github.com/google/fonts/pull/%s")
  79. args = parser.parse_args()
  80. profile_test_file = os.path.join(os.path.dirname(__file__), "test_profiles.py")
  81. for directory, check_type in directory_check_types(args.branch):
  82. out = os.path.join("out", os.path.basename(directory))
  83. fonts = glob(os.path.join(directory, "*.ttf"))
  84. qa_cmd_prefix = ["gftools", "qa", "-f"] + fonts + ["-o", out]
  85. if args.pr_number:
  86. if not args.pr_url_body.endswith("/"):
  87. args.pr_url_body += "/"
  88. url = "%s%s" % (args.pr_url_body, args.pr_number)
  89. qa_cmd_prefix += ["--out-url", url]
  90. if args.render and check_type == CheckType.NEW_FAMILY:
  91. print(f"Rendering new family: {directory}")
  92. subprocess.run(qa_cmd_prefix + ["-gfb", "--render", "--imgs"])
  93. elif args.render and check_type == CheckType.MODIFIED_FAMILY:
  94. print(f"Rendering modified family: {directory}")
  95. subprocess.run(qa_cmd_prefix + ["-gfb", "--render", "--imgs"])
  96. # we only want args.render to do the above two conditions
  97. elif args.render:
  98. continue
  99. elif check_type == CheckType.NEW_FAMILY:
  100. print(f"Checking new family: {directory}")
  101. subprocess.run(qa_cmd_prefix + ["--fontbakery", "--interpolations"])
  102. elif check_type == CheckType.MODIFIED_FAMILY:
  103. print(f"Checking modified family: {directory}")
  104. subprocess.run(qa_cmd_prefix + ["-gfb", "--fontbakery", "--diffenator", "--interpolations"])
  105. elif check_type == CheckType.MODIFIED_FAMILY_METADATA:
  106. print(f"Checking modified family metadata: {directory}")
  107. subprocess.run(qa_cmd_prefix + ["--fontbakery", "-o", out])
  108. elif check_type == CheckType.DESIGNER:
  109. print(f"Checking designer profile: {directory}")
  110. subprocess.run(["pytest", profile_test_file, directory])
  111. else:
  112. print(f"Skipping directory {directory}")
  113. if __name__ == "__main__":
  114. main()