Hellbox

A modular, editor-agnostic build system for font development

curl https://www.hellbox.dev/install.sh -sSf | sh

Getting started

The install command above installs hell, the CLI for managing Hellbox projects. Once installed, run hell init inside your project directory:

$ hell init

This creates a Hellfile.py where your tasks live, sets up an isolated virtual environment, and installs hellbox itself. If you're joining an existing Hellbox project, run hell install instead to install its pinned dependencies.

How it works

Hellbox tasks are pipelines made of chutes — small, single-purpose modules that pass files from one step to the next using the >> operator. Chutes can be mixed, matched, and shared across projects.

Read
task.read("*.ufo")
››
Transform
GenerateOtf()
››
Write
task.write("./otf")

Example

from hellbox import Hellbox
from hellbox_generate_otf import GenerateOtf

with Hellbox("build") as task:
    task.describe("Builds .otf files from .ufo source")
    task.read("*.ufo") >> GenerateOtf() >> task.write("./otf")

Run with hell run build.

Principles

Consistent
Tasks don't take arguments by design. The same task always produces the same output, regardless of who runs it or when.
Modular
Chutes are reusable and composable. Combine first-party and custom packages to build exactly the workflow your project needs.
Isolated
Dependencies are version-locked per project, isolated from your system Python and from other Hellbox projects.

Writing a chute

Subclass Chute and implement process, which is called once per file and returns the file, a list of files, or None to drop it. Optionally implement flush to operate on the full batch after all files have been processed.

from hellbox import Chute

class FilterByExt(Chute):
    def __init__(self, *extensions):
        self.extensions = extensions

    def process(self, file):
        if file.extension in self.extensions:
            return file
        return None