Prerequisites, common tasks, and optional local database setup are in README.md.
On Linux, run Zed's script/linux once for GPUI build dependencies before building or packaging.
Git hooks (via lefthook) run automatically after mise install:
- pre-commit: Rust —
lint-fix→format; Web —lint-fix→format(oxlint/oxfmt) - pre-push: Rust —
lint→test; Web —lint→check(astro check)
Install cargo-packager (cargo install cargo-packager --locked), then:
mise run packageInstallers are written to apps/desktop/dist/. Packager configuration lives in the [package.metadata.packager] section of apps/desktop/Cargo.toml.
Releases use CalVer in the form vYYYY.M.PATCH, where PATCH resets to 0 at the start of each calendar month (UTC) and increments per release within that month — for example, v2026.5.0, v2026.5.1, then v2026.6.0. Months 10-12 are double-digit (v2026.10.0). The month is not zero-padded because Cargo enforces strict SemVer in [package].version and forbids leading zeros (2026.05.0 would be rejected). script/next-version.py is the single source of truth for "what version comes next" — both CI and maintainers can run it (python3 script/next-version.py) to preview the result.
The version in apps/desktop/Cargo.toml is permanently 0.0.0-dev. The real CalVer version is computed by CI and stamped into Cargo.toml in-memory on each runner before cargo build, so the installed binary reports the correct version via env!("CARGO_PKG_VERSION"). Local builds from main intentionally report 0.0.0-dev — they are not release builds.
- Go to Actions → Release → Run workflow on GitHub, select
main, click Run workflow. - CI:
- Computes the next CalVer by running
script/next-version.py, which scans existingv*tags and emitsvYYYY.M.PATCHfor the current UTC month. - Stamps it into
Cargo.tomlon each runner viascript/set-version.py. - Builds installers for macOS (arm64), Linux, and Windows with
cargo-packager, plus signed updater bundles and alatest.jsonmanifest for in-app updates. - Publishes a GitHub Release at the new tag with auto-generated notes from PRs/commits since the previous tag and all installers.
- Bumps the Homebrew cask and opens a winget-pkgs PR when
HOMEBREW_TAP_TOKEN/WINGET_RELEASE_TOKENsecrets are set.
- Computes the next CalVer by running
- (Optional) Edit the release on GitHub afterward to polish the auto-generated notes.
Generate a minisign key pair for update manifests:
cargo install cargo-packager --locked
cargo packager signer generate --path apps/desktop/assets/updater-key --ci --force- Commit
apps/desktop/assets/updater-key.pub(already gitignored:apps/desktop/assets/updater-keyprivate key). - Store the private key in the GitHub Actions secret
UPDATER_PRIVATE_KEYusing the GitHub CLI:
# From the repo root, after generating the key pair above:
gh secret set UPDATER_PRIVATE_KEY < apps/desktop/assets/updater-key
# Or inline (same result):
gh secret set UPDATER_PRIVATE_KEY --body "$(cat apps/desktop/assets/updater-key)"
# Verify the secret exists (value is never shown):
gh secret list | grep UPDATER_PRIVATE_KEYEach release job ad-hoc signs macOS .app bundles (signing_identity = "-" in apps/desktop/Cargo.toml) so Gatekeeper does not treat linker-signed binaries as “damaged.” Update signing (minisign on .app.tar.gz, .AppImage, NSIS .exe) is separate from Apple Developer ID / notarization — first install may still show an unverified-developer or SmartScreen prompt.
GitHub exposes a SHA-256 digest for every release asset. After downloading, compare locally:
shasum -a 256 Based_2026.5.0_aarch64.dmg
# vs assets[].digest from GET /repos/pavi2410/based/releases/latest- No bot push to
main. The version is never committed back; the tag is created bygh release createpointing at the workflow's commit SHA. - Do not hand-edit the
[package].versioninapps/desktop/Cargo.toml— it should always be0.0.0-dev. The release flow owns the real version end-to-end. - First release: if no prior
v*tag exists for the current month, the action starts at.0; no bootstrap tag is required.
GitHub Releases remain the only artifact builder. Homebrew and winget are thin manifests (URL + SHA-256) updated after each release. See packaging/README.md for filename contracts.
Tap repo: pavi2410/homebrew-tap
brew tap pavi2410/tap
brew install --cask basedAfter each release, CI runs script/bump-packaging.py --homebrew when HOMEBREW_TAP_TOKEN is set (fine-grained PAT with contents: write on pavi2410/homebrew-tap only).
Manual bump:
VERSION=v2026.5.2 python3 script/bump-packaging.py --homebrew --dry-run
HOMEBREW_TAP_TOKEN=... VERSION=v2026.5.2 python3 script/bump-packaging.py --homebrewPackage ID: pavi2410.Based
winget install pavi2410.BasedOne-time bootstrap: fork microsoft/winget-pkgs, copy manifests from packaging/winget/ templates (or run VERSION=v2026.5.2 python3 script/bump-packaging.py --winget-snapshot), and open a PR. Full steps: packaging/winget/BOOTSTRAP.md.
Ongoing updates: set GitHub secret WINGET_RELEASE_TOKEN (PAT with fork + PR access to your winget-pkgs fork). The release workflow runs winget-releaser and opens a bump PR — merge when convenient.