Incur compiles a TypeScript or JavaScript CLI into standalone executables for macOS, Linux, and Windows. Each executable includes the Bun runtime. Users do not need Node.js, Bun, Incur, or a package installation to run the executable.
The build computer must have Bun. Pin the Bun version in release automation. This keeps the compiler version the same for all targets.
Run incur build from the CLI project root:
pnpm exec incur build ./src/bin.tsIncur reads the executable name and version from the nearest package.json. If
the file does not contain these values or describes a different executable, set
the values explicitly:
pnpm exec incur build ./src/bin.ts \
--name frog \
--version 1.2.3 \
--output ./dist/binariesThe default output directory is dist/binaries. A default build with
--name frog contains these files:
dist/binaries/
frog-darwin-arm64
frog-darwin-arm64.gz
frog-darwin-x64
frog-darwin-x64.gz
frog-linux-arm64-glibc
frog-linux-arm64-glibc.gz
frog-linux-arm64-musl
frog-linux-arm64-musl.gz
frog-linux-x64-glibc-baseline
frog-linux-x64-glibc-baseline.gz
frog-linux-x64-musl-baseline
frog-linux-x64-musl-baseline.gz
frog-windows-arm64.exe
frog-windows-arm64.exe.gz
frog-windows-x64-baseline.exe
frog-windows-x64-baseline.exe.gz
SHA256SUMS
The compressed files are the GitHub Release assets. SHA256SUMS contains the
SHA-256 checksum for each .gz release asset. The raw executables are local
build products. You do not need to upload them.
To build selected targets, repeat --target:
pnpm exec incur build ./src/bin.ts \
--target darwin-arm64 \
--target linux-x64-glibc-baselineIf one target fails, Incur fails the command.
To generate Unix and Windows installers, build all targets:
pnpm exec incur build ./src/bin.ts --installerIncur reads the public GitHub repository from the nearest package.json
repository field. Use --repository if the field is missing or identifies a
different repository:
pnpm exec incur build ./src/bin.ts \
--installer \
--repository wevm/frogPass --tag when the assets will use a release tag other than v<version>:
pnpm exec incur build ./src/bin.ts \
--installer \
--repository wevm/frog \
--tag frog@1.2.3To generate installers:
- Use a stable semantic version.
- Build all default targets.
The command adds install.sh and install.ps1 to dist/binaries/. Each
installer contains the exact release tag. A latest-release URL selects only the
installer. The installer downloads its executable and SHA256SUMS from the
tagged release.
After the release action appends the assets, users can install the latest stable release:
curl -fsSL https://github.com/wevm/frog/releases/latest/download/install.sh | shirm https://github.com/wevm/frog/releases/latest/download/install.ps1 | iexUse an exact tag URL if you do not want a newer release:
curl -fsSL https://github.com/wevm/frog/releases/download/v1.2.3/install.sh | shA repository owner can replace a tag, its assets, and its checksum file together. Enable GitHub immutable releases to prevent changes after publication. Without this setting, the checksum verifies only the current release asset. It does not prove that the asset never changed.
A project URL can make the install command shorter. For example, redirect
https://frog.dev/install.sh to
https://github.com/wevm/frog/releases/latest/download/install.sh. The
installer still uses its embedded tag for all later downloads. GitHub documents
the stable
/releases/latest/download/<asset> URL.
The Unix installer supports macOS and Linux with glibc or musl. It supports ARM64 and x64. The PowerShell installer supports Windows on ARM64 and x64.
Each installer:
- Selects the correct release asset for the operating system and architecture.
- Verifies the release asset against
SHA256SUMS. - Decompresses the release asset in a temporary directory.
- Checks that the new executable reports the embedded version.
- Puts a temporary copy of the new executable in the destination directory.
- Replaces an existing executable atomically.
- Installs to
$HOME/.local/binby default. - Does not change shell profiles.
Set FROG_INSTALL_DIR to select a destination for this CLI. Set INSTALL_DIR
to override the destination for one installer run. Incur forms the CLI-specific
variable name from the executable name.
If the destination is not on PATH, the installer tells you how to add it. You
can download and inspect an installer before you run it.
The default build contains eight targets:
| Target | Operating system | Architecture | Runtime and CPU | Release asset |
|---|---|---|---|---|
darwin-arm64 |
macOS | ARM64 | Darwin | <name>-darwin-arm64.gz |
darwin-x64 |
macOS | x64 | Darwin, baseline CPU | <name>-darwin-x64.gz |
linux-arm64-glibc |
Linux | ARM64 | glibc | <name>-linux-arm64-glibc.gz |
linux-x64-glibc-baseline |
Linux | x64 | glibc, baseline CPU | <name>-linux-x64-glibc-baseline.gz |
linux-arm64-musl |
Linux | ARM64 | musl | <name>-linux-arm64-musl.gz |
linux-x64-musl-baseline |
Linux | x64 | musl, baseline CPU | <name>-linux-x64-musl-baseline.gz |
windows-arm64 |
Windows | ARM64 | Windows | <name>-windows-arm64.exe.gz |
windows-x64-baseline |
Windows | x64 | Windows, baseline CPU | <name>-windows-x64-baseline.exe.gz |
The x64 builds use Bun baseline targets. These targets support CPUs that do not have AVX2. ARM64 does not have a baseline target.
Incur stores target information in each executable. On Linux, this information includes glibc or musl. The updater reads the information from the executable. It does not detect the target on the user's computer.
Each executable includes Bun and can be tens of megabytes or larger. Compression reduces the release asset size. The exact size depends on the Bun version and the application. Investigate unexpected size changes. Do not enforce a fixed size.
See Bun executable targets for compiler compatibility.
Use Binary.github as the CLI update provider:
import { Binary, Cli } from 'incur'
const cli = Cli.create('frog', {
update: Binary.github({ repository: 'wevm/frog' }),
})Binary.github runs only in an executable that contains embedded Incur
metadata. Package installations through npm, pnpm, or Bun continue to use the
package manager for updates.
incur build adds the resolved version to the executable. In the executable,
Cli.create uses this version by default. An explicit version overrides the
embedded version.
The provider supports public GitHub repositories and stable releases:
- Checks published releases with stable semantic version tags.
- Ignores drafts, prereleases, invalid versions, the current version, and older versions.
- Selects the highest semantic version above the current version with an asset for the embedded target.
- Uses the exact release and asset. It does not use
latest/download, because that URL can change. - Requires a SHA-256 checksum in the GitHub Release asset
digestfield. - Verifies the release asset before decompression or installation.
Incur reads the cached result for automatic update checks. It starts a background refresh if the cache is absent or at least one day old.
Incur does not show automatic notices in agent, JSON, MCP, help, or completion output. When the cache contains a newer version, an interactive terminal shows this message:
Update available for frog:
frog --update # upgrade from 1.2.3 to 1.3.0
NO_UPDATE_NOTIFIER, CI, and npm_config_update_notifier=false stop
automatic checks. These settings do not change verification for an explicit
frog --update.
The updater does these steps before it replaces the installed executable:
- Downloads the release asset into memory.
- Verifies the SHA-256 checksum.
- Decompresses the release asset in memory.
- Puts the new executable beside the installed executable.
- Checks that the new executable reports the expected version.
On macOS and Linux, the updater makes the new executable runnable. It replaces the installed executable atomically on the same file system.
Windows cannot replace a running .exe. Incur starts a background update
process. This process replaces the file after the original process exits. The
command reports the update as staged before the process applies the replacement.
If a download, permission change, decompression, checksum verification, or version check fails, the installed executable stays usable. If a Windows update fails after it moves the old executable, the background process tries to restore it. The backup is in the same directory.
If the background process fails, Incur writes recovery details to
<executable>.incur-error-<uuid>.txt. It puts the error file beside the new
executable and any remaining backup. Read the error file. Fix the file system
error. Then run --update again. Never disable checksum verification.
Copy this workflow to .github/workflows/binary-release.yml:
name: Binary Release
on:
workflow_dispatch:
concurrency:
group: binary-release
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- id: release
uses: wevm/incur/release@v1The action uses these defaults:
- The entry point is
./src/bin.ts. - The CLI name comes from the root
package.json. - The version is the stable version in the root
package.json. - The release is the latest published GitHub release.
Before the action runs project code, it reads the package version from the triggering commit and resolves the latest published release. It verifies that the release tag identifies that package version, then checks out the tagged commit, installs dependencies, and builds the executables.
The job gives contents: write to all steps. Composite action steps share the
job permissions. Use only trusted source and lockfiles.
persist-credentials: false prevents checkout from storing credentials in the
worktree. This setting does not restrict permissions for later steps.
Run the action from a trusted push or manual workflow. The action rejects pull request merge refs.
The action:
- Requires a public repository and a stable package version.
- Resolves the latest mutable published release for that package version.
- Detects npm, pnpm, or Bun.
- Installs the project dependencies.
- Uses the pinned Bun version to compile all eight unsigned targets on Linux.
- Verifies all eight
.gzrelease assets againstSHA256SUMS. - Checks the syntax of both installer scripts.
- Tests the matching Linux glibc executable on the runner.
- Tests the matching Linux musl executable in Alpine.
- Uploads the
.gzrelease assets,SHA256SUMS,install.sh, andinstall.ps1. - Stops if an upload would replace an existing release asset.
The action tests only Linux executables for the runner architecture. It does not test other architectures, macOS, or Windows. It does not sign executables.
With changesets/action@v1, keep the default createGithubReleases: true. Run
Incur after Changesets reports a publication. Incur appends binary assets to the
release that Changesets published, including workspace releases tagged
<package>@<version>.
A published release is visible before its binaries are ready. A failed build can leave the release incomplete. Immutable releases cannot accept new assets.
Use these inputs only when you must override a default:
- Use
entryfor a different entry point. - Use
namefor a different executable and asset name. - Use
release_tagto select an existing release instead of the latest published release. - Use
package_managerif Incur cannot detect the package manager. - Use
pnpm_versionifpackage.jsondoes not specify the pnpm version. - Use
bun_versionto select the compiler version. - Use
smoke_commandto pass one safe argument to each tested Linux executable.
A composite action cannot set workflow concurrency. If release jobs can overlap, set concurrency in the caller workflow. This example runs one binary release at a time.
incur build and the release action create unsigned executables. Unsigned macOS
executables can trigger Gatekeeper warnings. Unsigned Windows executables can
trigger a SmartScreen warning or an enterprise-policy block. Use a separate
release process for each operating system that requires signed executables.
Standalone executable support does not include:
- Private-repository authentication for
Binary.githubor the release action. - Prerelease or named update channels.
- Packages for Homebrew, Scoop, Winget, or other package managers.
- GitHub Release publication, retagging, or package version management.
- Signing identities, notarization credentials, secret storage, or a signing service.
- Automatic upload from
incur build.
Other distribution providers can use the same release asset format. They must keep the same update checks.