Scarlet.Bun.Cli
1.4.3
dotnet tool install --global Scarlet.Bun.Cli --version 1.4.3
dotnet new tool-manifest
dotnet tool install --local Scarlet.Bun.Cli --version 1.4.3
#tool dotnet:?package=Scarlet.Bun.Cli&version=1.4.3
nuke :add-package Scarlet.Bun.Cli --version 1.4.3
Scarlet.Bun.Cli
Run Bun — the fast all-in-one JavaScript runtime, bundler and package manager — as a .NET tool.
dotnet bun install
dotnet bun run build.mjs
dotnet bun --version # prints Bun's version, not the tool's
Every argument is forwarded to Bun verbatim, so anything valid after bun is valid after dotnet bun.
Why not just install Bun?
You still can — nothing here stops you, and for a solo project with no CI this buys you little. It earns its keep once a repository has more than one contributor or a CI pipeline, because it makes Bun pinned and installed the same way as the rest of your .NET tooling, instead of a separate step with its own installer:
- Pinned like everything else. The Bun version lives in
.config/dotnet-tools.jsonnext todotnet-ef,dotnet-format, and friends.dotnet tool restorebrings it down with the repository — no "works on my machine because I have Bun 1.5 and CI has 1.3." - Hash-verified. NuGet checks the binary against what it published, unlike a
curl | shinstaller. - No network at run time, for the platform-specific package — it contains the Bun binary, so there's no download on first use, no dependency on github.com being reachable, and no chance of a CI agent quietly picking up a different Bun than your laptop did.
Install
Per repository (recommended — this is the part that pins):
dotnet new tool-manifest # once per repository
dotnet tool install Scarlet.Bun.Cli
dotnet bun --version
Commit .config/dotnet-tools.json and every contributor and CI agent gets the same Bun from
dotnet tool restore.
Globally:
dotnet tool install -g Scarlet.Bun.Cli
Or once, without installing anything (.NET 10 SDK):
dnx Scarlet.Bun.Cli -- run build.mjs
The package version is the Bun version.
Scarlet.Bun.Cli1.4.2 contains Bun 1.4.2, the same as theScarlet.Bun.Runtime.*packages.
To move to a newer Bun, update the package like any other .NET tool — no separate upgrade command needed:
dotnet tool update Scarlet.Bun.Cli # local: also bumps .config/dotnet-tools.json
dotnet tool update -g Scarlet.Bun.Cli # global
How it finds Bun
Scarlet.Bun.Cli is a pointer package: it owns the dotnet-bun command but carries no Bun binary itself.
Installing it makes dotnet tool install/dotnet tool restore also pull one matching sub-package for your
machine's RID — Scarlet.Bun.Cli.win-x64, Scarlet.Bun.Cli.linux-arm64, and so on — and that package
embeds the actual Bun binary. This is automatic; you never name a sub-package yourself, and running
dotnet add package Scarlet.Bun.Cli.<rid> on one directly installs nothing usable — it carries no library
assets, only a tool payload NuGet places when Scarlet.Bun.Cli asks for it.
Supported platforms are Windows, Linux and macOS on x64 or arm64, including musl-based Linux
distributions such as Alpine — one sub-package per combination, eight in total. Hosts outside that matrix
restore Scarlet.Bun.Cli.any instead: a portable fallback with no embedded binary, so it downloads Bun on
first use and caches it per user rather than shipping a mismatched one. Any other architecture gets an
explanatory error rather than a mismatched binary — install Bun through its own installer and point at it
with SCARLET_BUN_PATH if you need one.
Resolution order:
SCARLET_BUN_PATH, if set — errors if it points at nothing, rather than quietly falling back- the Bun embedded in the installed package
- a previously downloaded Bun in the per-user cache
- a download
To see what it chose and why:
dotnet bun --scarlet-info
dotnet bun --scarlet-info --json
That is the only argument the tool reserves for itself, it is recognised only as the first argument, and
SCARLET_BUN_PASSTHROUGH=1 disables even that. It never downloads anything — it reports the URL it
would use.
Configuration
| Variable | Effect |
|---|---|
SCARLET_BUN_PATH |
Use this Bun executable. Highest precedence. |
SCARLET_BUN_VERSION |
Resolve a different Bun version, or latest. Bypasses the embedded binary. |
SCARLET_BUN_CACHE |
Override the download cache root. |
SCARLET_BUN_NO_EMBEDDED |
Ignore the embedded binary. |
SCARLET_BUN_DIAGNOSTICS |
Print the resolved Bun path to stderr before running. |
SCARLET_BUN_PASSTHROUGH |
Disable --scarlet-info so every argument reaches Bun. |
SCARLET_BUN_DOWNLOAD_TIMEOUT |
Seconds to wait for a concurrent download. Defaults to 300. |
Configuration is environment variables rather than command-line flags on purpose: every argument belongs to Bun, so a flag Bun adds in future keeps working without a release of this package.
SCARLET_BUN_VERSION changes which Bun binary gets downloaded and run — it never changes which
NuGet package is installed; that's decided once, at dotnet tool install time (see
How it finds Bun). Setting it to anything other than the version baked into the
installed package skips the embedded binary and downloads the requested one into the per-user cache,
scoped by version (<cache>/runtimes/<version>/), so later runs with the same value reuse it instead of
re-downloading.
That caching applies to latest too, literally: the first run resolves whatever GitHub currently tags as
newest and caches it under a folder named latest, and every run after that reuses that cached binary
without checking GitHub again — latest means "newest at the time I first asked," not "always current."
Clear <cache>/runtimes/latest/ (or point SCARLET_BUN_CACHE elsewhere) to pick up a newer release.
Running Bun during a build instead
If you want Bun to run as part of dotnet build — bundling JavaScript, compiling Sass, installing npm
dependencies — use Scarlet.Bun.MSBuild instead.
The two are independent; this tool is for the command line.
Links
Licensed under MIT. Bun and its bundled components are distributed under their respective licenses; see
LICENSE-3RD-PARTY.txt in the package.
Learn more about Target Frameworks and .NET Standard.
This package has no dependencies.