Bringing Posit’s Air formatter for R to Homebrew

Step by step guide to creating a Homebrew formula for a Rust project

r
homebrew
posit
air
rust
Author

James Balamuta

Published

July 2, 2025

Abstract

This post walks through creating a Homebrew formula for Air, Posit’s new R formatter and language server. We explore why Air represents a significant step forward for R code formatting, discuss the technical considerations of packaging Rust-based tools for Homebrew, and demonstrate the complete process from formula creation to pull request submission.

The R ecosystem has several established formatting tools like {styler} and {formatR} that have served the community well for years. But when Posit in 2024 released Air, a blazingly fast R formatter built in Rust, it brought something new to the table: the performance and reliability of a compiled language combined with opinionated, consistent formatting decisions.

This brings us to a familiar challenge every developer faces: even the most powerful tools can struggle with adoption if installation and maintenance create friction. You discover an amazing tool, but installation involves downloading binaries, managing PATH variables, and remembering update procedures. These small barriers compound, often determining whether transformative tools become part of your daily workflow or remain GitHub bookmarks.

Homebrew and R

Homebrew has become the de facto package manager for macOS developers, providing a unified way to install and manage command-line tools, languages, and libraries. The beauty of Homebrew lies in its simplicity: brew install tool handles download, installation, PATH setup, and dependency management. Updates become brew upgrade, and removal is just brew uninstall. This consistency is invaluable when you’re managing dozens of development tools.

For R developers on Mac, Homebrew offers a familiar interface for managing the broader development toolchain from R itself (brew install --cask r-app) and RStudio (brew install --cask rstudio) to essential system libraries like brew install libssh. Given this existing integration, adding new R tooling like Air to the Homebrew ecosystem feels like a natural evolution.

What is Air?

Air (GitHub) represents a paradigm shift for R code formatting. Built from the ground up in Rust by Davis Vaughan and Lionel Henry at Posit, Air delivers the performance of a compiled language with deep understanding of R’s unique syntax quirks.

Think of it as ruff or black for R code. It automatically formats your R scripts to follow consistent style guidelines, handles complex cases like pipe chains and nested function calls gracefully, and can even serve as a language server for your editor. Most importantly, it’s designed to be fast, reliable, and – begrudgingly – opinionated about code style.

The tool has matured significantly since its early 2024 release. Version 0.7.0, which was released just a month back, continued to deliver formatting enhancements with even a GitHub Action posit-dev/setup-air release. This maturity, combined with the installation friction many potential users face, made it the perfect time to tackle a Homebrew integration.

Battling Adoption Barriers with a Homebrew Formula

So why does Air need a Homebrew formula? Why not just rely on the existing installation methods like downloading binaries with the included shell script installer? The answer lies in the practical challenges of introducing new tooling to development teams.

  1. Installation Complexity: While Air’s shell script installer works perfectly well, it requires manual PATH management and doesn’t integrate with the package management workflows that Mac developers already use. Picture trying to get a team of five R developers to install Air: you’d end up with different installation methods, inconsistent PATH configurations, and inevitably someone asking “wait, how did I install this again?” six months later.

  2. Update Friction: Formatters aren’t set-it-and-forget-it tools. They evolve with language features, community standards, and – in the case of Air – the new features and bug fixes that come with each release. With manual installations, keeping Air current becomes another item on the developer’s mental todo list. How many of us have productivity tools gathering dust simply because updating them requires remembering arcane installation steps?

  3. Psychological Barriers: When installation requires multiple steps and unfamiliar commands, the activation energy for experimentation becomes surprisingly high. Developers might bookmark Air, intend to try it “when they have time,” and never actually get around to it. Making that first experience as simple as brew install air removes the mental friction that prevents great tools from reaching their potential audience.

  4. Team Integration: For teams already using Homebrew to manage their R development environment, having Air available through the same channel means it fits naturally into existing onboarding and deployment processes. It becomes just another line in a Brewfile setup script rather than a special case requiring documentation and support.

The Formula Implementation Journey

With the benefits clear, let’s move onto covering the implementation.

Homebrew formulas are Ruby classes that define how to download, build, and install software. Given the large collection of existing Homebrew formulas, we can often find examples that closely match our needs. However, Air’s Rust workspace structure presented some interesting challenges.

Is Homebrew Set Up?

Before we can create a Homebrew formula, we need to ensure that Homebrew is installed and set up correctly on our machine. If you haven’t installed Homebrew yet, you can do so by running the following command in your terminal:

# Install Homebrew if you haven't already
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Ensure you have the latest Homebrew
brew update

Development Environment Setup

Now we need to fork and set up the Homebrew core repository. Fork homebrew-core on GitHub, then configure your local environment:

# Tap into the Homebrew core repository
brew tap --force homebrew/core
cd "$(brew --repository homebrew/core)"

