# Fira Code — Administrator & Developer Guide

## 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

```text
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:**
```bash
script/bootstrap_linux.sh
```

This runs:
```bash
sudo apt-get update
sudo apt-get install -y pkg-config zlib1g-dev ttfautohint woff2 sfnt2woff-zopfli
pip install -r requirements.txt
```

**macOS:**
```bash
script/bootstrap_macos.sh
```

This runs:
```bash
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 for `venv/` 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`, or `GITHUB_TOKEN`. If none is set, the release script raises `Exception: 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 `.fea` file may include a header comment line `### Name` which becomes the feature's display name in font selection UIs. The build engine (`features.clj`) extracts this header for `ss` features 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

```bash
# 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:

```text
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.sh` and `build_woff2.sh`.

### Docker Build

```bash
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:

```bash
cd script
./build.sh -f cv01,cv02,ss03 -n "Fira Code Custom" -w Regular,Bold
```

This:
1. Copies `FiraCode.glyphs` to a temp file
2. Runs `bake_in_features.sh` to inject the specified feature code into the `calt` feature
3. Optionally renames the font family
4. 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 `.glyphs` file.

### Release Process

The release script (`script/release.py`) handles:

1. Packaging `distr/` into a versioned ZIP (`Fira_Code_v{version}.zip`)
2. Creating a GitHub release with the ZIP attached
3. Placeholder hooks for npm, Homebrew, Scoop, and Google Fonts publishing (currently skipped)

```bash
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`, and `update_google_fonts` functions are currently stubs that print "Skip" — manual publishing is required for those channels.

### Version Management

Update the font version in the Glyphs source:

```bash
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 via `clj-reload`. The `clojure/user.clj` namespace 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:

```text
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:

1. Creates a temp copy of the Glyphs file
2. Disables the Retina master (`exports = 0`) via `awk` injection — Retina is excluded because it is not on the interpolation axis
3. Runs `fontmake` with `-o variable` flag
4. Applies post-processing fixes:
   - `gftools fix-nonhinting` — fixes non-hinting metadata
   - `gftools fix-gasp` — fixes grid-fitting and scan-conversion procedure table

### Clean Build

```bash
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:

1. **fontmake compilation** — compiling each weight from Glyphs source to OTF/TTF. Each weight takes 10–30 seconds depending on hardware.
2. **ttfautohint** — auto-hinting each TTF weight adds 5–15 seconds per weight.
3. **Variable font compilation** — single pass but produces a larger, more complex file.
4. **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:

```bash
./build.sh -w Regular
```

Use `--generate-glyphs-only` to skip font compilation entirely when iterating on ligature rules:

```bash
./build.sh -g
```

## 7. Security

### Supply Chain

- All Python dependencies are pinned to exact versions in `requirements.txt`
- The `googlefonts-qa/scripts/requirements.txt` uses 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.txt` pins exact versions, but `googlefonts-qa/scripts/requirements.txt` does 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:

1. `GITHUB_BASIC` — Base64-encoded `username:token` (Basic auth)
2. `API_TOKEN` — GitHub personal access token
3. `GITHUB_TOKEN` — GitHub Actions automatic token

All are read from environment variables. No credentials are stored in the repository.

> [!CAUTION]
> The `GITHUB_BASIC` method transmits credentials in Base64 (not encrypted). Prefer `GITHUB_TOKEN` (short-lived, scoped) when running in GitHub Actions, or `API_TOKEN` with 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

