--- /dev/null
+#!/usr/bin/python3
+import subprocess
+import re
+import os
+import argparse
+from collections import defaultdict
+from enum import Enum
+from glob import glob
+
+
+class CheckType(Enum):
+ NEW_FAMILY = 1
+ MODIFIED_FAMILY = 2
+ MODIFIED_FAMILY_METADATA = 3
+ DESIGNER = 4
+
+
+class CheckToken(Enum):
+ NEW_FONT = 1
+ DESIGNER = 2
+ FONT_FAMILY = 3
+ MODIFIED_FONTS = 4
+ MODIFIED_METADATA = 5
+
+
+def _parse_git_diff(diff_info: str) -> list[tuple[str, str]]:
+ """
+ '''
+ A ofl/mavenpro/MavenPro[wght].ttf
+ M ofl/mavenpro/METADATA.pb
+ ''' -> [
+ ("A", "ofl/mavenpro/MavenPro[wght].ttf"),
+ ("M", "ofl/mavenpro/METADATA.pb")
+ ]
+
+ """
+ parsed = re.findall(r"([A|M|D])(\t)(.*)", diff_info)
+ return [(s, p) for s, _, p in parsed]
+
+
+def directory_check_types(branch="origin/main"):
+ git_diff_text = subprocess.run(
+ ["git", "diff", branch, "--name-status"], capture_output=True
+ ).stdout.decode("utf-8")
+ git_diff = _parse_git_diff(git_diff_text)
+
+ # Tokenize each directory git diff has reported
+ directories_to_check = defaultdict(set)
+ for state, path in git_diff:
+ dirpath = (
+ os.path.dirname(path)
+ if path not in ("to_sandbox.txt", "to_production.txt")
+ else path
+ )
+
+ dir_tokens = directories_to_check[dirpath]
+ if path.startswith("catalog"):
+ dir_tokens.add(CheckToken.DESIGNER)
+
+ if path.startswith(("ofl", "ufl", "apache")):
+ dir_tokens.add(CheckToken.FONT_FAMILY)
+
+ if path.endswith((".ttf", ".otf")) and state == "A":
+ dir_tokens.add(CheckToken.NEW_FONT)
+
+ if path.endswith((".ttf", ".otf")) and state == "M":
+ dir_tokens.add(CheckToken.MODIFIED_FONTS)
+
+ if path.endswith((".txt", ".pb", ".html")) and state == "M":
+ dir_tokens.add(CheckToken.MODIFIED_METADATA)
+
+ # Set each directory's check type
+ results = []
+ for path, tokens in directories_to_check.items():
+ if CheckToken.FONT_FAMILY in tokens:
+ if CheckToken.MODIFIED_FONTS in tokens:
+ results.append((path, CheckType.MODIFIED_FAMILY))
+ elif CheckToken.MODIFIED_METADATA in tokens:
+ results.append((path, CheckType.MODIFIED_FAMILY_METADATA))
+ else:
+ results.append((path, CheckType.NEW_FAMILY))
+ if CheckToken.DESIGNER in tokens:
+ results.append((path, CheckType.DESIGNER))
+ return results
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--branch", default="origin/main", help="branch to compare current head against"
+ )
+ parser.add_argument(
+ "--render", action="store_true", help="Check rendering of families only"
+ )
+ parser.add_argument("--pr-number", help="PR to output fontbakery report to")
+ parser.add_argument("--pr-url-body", default="https://www.github.com/google/fonts/pull/%s")
+ args = parser.parse_args()
+
+ profile_test_file = os.path.join(os.path.dirname(__file__), "test_profiles.py")
+
+ for directory, check_type in directory_check_types(args.branch):
+ out = os.path.join("out", os.path.basename(directory))
+ fonts = glob(os.path.join(directory, "*.ttf"))
+
+ qa_cmd_prefix = ["gftools", "qa", "-f"] + fonts + ["-o", out]
+ if args.pr_number:
+ if not args.pr_url_body.endswith("/"):
+ args.pr_url_body += "/"
+ url = "%s%s" % (args.pr_url_body, args.pr_number)
+ qa_cmd_prefix += ["--out-url", url]
+
+ if args.render and check_type == CheckType.NEW_FAMILY:
+ print(f"Rendering new family: {directory}")
+ subprocess.run(qa_cmd_prefix + ["--render", "--imgs"])
+
+ elif args.render and check_type == CheckType.MODIFIED_FAMILY:
+ print(f"Rendering modified family: {directory}")
+ subprocess.run(qa_cmd_prefix + ["-gfb", "--render", "--imgs"])
+
+ # we only want args.render to do the above two conditions
+ elif args.render:
+ continue
+
+ elif check_type == CheckType.NEW_FAMILY:
+ print(f"Checking new family: {directory}")
+ subprocess.run(qa_cmd_prefix + ["--fontbakery"])
+
+ elif check_type == CheckType.MODIFIED_FAMILY:
+ print(f"Checking modified family: {directory}")
+ subprocess.run(qa_cmd_prefix + ["-gfb", "--fontbakery", "--diffenator"])
+
+ elif check_type == CheckType.MODIFIED_FAMILY_METADATA:
+ print(f"Checking modified family metadata: {directory}")
+ subprocess.run(qa_cmd_prefix + ["--fontbakery", "-o", out])
+
+ elif check_type == CheckType.DESIGNER:
+ print(f"Checking designer profile: {directory}")
+ subprocess.run(["pytest", profile_test_file, directory])
+
+ else:
+ print(f"Skipping directory {directory}")
+
+
+if __name__ == "__main__":
+ main()
+++ /dev/null
-# Only run gftools qa on prs which have modified files in directories which
-# contain font binaries.
-
-# Find directories which contain files that have been altered or added. Also
-# Skip /static, axisregistry, cc-by-sa and lang directories.
-CHANGED_DIRS=$(git diff origin/main --dirstat=files --diff-filter d | sed "s/[0-9. ].*%//g" | grep -v "static\|axisregistry\|cc-by-sa\|lang")
-OUT=out
-
-PR_URL="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/pull/$PR_NUMBER"
-echo "PR url: $PR_URL"
-
-for dir in $CHANGED_DIRS
-do
- font_count=$(ls -1 $dir*.ttf 2>/dev/null | wc -l)
- is_designer_dir=$(echo $dir | grep "designers")
- if [ $font_count != 0 ]
- then
- echo "Checking $dir"
- mkdir -p $OUT
- # If pr contains modified fonts, check with Fontbakery, Diffenator and DiffBrowsers.
- # If pr doesn't contain modified fonts, just check with Fontbakery.
- modified_fonts=$(git diff --name-only origin/main HEAD $dir*.ttf)
- if [ -n "$modified_fonts" ]
- then
- echo "Fonts have been modified. Checking fonts with all tools"
- if [ "$SCREENSHOTS" = true ]; then
- gftools qa -f $dir*.ttf -gfb --render --imgs -o $OUT/$(basename $dir)_qa
- else
- gftools qa -f $dir*.ttf -gfb --diffenator --fontbakery -o $OUT/$(basename $dir)_qa --out-url $PR_URL
- fi
- else
- echo "Fonts have not been modified. Checking fonts with Fontbakery only"
- gftools qa -f $dir*.ttf --fontbakery -o $OUT/$(basename $dir)_qa --out-url $PR_URL
- fi
- elif [ ! -z $is_designer_dir ]
- then
- echo "Checking designer profile"
- pytest .ci/test_profiles.py $dir
- else
- echo "Skipping $dir. Directory does not contain fonts"
- fi
-done
-