1. Architecture Overview
Fira Code is a monospaced programming font built on a hybrid Clojure/Python/Shell toolchain. The core intellectual property is a Clojure-based ligature generation engine that reads a Glyphs source file (FiraCode.glyphs), extracts ligature glyph definitions, generates OpenType calt (Contextual Alternates) feature code with priority ordering and conflict-avoidance ignore rules, injects character variant and stylistic set features from .fea source files, and writes the enriched Glyphs file back. Shell scripts then drive Python-based font compilation tools (fontmake, gftools, ttfautohint) to produce distributable OTF, TTF, variable TTF, WOFF, and WOFF2 font files.
The system is designed as a build pipeline, not a runtime application. There is no server, no API, and no persistent process — it takes source input (FiraCode.glyphs + .fea files) and produces font files.
Architecture Diagram
FiraCode.glyphs (Glyphs source)
│
▼
┌──────────────────────────┐
│ Clojure Build Engine │
│ (clojure/fira_code/) │
│ │
│ main.clj ─────────────► Parse .glyphs file
│ ├─ calt.clj Generate calt ligature rules
│ ├─ classes.clj Fill glyph classes from classes/*.fea
│ ├─ features.clj Fill features from features/*.fea
│ ├─ spacers.clj Add spacer glyphs for ligatures
│ ├─ not_space.clj Regenerate NotSpace class
│ └─ checks.clj Validate glyph widths
│ │
│ Output: enriched .glyphs file
└──────────────┬───────────┘
│
▼
┌──────────────────────────┐
│ Shell Build Pipeline │
│ (script/) │
│ │
│ build.sh ──────────────► Orchestrate all formats
│ ├─ build_otf.sh fontmake → OTF (6 weights)
│ ├─ build_ttf.sh fontmake → TTF + ttfautohint
│ ├─ build_variable.sh fontmake → Variable TTF
│ ├─ build_woff2.sh woff2_compress → WOFF2
│ └─ build_woff.sh sfnt2woff-zopfli → WOFF
│ │
│ Output: distr/{otf,ttf,variable_ttf,woff,woff2}/
└──────────────────────────┘
Note
The Clojure engine and shell build pipeline are decoupled. You can run
script/update_glyphs.sh(Clojure-only step) independently of the font compilation scripts. This is useful when iterating on ligature rules without recompiling all font formats.
Component Inventory
| Component | Path | Role |
|---|---|---|
| Glyphs source | FiraCode.glyphs |
Master font design file (Glyphs.app format) |
| Clojure engine | clojure/fira_code/ |
Ligature rule generation, feature injection, glyph manipulation |
| OpenType features | features/*.fea |
Character variant (cv) and stylistic set (ss) definitions |
| OpenType classes | classes/*.fea |
Glyph class definitions (brackets, digits, tall chars) |
| Calt sub-features | features/calt/*.fea |
Additional contextual alternate rules (arrows, dashes, underscores, etc.) |
| Build scripts | script/ |
Shell/Python scripts for font compilation and release |
| Distribution | distr/ |
Built font files, CSS, specimen page |
| Docker build | Dockerfile |
Containerized build environment |
| Google Fonts QA | googlefonts-qa/ |
Quality assurance checks for Google Fonts submission |
| Dev environment | clojure/user.clj |
Clojure REPL configuration with hot-reload |
Technology Stack
| Technology | Version | Purpose |
|---|---|---|
| Clojure | (via deps.edn) |
Ligature generation engine |
| Python | 3.x | Font compilation orchestration, release automation |
| fontmake | 3.11.1 | Glyphs → OTF/TTF/Variable compilation |
| glyphsLib | 6.12.7 | Glyphs file format parsing (Python library) |
| gftools | 0.9.993 | Google Fonts tools (metadata fixes, hinting) |
| fontbakery | 1.1.0 | Font quality assurance checks |
| ufo2ft | 3.7.0 | UFO to font compilation |
| uharfbuzz | 0.53.3 | HarfBuzz shaping engine bindings |
| ttfautohint | system | TrueType auto-hinting |
| woff2 | system | WOFF2 compression |
| sfnt2woff-zopfli | system | WOFF compression with Zopfli |
| Docker | optional | Containerized builds |
| npm | via package.json |
Web distribution (package version 6.2.0) |
2. System Requirements
Build Environment (Font Compilation)
| Requirement | Detail |
|---|---|
| Clojure | JDK + Clojure CLI (deps.edn-based project) |
| Python | 3.x with pip |
| System packages (Linux) | pkg-config, zlib1g-dev, ttfautohint, woff2, sfnt2woff-zopfli |
| System packages (macOS) | pkg-config, zlib, ttfautohint, woff2, sfnt2woff-zopfli (via Homebrew) |
| Disk | ~500 MB for build artifacts |
Bootstrap Scripts
Linux:
script/bootstrap_linux.sh
This runs:
sudo apt-get update
sudo apt-get install -y pkg-config zlib1g-dev ttfautohint woff2 sfnt2woff-zopfli
pip install -r requirements.txt
macOS:
script/bootstrap_macos.sh
This runs:
brew install python@3.12
python3.12 -m venv venv
source venv/bin/activate
brew install pkg-config zlib ttfautohint woff2
brew tap bramstein/webfonttools
brew install sfnt2woff-zopfli
pip install -r requirements.txt
Important
The macOS bootstrap creates a Python virtual environment at
venv/. All subsequent build scripts check forvenv/and activate it automatically ([ -d venv ] && source venv/bin/activate).
Python Dependencies
From requirements.txt:
| Package | Version | Purpose |
|---|---|---|
fontmake |
3.11.1 | Font compilation from Glyphs source |
glyphsLib |
6.12.7 | Glyphs file format I/O |
gftools |
0.9.993 | Google Fonts tooling |
fontbakery |
1.1.0 | Font QA validation |
ufo2ft |
3.7.0 | UFO → OpenType compilation |
uharfbuzz |
0.53.3 | HarfBuzz shaping bindings |
3. Configuration Reference
Environment Variables
| Variable | Type | Default | Description | Required? |
|---|---|---|---|---|
FIRACODE_GLYPHS_FILE |
string | FiraCode.glyphs |
Path to the Glyphs source file | No |
FIRACODE_FAMILY_NAME |
string | Fira Code |
Font family name in output | No |
GITHUB_REF |
string | (none) | Git tag reference for version detection (e.g., refs/tags/6.2) |
CI only |
GITHUB_BASIC |
string | (none) | Base64-encoded user:token for GitHub API auth |
Release only |
API_TOKEN |
string | (none) | GitHub API token (alternative to GITHUB_BASIC) |
Release only |
GITHUB_TOKEN |
string | (none) | GitHub token (alternative to API_TOKEN) |
Release only |
GITHUB_REPOSITORY |
string | tonsky/FiraCode |
GitHub repo for release target | Release only |
Important
Release authentication requires exactly one of
GITHUB_BASIC,API_TOKEN, orGITHUB_TOKEN. If none is set, the release script raisesException: Missing API_TOKEN or GITHUB_TOKEN.
Build Script Flags
The main build orchestrator script/build.sh accepts:
| Flag | Alias | Argument | Description |
|---|---|---|---|
-f |
--features |
Comma-separated list | Bake specific features into the font (e.g., cv01,cv02,ss01) |
-w |
--weights |
Comma-separated list | Build only specified weights (default: all 6) |
-n |
--family-name |
String or features |
Custom family name; features appends feature list to name |
-g |
--generate-glyphs-only |
(none) | Generate enriched .glyphs file only, skip font compilation |
Configuration Files
| File | Format | Purpose |
|---|---|---|
FiraCode.glyphs |
Glyphs.app plist-like | Master font source (all glyphs, layers, metrics, features) |
deps.edn |
EDN | Clojure dependency and alias configuration |
package.json |
JSON | npm package metadata (version 6.2.0, SIL OFL-1.1 license) |
requirements.txt |
pip | Python build tool dependencies |
Makefile |
Make | Build automation targets |
Dockerfile |
Docker | Containerized build environment |
OpenType Feature Files
Character Variants (features/cv01.fea – features/cv32.fea)
32 individual character variant feature files. Each defines an OpenType cv feature that substitutes a default glyph with an alternate form. These are independent toggles — users can enable any combination.
Stylistic Sets (features/ss01.fea – features/ss10.fea)
10 stylistic set feature files. Each defines an OpenType ss feature with a descriptive label (stored in the labels metadata with language: dflt). Stylistic sets group related alternate glyphs for batch activation.
Note
Each
.feafile may include a header comment line### Namewhich becomes the feature's display name in font selection UIs. The build engine (features.clj) extracts this header forssfeatures and stores it as a label.
Additional Feature Files
| File | Feature Tag | Description |
|---|---|---|
features/onum.fea |
onum |
Old-style (lowercase) numerals |
features/zero.fea |
zero |
Slashed/dotted zero alternate |
Calt Sub-Features (features/calt/)
| File | Purpose |
|---|---|
center.fea |
Centering rules for specific ligature combinations |
conj_disj.fea |
Conjunction/disjunction operator ligatures |
cross.fea |
Cross-shaped operator ligatures |
dashes.fea |
Dash and hyphen sequence ligatures |
equal_arrows.fea |
Equal-sign arrow ligatures (==>, =>>, etc.) |
fi_fl.fea |
Classic fi/fl typographic ligatures |
greek.fea |
Greek letter-related rules |
hyphen_arrows.fea |
Hyphen-based arrow ligatures (-->, ->>, etc.) |
match_case.fea |
Case-matching rules |
numbersigns.fea |
Hash/numbersign sequence handling |
underscores.fea |
Underscore sequence ligatures |
Glyph Classes (classes/)
| File | Class Name | Contents |
|---|---|---|
ClosingBracket.fea |
ClosingBracket |
Closing bracket glyphs |
Digit.fea |
Digit |
Decimal digit glyphs (0–9) |
DigitTosf.fea |
DigitTosf |
Tabular/oldstyle figure digit variants |
HexDigit.fea |
HexDigit |
Hexadecimal digit glyphs (0–9, a–f, A–F) |
OpeningBracket.fea |
OpeningBracket |
Opening bracket glyphs |
Tall.fea |
Tall |
Tall character glyphs (for vertical metrics) |
4. Deployment & Scaling
Standard Build
# 1. Bootstrap environment (one-time)
script/bootstrap_linux.sh # or bootstrap_macos.sh
# 2. Update Glyphs file with generated features
script/update_glyphs.sh
# 3. Build all formats
cd script && ./build.sh
Build output structure:
distr/
├── otf/Fira Code/
│ ├── FiraCode-Light.otf
│ ├── FiraCode-Regular.otf
│ ├── FiraCode-Retina.otf
│ ├── FiraCode-Medium.otf
│ ├── FiraCode-SemiBold.otf
│ └── FiraCode-Bold.otf
├── ttf/Fira Code/
│ └── (same 6 weights as .ttf)
├── variable_ttf/Fira Code/
│ └── FiraCode-VF.ttf
├── woff/Fira Code/
│ └── (5 weights, no Retina)
├── woff2/Fira Code/
│ └── (5 weights, no Retina)
├── fira_code.css
├── specimen.html
└── README.txt
Note
The Retina weight is deliberately excluded from WOFF and WOFF2 output — it is removed after the conversion step in
build_woff.shandbuild_woff2.sh.
Docker Build
script/build_docker.sh
This builds a Docker image from the repo's Dockerfile and runs the build pipeline inside a container with the repo mounted at /opt/FiraCode. All output goes to the distr/ directory on the host.
Custom Feature Builds
Build a variant with specific features baked into the calt table:
cd script
./build.sh -f cv01,cv02,ss03 -n "Fira Code Custom" -w Regular,Bold
This:
- Copies
FiraCode.glyphsto a temp file - Runs
bake_in_features.shto inject the specified feature code into thecaltfeature - Optionally renames the font family
- Compiles only the specified weights
Tip
Use
-g(generate glyphs only) for quick iteration when developing new features — it skips the slow font compilation step and outputs only the enriched.glyphsfile.
Release Process
The release script (script/release.py) handles:
- Packaging
distr/into a versioned ZIP (Fira_Code_v{version}.zip) - Creating a GitHub release with the ZIP attached
- Placeholder hooks for npm, Homebrew, Scoop, and Google Fonts publishing (currently skipped)
python3 script/release.py --version 6.2
Warning
The release script requires network access and valid GitHub authentication. The
npm_publish,update_homebrew,update_scoop, andupdate_google_fontsfunctions are currently stubs that print "Skip" — manual publishing is required for those channels.
Version Management
Update the font version in the Glyphs source:
python3 script/update_version.py --version 6.3
This modifies versionMajor and versionMinor fields in FiraCode.glyphs.
5. Operations
Build Pipeline Scripts
| Script | Purpose | Dependencies |
|---|---|---|
script/build.sh |
Master orchestrator — compiles all formats | All build tools |
script/build_otf.sh |
Compile OTF static fonts | fontmake |
script/build_ttf.sh |
Compile TTF static fonts + auto-hint | fontmake, ttfautohint |
script/build_variable.sh |
Compile variable TTF | fontmake, gftools |
script/build_woff.sh |
Convert TTF → WOFF | sfnt2woff-zopfli |
script/build_woff2.sh |
Convert TTF → WOFF2 | woff2_compress |
script/bake_in_features.sh |
Inject feature code into calt in .glyphs file |
sed, grep |
script/update_glyphs.sh |
Run Clojure engine to regenerate features | Clojure |
script/update_version.py |
Update font version numbers | Python |
script/release.py |
Package and publish release | Python, GitHub API |
script/package.sh |
Create distribution ZIP | zip |
script/clean.sh |
Remove all built artifacts from distr/ |
(none) |
script/bootstrap_linux.sh |
Install build deps on Linux | apt, pip |
script/bootstrap_macos.sh |
Install build deps on macOS | Homebrew, pip |
script/repl.sh |
Start Clojure REPL for development | Clojure |
Tip
The Clojure REPL (
script/repl.sh) starts a dev server with hot-reload viaclj-reload. Theclojure/user.cljnamespace configures pretty-printing and error formatting automatically. Call(reload)in the REPL to pick up changes without restarting.
Monitoring Build Health
The Clojure engine emits diagnostic messages during processing:
Parsing 'FiraCode.glyphs'...
generated calt: XX pairs, XX triples, XX quadruples, XX total
replacing feature ss01 with XX lines
appending to feature calt XX lines
regenerated NotSpace: XXXX glyphs
added glyphs: X.spacer Y.spacer ...
WARN glyph 'X' has unexpected width=YYY
Saving 'FiraCode.glyphs'...
Note
Width warnings (
WARN glyph ... has unexpected width) indicate glyphs that are not 0-width (non-exporting) or 1200 units wide (standard monospace width). These are informational — the font will still build, but mismatched widths can cause alignment issues in monospace contexts.
Variable Font Build Details
The variable font build (build_variable.sh) includes special handling:
- Creates a temp copy of the Glyphs file
- Disables the Retina master (
exports = 0) viaawkinjection — Retina is excluded because it is not on the interpolation axis - Runs
fontmakewith-o variableflag - Applies post-processing fixes:
gftools fix-nonhinting— fixes non-hinting metadatagftools fix-gasp— fixes grid-fitting and scan-conversion procedure table
Clean Build
script/clean.sh
Removes distr/ttf, distr/variable_ttf, distr/woff2, and distr/woff directories. OTF output is not cleaned (it lives in distr/otf).
6. Performance
Build Times
Font compilation is CPU-bound. The heaviest steps are:
- fontmake compilation — compiling each weight from Glyphs source to OTF/TTF. Each weight takes 10–30 seconds depending on hardware.
- ttfautohint — auto-hinting each TTF weight adds 5–15 seconds per weight.
- Variable font compilation — single pass but produces a larger, more complex file.
- WOFF/WOFF2 compression — relatively fast (seconds per file).
Total build time for all formats: approximately 3–10 minutes depending on hardware.
Optimization: Partial Builds
Use --weights to compile only the weights you need during development:
./build.sh -w Regular
Use --generate-glyphs-only to skip font compilation entirely when iterating on ligature rules:
./build.sh -g
7. Security
Supply Chain
- All Python dependencies are pinned to exact versions in
requirements.txt - The
googlefonts-qa/scripts/requirements.txtuses unpinned versions and a git dependency (git+https://github.com/googlefonts/gftools.git) — this is only for QA, not production builds
Warning
The main
requirements.txtpins exact versions, butgooglefonts-qa/scripts/requirements.txtdoes not. If running Google Fonts QA checks, be aware that unpinned dependencies may introduce breaking changes.
Release Authentication
The release script supports three authentication methods:
GITHUB_BASIC— Base64-encodedusername:token(Basic auth)API_TOKEN— GitHub personal access tokenGITHUB_TOKEN— GitHub Actions automatic token
All are read from environment variables. No credentials are stored in the repository.
Caution
The
GITHUB_BASICmethod transmits credentials in Base64 (not encrypted). PreferGITHUB_TOKEN(short-lived, scoped) when running in GitHub Actions, orAPI_TOKENwith minimal scopes for manual releases.
License
SIL Open Font License 1.1 — permits use, modification, and redistribution with attribution. The license is in the LICENSE file at the repo root.
8. Developer Internals
Repository Structure
tonsky/FiraCode/
├── FiraCode.glyphs Master font source file
├── Dockerfile Docker build environment
├── Makefile Build automation
├── deps.edn Clojure dependencies
├── package.json npm package (v6.2.0)
├── requirements.txt Python build dependencies
├── LICENSE SIL OFL-1.1
├── README.md Project readme (+ CN, JA, ES, PT translations)
├── CHANGELOG.md Version history
├── AUTHORS Contributors
│
├── clojure/
│ ├── user.clj Dev REPL config (hot-reload, pretty-print)
│ └── fira_code/
│ ├── main.clj Entry point — orchestrates full pipeline
│ ├── calt.clj Ligature rule generation (priorities, ignores, conflict avoidance)
│ ├── checks.clj Glyph width validation
│ ├── classes.clj Glyph class injection from .fea files
│ ├── coll.clj Collection utility functions
│ ├── features.clj OpenType feature injection from .fea files
│ ├── files.clj File finding utilities
│ ├── glyphs.clj Glyphs file parser/serializer (custom recursive descent)
│ ├── not_space.clj NotSpace class regeneration
│ ├── spacers.clj Spacer glyph generation for ligatures
│ └── time.clj UTC timestamp formatting
│
├── classes/ Glyph class definitions (.fea)
├── features/ Character variants + stylistic sets (.fea)
│ └── calt/ Additional contextual alternate rules
├── script/ Build and release scripts
├── distr/ Built font distribution
├── extras/ Marketing images, logo, specimens
├── docs/ Performance documentation
└── googlefonts-qa/ Google Fonts submission QA
Module Reference
clojure/fira_code/main.clj — Entry Point
The -main function orchestrates the full Clojure pipeline:
- Load and parse
FiraCode.glyphsviaglyphs/load - Extract all ligature definitions (glyphs ending in
.ligathat are exported) - Split ligature names on
_to get glyph sequences (e.g.,dash_greater_greater.liga→["dash" "greater" "greater"]) - Apply transformations in order:
calt/replace-calt— generate and injectcaltfeature codeclasses/fill-all— load class definitions fromclasses/*.feafeatures/fill-all— load feature definitions fromfeatures/*.fea+ append calt sub-featuresspacers/add-spacers— create spacer glyphs for ligature renderingnot-space/regen-not-space— rebuild the NotSpace glyph classchecks/widths— validate all glyph widths
- Save the enriched
.glyphsfile
clojure/fira_code/glyphs.clj — Glyphs File Parser
A complete recursive-descent parser for the Glyphs.app file format (a plist-like syntax with { key = value; } maps and ( item, item ) lists). Key details:
- Parsing: Character-by-character state machine using mutable atoms (
*str,*pos) - Data model: Uses
flatland.ordered.mapto preserve key insertion order (critical for reproducible output) - Serialization: Round-trip capable —
parse→ modify →serializeproduces valid Glyphs files - Escape handling: Supports
\"and\\in quoted strings - Type inference: Integers (
-?[1-9][0-9]*), floats (-?[0-9]+\.[0-9]+), keywords ([a-zA-Z][a-zA-Z.0-9]*), and bare strings - Special cases:
.appVersionis always quoted (per glyphsLib compatibility);stemValuesuses a specific list format
clojure/fira_code/calt.clj — Ligature Rule Engine
The core ligature generation module. Key concepts:
Priority system: Ligatures are sorted by priority before rule generation. Lower priority numbers are processed first:
- Priority 0:
<|>(pipe operators with angle brackets) - Priority 1: Single/multi-bar-angle combinations (
|>,<|,||>,<||,|||>,<|||) - Priority 2: Repeated operators (
<<,>>,||,<<<,>>>,|||) - Default (MAX_VALUE): All other ligatures, sorted by length (longer first), then alphabetically
Ignore rules: Prevent false ligatures in specific contexts. The ignores map defines context-sensitive exclusions keyed by ligature glyph sequence. Examples:
/* */sequences prevent internal false ligatures::prevents ligation before<or>(issue #1061)[== ==]bracket-equality patterns suppressed (issues #948, #968)=:=,=!=prevented from double-ligating (issue #346)<||>handled as a special four-glyph case (issue #621){|},[|]bracket-pipe suppressed (issue #593)//=suppressed (issue #816)
Ignore prefixes: A general mechanism (gen-ignore-prefixes) that prevents ligatures from triggering when preceded by specific character sequences. This handles:
- Regex lookahead:
(?:,(?=,(?<=,(?!,(?<!(issues #578, #624) - PHP embed:
<?=(issue #850)
Rule generation (liga->rule): Generates OpenType lookup blocks for 2, 3, 4, and 5-glyph ligatures using a spacer-based substitution chain. The algorithm:
- Replace rightmost glyph with the ligature glyph
- Replace intermediate glyphs right-to-left with spacer glyphs
- Replace leftmost glyph with its spacer
Skip-ignores and manual sets: Some ligatures skip ignore generation (skip-ignores? set: <*>, <+>, <$> combinations per issue #410) or are marked manual and excluded from auto-generation (/\, \/).
clojure/fira_code/features.clj — Feature Injection
Two-phase feature loading:
append-calt: Reads all.feafiles fromfeatures/calt/, trims whitespace, and appends to the existingcaltfeature codefill-features: Reads each.feafile fromfeatures/, parses the header comment for a display name (### Name), creates a feature entry with tag and optional label, then inserts or replaces it in the font
For ss (stylistic set) features, the header name is stored as a labels entry with language: dflt — this becomes the user-visible feature name in font selection UIs.
clojure/fira_code/spacers.clj — Spacer Glyph Management
Ligatures require spacer glyphs to maintain monospace alignment. For each unique glyph name in any ligature, a corresponding .spacer glyph is needed. The spacer is a zero-content glyph with:
- Width: 1200 (standard monospace cell width)
- Two layers: Light and Bold (keyed by UUID)
- Color: 3 (visual marker in Glyphs.app)
The module diffs existing spacers against needed spacers and only adds missing ones.
clojure/fira_code/checks.clj — Width Validation
Validates that all exported glyphs have consistent widths across layers. Acceptable widths are:
"0"or0— zero-width (combining marks, etc.)1200— standard monospace width
Warnings are emitted for:
- Glyphs with different widths across layers (Light/Regular/Bold inconsistency)
- Glyphs with unexpected widths (not 0 or 1200)
clojure/fira_code/glyphs.clj — Weight Constants
Three master weights are defined by UUID:
| Weight | UUID |
|---|---|
| Light | B67F0F2D-EC95-4CB8-966E-23AE86958A69 |
| Regular | UUID0 |
| Bold | 4B7A3BAF-EAD8-4024-9BEA-BB1DE86CFCFA |
These UUIDs are used to identify layers in the Glyphs source file.
script/build.sh — Build Orchestrator
The main build script:
- Parses CLI flags (
-f,-w,-n,-g) - Copies
FiraCode.glyphsto a temp file (avoids mutating the original) - If features specified: runs
bake_in_features.shto inject them - If custom family name: rewrites
familyNamein the temp file viaawk - Saves the custom
.glyphsfile as{Family Name}.glyphs - Unless
-gflag: runs all format-specific build scripts sequentially - Cleans up temp files
script/bake_in_features.sh — Feature Injection
Injects feature code directly into the calt section of the .glyphs file via text manipulation:
- For each specified feature name, reads the corresponding
.feafile fromfeatures/ - Strips lookup wrappers, comments, and blank lines
- Locates the
caltfeature declaration line in the.glyphsfile - Injects the extracted code before the closing delimiter via
sed
script/common.py — Version Helper
Resolves version from two sources (in order):
--versionCLI argumentGITHUB_REFenvironment variable (stripsrefs/tags/prefix)
Raises Exception: Can't identify version if neither source provides a version.
script/release.py — Release Pipeline
package(version)— createsFira_Code_v{version}.zipfromdistr/contentsgithub_release(version)— creates a GitHub release and uploads the ZIPnpm_publish,update_homebrew,update_scoop,update_google_fonts— all currently stubs ("Skip")
The @log_errors decorator wraps each publish step so failures in one channel do not block others.
Code Patterns and Conventions
Clojure conventions:
- Namespaces match directory structure (
fira-code.calt→clojure/fira_code/calt.clj) - Entry points use
-mainconvention - Ordered maps (
flatland.ordered.map) used throughout for deterministic output - String-heavy OpenType feature code generation via
str/joinandstr/replacewith regex digit substitution - Functional pipeline style:
(-> font transform1 transform2 ...)threading macro
Shell conventions:
- All scripts use
set -o errexit -o nounset -o pipefail(strict mode) - All scripts
cdto repo root viacd "$(dirname "$0")/.." - Virtual environment auto-activation:
[ -d venv ] && source venv/bin/activate - Environment variables with defaults:
${FIRACODE_FAMILY_NAME:-"Fira Code"}
Python conventions:
- Minimal dependencies — uses only stdlib (
argparse,os,re,subprocess,urllib,zipfile,json,glob) common.pyshared module for version resolution@log_errorsdecorator pattern for non-critical release steps
Google Fonts QA
The googlefonts-qa/ directory contains:
METADATA.pb— Google Fonts metadata in Protocol Buffer text formatgfonts-description.html— HTML description for Google Fonts listingchecks/— fontbakery check results (.checks.mdfiles) for static and variable fontsnotes/— QA notes with visual comparison screenshotsscripts/— QA-specific build and check scripts (separaterequirements.txt)
Testing
The Clojure engine includes a width-checking module (checks.clj) that validates all exported glyphs at build time. There is no formal test suite — the build pipeline itself serves as an integration test (compilation failures indicate regressions).
The script/check and script/check_widths scripts provide additional validation:
check— runs general font validationcheck_widths— verifies monospace width consistency
Potential Improvements
Based on source analysis:
- Variable font hinting: The
build_variable.shscript contains a TODO noting that TTFautohint-VF support is needed for better variable font hinting - npm/Homebrew/Scoop publish automation: The release script has stub functions for these channels — automating them would streamline releases
- Release script modernization: Uses
urllib.requestfor GitHub API; migrating torequestsorghCLI would simplify auth handling - CI/CD integration: Version detection relies on
GITHUB_REFenvironment variable, suggesting GitHub Actions usage, but no workflow files are present in the repo - OTF not cleaned by
clean.sh: The clean script removes TTF, variable TTF, WOFF, and WOFF2 but leaves OTF — likely intentional but undocumented - fontbakery QA checks are dated: The check markdown files in
googlefonts-qa/checks/appear to be point-in-time snapshots, not part of an automated pipeline
Contributing
The project uses a Clojure development workflow:
- Start the REPL:
script/repl.sh - Edit Clojure source files
- Call
(reload)in the REPL to pick up changes (viaclj-reload) - Run the pipeline:
(fira-code.main/-main)orscript/update_glyphs.sh - Build fonts:
cd script && ./build.sh
The clojure/user.clj dev namespace installs:
hashp— debug printing (#preader tag)print— pretty-printed outputerror— enhanced error formatting
9. Integration Points
npm Distribution
The package.json defines:
- Package name:
firacode - Version:
6.2.0 - Main entry:
distr/fira_code.css - Files included:
distr/directory only - License:
OFL-1.1
After npm install firacode, consumers access font files at node_modules/firacode/distr/.
Google Fonts
Fira Code is distributed via Google Fonts. The googlefonts-qa/ directory contains all assets for the submission:
METADATA.pbdefines the font family metadatagfonts-description.htmlprovides the HTML description- QA checks validate compliance with Google Fonts standards
Homebrew / Scoop / Chocolatey
Distribution through OS-level package managers is handled externally:
- Homebrew:
homebrew-cask-fontsrepository - Scoop:
scoop-nerd-fontsbucket - Chocolatey: community-maintained package
The release script has placeholder hooks for updating these, but they currently print "Skip" and require manual intervention.