Created
June 13, 2026 16:29
-
-
Save ilkka/0d89c1ca7da0833186a52e6b8289db16 to your computer and use it in GitHub Desktop.
Python Nerd Font installer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env -S uv run --script | |
| # /// script | |
| # requires-python = ">=3.14" | |
| # dependencies = ["httpx>=0.28.1"] | |
| # /// | |
| from pathlib import Path | |
| from dataclasses import dataclass | |
| import inspect | |
| import logging | |
| import httpx | |
| import subprocess | |
| # Add fonts here | |
| FONTS = ["IBMPlexMono.tar.xz"] | |
| FONTS_DIR = Path.home() / ".local" / "share" / "fonts" / "nfdl" | |
| BASEURL = "https://api.github.com/repos/ryanoasis/nerd-fonts" | |
| COMMON_HEADERS = { | |
| "Accept": "application/vnd.github+json", | |
| "X-GitHub-API-Version": "2026-03-10", | |
| } | |
| logging.basicConfig(level=logging.INFO) | |
| log = logging.getLogger("nfdl") | |
| @dataclass | |
| class Asset: | |
| name: str | |
| browser_download_url: str | |
| def name_without_suffixes(self) -> str: | |
| return self.name.replace("".join(Path(self.name).suffixes), "") | |
| @classmethod | |
| def from_dict(cls, env): | |
| """Make an instance from a bag of things ignoring anything not actually in the dataclass def.""" | |
| return cls( | |
| **{k: v for k, v in env.items() if k in inspect.signature(cls).parameters} | |
| ) | |
| def get_font_download_urls(client: httpx.Client, fonts: list[str]) -> list[Asset]: | |
| r = client.get(f"{BASEURL}/releases/latest", follow_redirects=True) | |
| picked = [] | |
| assets = r.json()["assets"] | |
| for font in fonts: | |
| for asset in assets: | |
| if asset["name"].startswith(font): | |
| picked.append(Asset.from_dict(asset)) | |
| return picked | |
| def download_font(font: Asset, target_dir: Path, client: httpx.Client) -> None: | |
| fontname = font.name_without_suffixes() | |
| fontdir = target_dir / fontname | |
| if fontdir.exists(): | |
| log.info(f"{fontdir} exists, remove to re-download") | |
| return | |
| log.info(f"Installing {fontname} in {fontdir}") | |
| fontdir.mkdir(parents=True, exist_ok=True) | |
| with subprocess.Popen( | |
| ["tar", "-xJf", "-"], stdin=subprocess.PIPE, cwd=fontdir | |
| ) as tar: | |
| with client.stream( | |
| "GET", font.browser_download_url, follow_redirects=True | |
| ) as stream: | |
| for data in stream.iter_bytes(): | |
| tar.stdin.write(data) | |
| def rebuild_font_cache() -> None: | |
| subprocess.run(["fc-cache"]) | |
| def main() -> None: | |
| FONTS_DIR.mkdir(parents=True, exist_ok=True) | |
| with httpx.Client(headers=COMMON_HEADERS) as client: | |
| urls = get_font_download_urls(client, FONTS) | |
| for font_url in urls: | |
| download_font(font_url, FONTS_DIR, client) | |
| rebuild_font_cache() | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment