Convert from Homebrew Formulae to Casks for font installation

Formulae can't install to ~/Library/Fonts/ due to Homebrew sandboxing.
Casks have a built-in `font` artifact that handles this automatically.

- Replace Formula/ with Casks/ directory
- Rewrite generator to produce cask files instead of formulae
- Add .ttc (TrueType Collection) support
- Update all tests for cask format
- Update CLI and documentation
- Fonts with no installable files (TTF/OTF/TTC) are skipped

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt Troutman 2026-03-07 21:38:59 -06:00
parent cb1918c30f
commit 76743cdc4d
No known key found for this signature in database
273 changed files with 2509 additions and 10048 deletions

View file

@ -1,34 +1,40 @@
"""Global tests: no orphan formulae, no duplicate formula names."""
import pytest
from tests.conftest import FONT_FILES_DIR, FORMULA_DIR, get_font_dir_names
"""Global tests: no orphan casks, no duplicate cask names."""
from tests.conftest import CASKS_DIR, FONT_FILES_DIR, get_font_dir_names
def test_every_formula_has_matching_font_folder():
"""Every Formula/font-*.rb has a matching font_files/font-<name>/ directory."""
formula_files = sorted(FORMULA_DIR.glob("font-*.rb"))
for formula_path in formula_files:
name = formula_path.stem # e.g. font-acrylic-hand
def test_every_cask_has_matching_font_folder():
"""Every Casks/font-*.rb has a matching font_files/font-<name>/ directory."""
cask_files = sorted(CASKS_DIR.glob("font-*.rb"))
for cask_path in cask_files:
name = cask_path.stem # e.g. font-acrylic-hand
font_dir = FONT_FILES_DIR / name
assert font_dir.is_dir(), (
f"Orphan formula: {formula_path.name} has no matching font folder {font_dir}"
f"Orphan cask: {cask_path.name} has no matching font folder {font_dir}"
)
def test_no_duplicate_formula_names():
"""Only one formula file per font-<name> (no duplicate basenames)."""
formula_files = list(FORMULA_DIR.glob("font-*.rb"))
names = [p.stem for p in formula_files]
def test_no_duplicate_cask_names():
"""Only one cask file per font-<name> (no duplicate basenames)."""
cask_files = list(CASKS_DIR.glob("font-*.rb"))
names = [p.stem for p in cask_files]
seen = set()
for n in names:
assert n not in seen, f"Duplicate formula name: {n}.rb"
assert n not in seen, f"Duplicate cask name: {n}.rb"
seen.add(n)
def test_formula_count_matches_font_count():
"""Number of font-*.rb formulae equals number of font-* directories."""
def test_cask_count_matches_font_count():
"""Number of font-*.rb casks equals number of font-* directories with TTF/OTF files."""
font_names = get_font_dir_names()
formula_count = len(list(FORMULA_DIR.glob("font-*.rb")))
assert formula_count == len(font_names), (
f"Formula count ({formula_count}) != font dir count ({len(font_names)})"
# Only count fonts that have TTF or OTF files (casks skip fonts with no installable files)
fonts_with_files = []
for name in font_names:
font_dir = FONT_FILES_DIR / name
has_ttf = any((font_dir / "ttf").glob("*.ttf")) or any((font_dir / "ttf").glob("*.ttc"))
has_otf = any((font_dir / "otf").glob("*.otf"))
if has_ttf or has_otf:
fonts_with_files.append(name)
cask_count = len(list(CASKS_DIR.glob("font-*.rb")))
assert cask_count == len(fonts_with_files), (
f"Cask count ({cask_count}) != font dir count with TTF/OTF ({len(fonts_with_files)})"
)