# Add your fork as a remote
# Replace YOUR_USERNAME with your GitHub username
git remote add YOUR_USERNAME https://github.com/YOUR_USERNAME/homebrew-core.git

# Create a working branch
git checkout -b air-new-formula

This setup enables local testing while preparing for the eventual pull request workflow that Homebrew requires.

Create the Formula

First, we’ll use the brew create command to generate a formula from a template:

brew create --rust https://github.com/posit-dev/air/archive/refs/tags/0.7.0.tar.gz

The --rust flag generates a template optimized for Rust projects, creating a file at Formula/a/air.rb with the basic structure we need.

Other Available Templates

Homebrew supports templates for many build systems:

  • --autotools, --cmake, --meson: For C/C++ projects
  • --go, --rust, --zig: For compiled languages
  • --python, --ruby, --node: For interpreted languages
  • --cabal: For Haskell projects

Run brew create --help to see all options.

Handling Rust Workspaces

The generated template provides a solid starting point, but Air’s workspace structure required customization. Air uses Cargo’s workspace feature, where multiple related packages live in a monorepo:

air/
├── Cargo.toml          # Workspace root
├── crates/
│   ├── air/            # Main binary package
│   ├── air_r_parser/   # Parser library
│   └── other_crates/

The standard Homebrew approach for Rust projects fails with workspaces:

def install
  system "cargo", "install", *std_cargo_args  # This fails!
end

Cargo doesn’t know which specific package to install from the workspace. The solution is to specify the exact package path inside of std_cargo_args:

def install
  system "cargo", "install", *std_cargo_args( "crates/air")
end

This pattern applies to any Rust workspace project you might package for Homebrew.

The Complete Formula

Here’s our final Air formula, incorporating workspace handling and some testing of the formatter functionality:

class Air < Formula
  desc "R formatter and language server"
  homepage "https://github.com/posit-dev/air"
  url "https://github.com/posit-dev/air/archive/refs/tags/0.7.0.tar.gz"
  sha256 "f33fc7aae6829f8471ca3b9144b0a314137393dc5423e10fa313a43278ffc6eb"
  license "MIT"

  depends_on "rust" => 

  def install
    system "cargo", "install", *std_cargo_args( "crates/air")
  end

  test do
    (testpath/"test.R").write <<~R
      # Simple R code for testing
      x<-1+2
      y <- 3 + 4
      print(x+y)
    R

    assert_match "air #{version}", shell_output("#{bin}/air --version")

    system bin/"air", "format", testpath/"test.R"

    formatted_content = (testpath/"test.R").read
    assert_match "x <- 1 + 2", formatted_content
    assert_match "y <- 3 + 4", formatted_content
  end
end

The test block creates a simple R script with intentionally inconsistent formatting, runs Air’s formatter, and verifies the output matches expected patterns. This ensures the formula not only installs correctly but produces working functionality.

Testing and Quality Assurance

With the formula created, we need to ensure it works correctly. Homebrew provides a robust testing framework that allows us to verify our formula behaves as expected.

We can run the full test suite using the following commands:

# Check formula quality and standards compliance
HOMEBREW_NO_INSTALL_FROM_API=1 brew audit --new air

# Test installation from source across architectures  
HOMEBREW_NO_INSTALL_FROM_API=1 brew install --build-from-source ./Formula/a/air.rb

# Verify functionality with our test suite
HOMEBREW_NO_INSTALL_FROM_API=1 brew test air

Each step validates different aspects:

  • audit catches common issues like missing dependencies or incorrect metadata;
  • install --build-from-source ensures compatibility across macOS configurations; and,
  • test verifies actual functionality.

The testing commands above are prefixed with HOMEBREW_NO_INSTALL_FROM_API=1 to ensure we are testing the formulae and casks using local checkouts rather than fetching it from the Homebrew API.

Submit the Formula

With all tests passing locally, we can submit our formula to Homebrew:

# Stage the new formula
git add Formula/a/air.rb

# Commit the changes following Homebrew's conventions
git commit -m "air 0.7.0 (new formula)

R formatter and language server written in Rust"

# Push to your fork
git push YOUR_USERNAME air-new-formula

Creating a pull request triggers Homebrew’s BrewTestBot, which automatically tests the formula across multiple macOS versions and architectures, ensuring broad compatibility before merge.

The Result

Our pull request #228899 is now live! Once merged, R users will be able to install Air with:

brew install air

This makes Air accessible to the broader R community without requiring manual downloads or PATH management. It’s a small contribution that removes friction for adopting better R code formatting practices.

Fin

This Homebrew formula shown is more than just packaging convenience. We’ve created a streamlined installation to reduce friction for R developers, making it easier to adopt Air as part of their development practices. When quality tools are difficult to install or manage, they don’t get used. When they’re a simple brew install away, they become part of the standard toolkit.

Moreover, the formula also establishes a pattern for packaging other tools (looking at you Ark).