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.
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.
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?
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.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.
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(path: "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
"R formatter and language server"
desc "https://github.com/posit-dev/air"
homepage "https://github.com/posit-dev/air/archive/refs/tags/0.7.0.tar.gz"
url "f33fc7aae6829f8471ca3b9144b0a314137393dc5423e10fa313a43278ffc6eb"
sha256 "MIT"
license
"rust" => :build
depends_on
def install
system "cargo", "install", *std_cargo_args(path: "crates/air")
end
test do
/"test.R").write <<~R
(testpath# Simple R code for testing
<-1+2
x<- 3 + 4
y print(x+y)
R
"air #{version}", shell_output("#{bin}/air --version")
assert_match
system bin/"air", "format", testpath/"test.R"
= (testpath/"test.R").read
formatted_content "x <- 1 + 2", formatted_content
assert_match "y <- 3 + 4", formatted_content
assert_match 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).