```text
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:

1. Load and parse `FiraCode.glyphs` via `glyphs/load`
2. Extract all ligature definitions (glyphs ending in `.liga` that are exported)
3. Split ligature names on `_` to get glyph sequences (e.g., `dash_greater_greater.liga` → `["dash" "greater" "greater"]`)
4. Apply transformations in order:
   - `calt/replace-calt` — generate and inject `calt` feature code
   - `classes/fill-all` — load class definitions from `classes/*.fea`
   - `features/fill-all` — load feature definitions from `features/*.fea` + append calt sub-features
   - `spacers/add-spacers` — create spacer glyphs for ligature rendering
   - `not-space/regen-not-space` — rebuild the NotSpace glyph class
   - `checks/widths` — validate all glyph widths
5. Save the enriched `.glyphs` file

#### `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.map` to preserve key insertion order (critical for reproducible output)
- **Serialization:** Round-trip capable — `parse` → modify → `serialize` produces 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:** `.appVersion` is always quoted (per glyphsLib compatibility); `stemValues` uses 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:
1. Replace rightmost glyph with the ligature glyph
2. Replace intermediate glyphs right-to-left with spacer glyphs
3. 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:

1. **`append-calt`**: Reads all `.fea` files from `features/calt/`, trims whitespace, and appends to the existing `calt` feature code
2. **`fill-features`**: Reads each `.fea` file from `features/`, 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"` or `0` — 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:

1. Parses CLI flags (`-f`, `-w`, `-n`, `-g`)
2. Copies `FiraCode.glyphs` to a temp file (avoids mutating the original)
3. If features specified: runs `bake_in_features.sh` to inject them
4. If custom family name: rewrites `familyName` in the temp file via `awk`
5. Saves the custom `.glyphs` file as `{Family Name}.glyphs`
6. Unless `-g` flag: runs all format-specific build scripts sequentially
7. 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:

1. For each specified feature name, reads the corresponding `.fea` file from `features/`
2. Strips lookup wrappers, comments, and blank lines
3. Locates the `calt` feature declaration line in the `.glyphs` file
4. Injects the extracted code before the closing delimiter via `sed`

#### `script/common.py` — Version Helper

Resolves version from two sources (in order):
1. `--version` CLI argument
2. `GITHUB_REF` environment variable (strips `refs/tags/` prefix)

Raises `Exception: Can't identify version` if neither source provides a version.

#### `script/release.py` — Release Pipeline

1. `package(version)` — creates `Fira_Code_v{version}.zip` from `distr/` contents
2. `github_release(version)` — creates a GitHub release and uploads the ZIP
3. `npm_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 `-main` convention
- Ordered maps (`flatland.ordered.map`) used throughout for deterministic output
- String-heavy OpenType feature code generation via `str/join` and `str/replace` with 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 `cd` to repo root via `cd "$(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.py` shared module for version resolution
- `@log_errors` decorator pattern for non-critical release steps

### Google Fonts QA

The `googlefonts-qa/` directory contains:

- `METADATA.pb` — Google Fonts metadata in Protocol Buffer text format
- `gfonts-description.html` — HTML description for Google Fonts listing
- `checks/` — fontbakery check results (`.checks.md` files) for static and variable fonts
- `notes/` — QA notes with visual comparison screenshots
- `scripts/` — QA-specific build and check scripts (separate `requirements.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 validation
- `check_widths` — verifies monospace width consistency

### Potential Improvements

Based on source analysis:

1. **Variable font hinting**: The `build_variable.sh` script contains a TODO noting that TTFautohint-VF support is needed for better variable font hinting
2. **npm/Homebrew/Scoop publish automation**: The release script has stub functions for these channels — automating them would streamline releases
3. **Release script modernization**: Uses `urllib.request` for GitHub API; migrating to `requests` or `gh` CLI would simplify auth handling
4. **CI/CD integration**: Version detection relies on `GITHUB_REF` environment variable, suggesting GitHub Actions usage, but no workflow files are present in the repo
5. **OTF not cleaned by `clean.sh`**: The clean script removes TTF, variable TTF, WOFF, and WOFF2 but leaves OTF — likely intentional but undocumented
6. **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:

1. Start the REPL: `script/repl.sh`
2. Edit Clojure source files
3. Call `(reload)` in the REPL to pick up changes (via `clj-reload`)
4. Run the pipeline: `(fira-code.main/-main)` or `script/update_glyphs.sh`
5. Build fonts: `cd script && ./build.sh`

The `clojure/user.clj` dev namespace installs:
- `hashp` — debug printing (`#p` reader tag)
- `print` — pretty-printed output
- `error` — 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.pb` defines the font family metadata
- `gfonts-description.html` provides 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-fonts` repository
- Scoop: `scoop-nerd-fonts` bucket
- Chocolatey: community-maintained package

The release script has placeholder hooks for updating these, but they currently print "Skip" and require manual intervention.

---

_Generated May 2026 from tonsky/FiraCode at d0cf393. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
