Skip to content

Start Here

Release Channels

Wheels ships on two parallel channels. Pick the one that matches how much churn you can absorb.

StableBleeding-edge
Package namewheelswheels-be
TracksGA tags on mainSnapshot pre-releases on develop
Update cadenceWhen a new GA ships (~quarterly)On every merge to develop (multiple per day during active development)
Source repowheels-dev/wheelswheels-dev/wheels-snapshots
Best forProduction apps, teams that want predictabilityFramework contributors, early adopters, validating fixes before they GA
Version string shape4.0.0, 4.1.04.0.1-snapshot.1700
wheels version reports(stable)(bleeding-edge)
your shell
wheels version

The output’s parenthetical tells you the channel:

Wheels 4.0.3 (stable)
Java 21.0.11

…or:

Wheels 4.0.0-snapshot.1787 (bleeding-edge)
Java 21.0.11

Note the command is wheels version (no dashes). The flag form wheels --version varies by install method — Homebrew stable and manual JAR installs print a Wheels Version: <version> banner plus ASCII art with no channel, while the apt/yum and Homebrew bleeding-edge package wrappers print a one-line wheels <version> (<channel>). wheels version is the form that reports the channel reliably everywhere.

If you see (development) instead, you’re running from a dev checkout (cloned source repo) rather than an installed package — which is its own thing and not covered here.

your shell
brew tap wheels-dev/wheels
# Stable
brew install wheels
# Bleeding-edge
brew install wheels-be

Homebrew 5.1+ asks you to trust third-party taps on first use — run brew trust wheels-dev/wheels once if prompted (one trust covers both formulas).

The two formulas are mutually exclusive — both expose the wheels binary, so Homebrew refuses to install both at once. To switch channels, uninstall one and install the other (see Switching channels below).

On macOS and Windows, the channels publish two differently-named packages (wheels vs wheels-be) that both install a binary called wheels, so the package managers refuse to install both at once — Homebrew enforces this via conflicts_with, Scoop refuses outright.

On Linux, the two channels likewise publish differently-named packages: wheels (stable, from wheels-dev/wheels) and wheels-be (bleeding-edge, from wheels-dev/wheels-snapshots). Switching = installing the package from the other repo. The wheels-be .deb declares Replaces: wheels + Conflicts: wheels, so on Debian/Ubuntu apt handles the stable → bleeding-edge swap automatically. The wheels-be .rpm declares only Conflicts: (no Obsoletes:), and the stable wheels package doesn’t declare either against wheels-be, so the dnf direction and the reverse (bleeding-edge → stable) both need an explicit remove step. Snippets below cover each case.

macOS — stable → bleeding-edge
brew uninstall wheels
brew install wheels-be
macOS — bleeding-edge → stable
brew uninstall wheels-be
brew install wheels
Linux — stable → bleeding-edge (Debian / Ubuntu)
# Add the bleeding-edge source (uses the same key already imported for stable).
# wheels-be .deb declares Replaces: wheels + Conflicts: wheels, so apt removes
# the stable wheels package and installs wheels-be in its place — no separate
# `apt remove` needed.
echo "deb [signed-by=/usr/share/keyrings/wheels.gpg] https://apt.wheels.dev bleeding-edge main" \
| sudo tee /etc/apt/sources.list.d/wheels-be.list
sudo apt update && sudo apt install wheels-be
Linux — stable → bleeding-edge (Fedora / RHEL)
# wheels-be .rpm declares Conflicts: wheels but no Obsoletes, so dnf
# refuses to install while the stable wheels package is present. Remove
# it first, then add the bleeding-edge .repo source and install.
sudo dnf remove wheels
sudo dnf config-manager --add-repo https://yum.wheels.dev/wheels-be.repo
sudo dnf install wheels-be
Linux — bleeding-edge → stable (Debian / Ubuntu)
# wheels-be declares Conflicts: wheels, so apt refuses to install wheels
# while wheels-be is present. Remove wheels-be, then install wheels —
# both come from the same apt.wheels.dev source list, so no extra setup
# is needed.
sudo apt remove wheels-be
sudo apt install wheels
Linux — bleeding-edge → stable (Fedora / RHEL)
# Same as Debian above — wheels-be declares Conflicts: wheels, so dnf
# refuses to install wheels while wheels-be is present. Remove wheels-be
# first, then install wheels. Both come from yum.wheels.dev (stable
# lives at wheels.repo, bleeding-edge at wheels-be.repo); the stable
# .repo file is left in place from the original install.
sudo dnf remove wheels-be
sudo dnf install wheels
Windows (Scoop) — stable → bleeding-edge
scoop uninstall wheels
scoop install wheels-be
Windows (Scoop) — bleeding-edge → stable
scoop uninstall wheels-be
scoop install wheels

Already-scaffolded apps are unaffected. Each Wheels app commits its own copy of the framework into vendor/wheels/ at scaffold time, so the version baked into your project is independent of what channel your CLI is on. Switching wheelswheels-be only affects what next wheels new produces.

If you want an existing app to follow the channel switch:

inside the app
wheels upgrade check --to=<version> # scan for breaking changes first
wheels upgrade apply # swap vendor/wheels/ from the CLI bundle (creates backup)

…or re-scaffold the app’s vendor/wheels/ from a fresh wheels new.

Pick stable (wheels) when:

  • You’re shipping a production app
  • You want predictable upgrade windows (one major release per ~quarter, see Upgrading Wheels)
  • You don’t want to think about CI breaking from upstream churn
  • You’re following the tutorial — it’s pinned to stable behavior

Pick bleeding-edge (wheels-be) when:

  • You’re contributing to Wheels and want to dogfood your changes
  • You’re validating that an upcoming fix lands correctly before GA
  • You’re early-adopting a feature that’s merged but not yet released
  • You’re OK with the occasional regression (we revert quickly, but the window exists)

If you’re not sure, install stable. You can always switch later — your apps come with you.

This section is for the curious. If you just want to use Wheels, you can skip it.

A snapshot is an automated pre-release built and published on every merge to the develop branch. The version string 4.0.0-snapshot.1787 decodes as:

  • 4.0.0 — the target of the next release (a baseline, not a commitment — the actual GA could end up being 4.0.0, 4.0.1, 4.1.0, or whatever the maintainer picks at tag time)
  • -snapshot — SemVer pre-release identifier (sorts strictly less than 4.0.0)
  • .1787github.run_number of the workflow that built it (monotonically increasing)

So brew upgrade wheels-be always lands on a numerically-newer snapshot, and any GA release sorts strictly higher than every snapshot that preceded it.

Snapshots publish to wheels-dev/wheels-snapshots instead of the main repo’s Releases page. The split keeps the main repo’s Releases page focused on GA tags (~6–12 per year) instead of drowning them under snapshot churn (~200–500 per year). Snapshots auto-delete after 30 days; GA releases stay forever.

When a snapshot publishes:

  1. The release workflow on wheels-dev/wheels dispatches a wheels-released event to the brew tap with channel=bleeding-edge in the payload.
  2. The tap’s bleeding-edge-update.yml workflow fires, computes fresh sha256s, opens an auto-bump PR for Formula/wheels-be.rb.
  3. Tap CI validates the formula (brew audit + brew fetch), maintainer rubberstamps, the PR auto-merges.
  4. brew upgrade wheels-be lands the new version on user machines.

End-to-end latency: ~5–10 minutes from develop merge to user-installable.

For stable releases, the same chain runs against Formula/wheels.rb and the auto-update.yml workflow.