Skip to content
View as .md

Releases

arbe ships as standalone binaries built with bun build --compile for darwin-arm64, linux-x64 (Linux ARM and Intel Mac deferred — scripts/release-build.ts TARGETS is canonical). This page is the durable contract — installer scripts, Quickstart, and release tooling all reference it. Change here first, then update consumers.

Versioning is the semver in apps/cli/package.json; commit SHA + UTC build date are baked in via --define process.env.ARBE_BUILD_COMMIT / process.env.ARBE_BUILD_DATE. arbe --version prints arbe <version> (<short-commit>, <yyyy-mm-dd>) (or arbe dev in dev). arbe upgrade compares only the version string against latest.json — commit SHA and date don’t affect the upgrade decision.

dist/release/latest/
arbe-<target>.tar.gz # single `arbe` at archive root
SHA256SUMS # one per line, sorted
release-latest.json # → arbe/latest.json (mutable, 60s cache, must-revalidate)
release-<version>.json # → arbe/<version>.json (immutable, 1y)

R2 holds two pointers to the same bytes: arbe/latest/ (mutable — newest published build) and arbe/<version>/ (pinned, never overwritten in practice). Manifests at arbe/latest.json / arbe/<version>.json follow LatestReleaseManifest (apps/cli/src/update/shared.ts) — version, commit, published_at, artifacts.<target>.{url,sha256,size} all required, plus an optional notes string carrying that version’s changelog section as markdown (manifests published before it existed simply omit it — arbe upgrade prints it when present and says nothing when not). Always fetch the tarball and SHA256SUMS from the same prefix together — latest/ is mutable so they can mismatch mid-publish; latest.json is the only stable metadata pointer; the commit SHA baked into the binary is authoritative for “what build do I have.”

Installer at apps/www/static/install.sh (one-liner: curl -fsSL https://arbe.0sk.ar/install.sh | sh) reads the manifest, picks target by uname, downloads + verifies sha256, drops arbe in $ARBE_INSTALL_DIR (default ~/.local/bin). POSIX sh, no jq/python — only curl/wget, tar, sha256sum/shasum. Knobs: ARBE_VERSION=latest|<x.y.z>, ARBE_TARGET=<target> (cross-arch testing), ARBE_INSTALL_DIR=<path>. Drift is bounded by the manifest schema.

Cut a release: bun run release [patch|minor|major|x.y.z] (release:bump is kept as an alias) closes the changelog’s Unreleased section into v<x.y.z> — YYYY-MM-DD, bumps apps/cli/package.json, jj-commits only those two files, advances the release bookmark, and pushes (--dry-run writes both files but stops, printing the manual jj steps). It refuses when Unreleased has no bullets (--allow-empty overrides), and runs no checks or tests of its own — run bun run check and bun run test first. The cli-release GitHub Action runs release:build + release:upload against R2 on pushes to the release branch. CLOUDFLARE_ACCOUNT_ID + CLOUDFLARE_API_TOKEN repo secrets do the auth — wrangler picks them up automatically; one-time setup in system/deployment.

The release pushes main and release together — after a release both point at the stamped commit.

Verify after (like deploys, releases get a recipe): watch the cli-release run to green (gh run list --workflow cli-release), then check arbe/latest.json and the pinned arbe/<x.y.z>.json both report the new version and the release commit, notes carries the changelog section, and a downloaded tarball’s sha256 matches its manifest entry. The extracted binary must print the new version and commit: arbe <x.y.z> (<sha>, <date>).

Break-glass: bun run release:build then bun run release:upload mirror manually; local invocation needs bunx wrangler whoami authed against the right Cloudflare account. Always overwrites. Cache-Control is set per-key on upload — latest/* and latest.json get max-age=60, must-revalidate; <version>/* and <version>.json get max-age=31536000, immutable. Releases land on the edge within ~60s.

Changelog

How entries are written — the rules, voice, and ordering — lives in the changelog skill (packages/skills/changelog/SKILL.md). Mechanically: bun run release moves the whole Unreleased body under a new ## v<x.y.z> — YYYY-MM-DD heading and leaves a fresh empty Unreleased above it, in the same commit as the version bump. scripts/release-build.ts reads that section back out and stamps it into both manifests as notes, so arbe upgrade prints what’s new right after installing.

Code: scripts/release-build.ts, scripts/release-upload.ts, scripts/release-bump.ts, scripts/lib/changelog.ts, apps/cli/src/update/shared.ts (LatestReleaseManifest), .github/workflows/cli-release.yml.
See cli, system/deployment.