Documentation
Builds and releases
Target-specific sidecars, signed updater assets, and a release index published after platform installers.
Distribution model
The desktop build packages a Vite frontend, a Bun-compiled sidecar, and generated SQLite migrations in a Tauri application. The repository defines macOS Apple Silicon and Intel, Windows x64 and ARM64, and Linux x64 and ARM64 builds. Release automation is configured in .github/workflows/release.yml; platform build checks also run independently through build-platforms.yml.
A workflow definition describes the publication process. The availability and validity of an installer must be established from its particular run and release assets. Source code alone does not prove signing credentials, notarization results, or successful installation on a target machine.
Build pipeline
scripts/build.ts runs six ordered stages and aborts when a stage fails:
- Install: frozen-lockfile installation, including optional dependencies for all operating systems and CPU architectures.
- Check: workspace typechecking. Unit tests are a separate CI command.
- Compile: build the sidecar with
scripts/compile-sidecar.tsfor the Rust target triple. - Resources: stage
packages/db/drizzleas bundled migrations. - Bundle: build the frontend, then invoke Tauri with target, bundle, signing, and updater settings.
- Artifacts: index installer assets, write SHA-256 checksums, and generate release notes from Git history.
bun scripts/build.ts --help
bun scripts/build.ts --dry-run
bun run build:sidecar
bun scripts/build.ts --only=resources
bun scripts/build.ts --no-updater --only and --skip accept stage names; --target, --bundles, and --out-dir select outputs. The default target is the host reported by rustc -vV. Sidecar binaries are written to apps/desktop/src-tauri/binaries/sidecar-<triple>, with .exe on Windows. Identical binaries and migration trees are kept unchanged to avoid unnecessary development restarts.
Use compile-sidecar.ts through the pipeline: it applies the embedded-host bundling adaptations. A direct generic compile command does not represent the same build procedure. The default artifact directory is apps/desktop/src-tauri/target/release-artifacts.
The resources step copies packages/db/drizzle/ into apps/desktop/src-tauri/resources/migrations/ and is safe to repeat. The artifacts step writes SHA256SUMS and RELEASE_NOTES.md, built from the commits since the last tag. Step aliases are accepted by --only and --skip: deps, checks/test, sidecar, migrations, and notes/checksums/release. The script never reads .env* files or prints secret values.
CI and publication
| Workflow | Configured responsibilities |
|---|---|
ci.yml | Frozen install, workspace typecheck/tests, sidecar/resource staging, Rust clippy/tests, and companion JavaScript checks. |
build-platforms.yml | Manual or build/** branch builds for six target architectures. Bundles use --no-updater and upload indexed artifacts without publishing a release. |
release.yml | Version-tag validation, signed platform builds, per-platform manifests, six-platform manifest merge, installer publication, and final index publication. |
ios.yml | Unsigned simulator build and pairing round trip; no App Store or TestFlight publication. |
The release workflow runs for v* tags or a manually selected existing version tag on the non-fork repository. release-ref.ts checks the tag and application version and resolves the commit. Each build creates a platform release, such as a version tag suffixed with macos-arm64 or linux-x64. Only after all platform releases publish does the version index publish the merged latest.json.
stage-release.ts checks the selected artifacts, checksum/signature relationships, and manifest links before publication. Already published releases are not overwritten. The index release is eligible to become latest for stable versions; prereleases remain marked as prereleases.
Updater builds require the Tauri signing private key and password configuration corresponding to the public key in tauri.conf.json. macOS release signing/notarization uses Apple certificate and account settings; Windows imports a signing certificate and passes its thumbprint to the bundler. Missing required release signing inputs fail the release build. Configure these through the CI secret store.
A separate release job publishes the nikcli plugin package when an npm token is available. It skips an already published version and can use the configured fallback package name when the primary scope cannot be published. This package job does not establish desktop installer availability.
Updater behavior
The configured updater endpoint is the repository's latest GitHub release download at latest.json. The Rust updater checks at launch and every four hours, with manual checks queued through its service. Each request uses a 30-second timeout. The manifest identifies platform-specific assets and signatures; the public key is part of the application configuration.
update://status carries checking, up-to-date, available, downloading, ready, and failed states. Release notes containing [critical] mark the available update as non-dismissible. A ready-to-restart state is also non-dismissible. This affects the prompt; installation still respects the unsaved-work guard.
install_update records and rechecks the announced version, then downloads a signature-verified package. The editor saves before download and again before apply_update, protecting edits made during download. Unsaved work blocks both operations; failed saves retain verified bytes for retry. Native status is recovered after subscribing, and checks cannot replace active progress or a pending restart. Windows drains the sidecar before its installer takes control; other platforms offer a restart after installation.
Keep release keys stable
The application verifies update signatures using its embedded public key. A release signed with a different private key is not interchangeable. Verify the produced installer and updater asset on the target platform before declaring a release ready for users.
Database compatibility
Packaged applications open cadcamfun.db under their application data directory. Pending migrations are applied by the database layer at startup using the migrations included in the bundle. Schema upgrades and binary updates are therefore related even though they are separate artifacts.
Generated release notes compare tracked migration trees with the previous release tag. A changed tree is reported as unsafe for downgrade; an unchanged tree is reported as migration-compatible; unavailable history is reported as unknown. This automated comparison does not validate every possible application-level compatibility issue.
Before replacing a binary with an older version, preserve the current data and inspect the release's compatibility notes. Forward migration code does not imply reverse migrations. Follow the database backup and recovery procedures for the actual data directory.
Release verification
bun test scripts
bun scripts/build.ts --only=artifacts --dry-run Helper tests validate target mapping, signing requirements, artifact naming, manifest handling, and staging rules. A dry run prints planned operations without compiling or signing. Neither substitutes for an actual native bundle, updater download/install test, nor physical-device acceptance for the companion.
For an operational release, retain the exact tag/commit, workflow result, target-specific installer and update asset, checksum/signature files, and published manifest. Validate that the installed app starts its bundled sidecar and migrations and that the updater selects the intended platform asset.
Target and asset reference
| Platform | Rust target | Manifest key | Updater asset |
|---|---|---|---|
| macOS Apple Silicon | aarch64-apple-darwin | darwin-aarch64 | .app.tar.gz |
| macOS Intel | x86_64-apple-darwin | darwin-x86_64 | .app.tar.gz |
| Windows ARM64 | aarch64-pc-windows-msvc | windows-aarch64, windows-aarch64-msi | .exe, .msi |
| Windows x64 | x86_64-pc-windows-msvc | windows-x86_64, windows-x86_64-msi | .exe, .msi |
| Linux ARM64 | aarch64-unknown-linux-gnu | linux-aarch64-deb, linux-aarch64-rpm | .deb, .rpm |
| Linux x64 | x86_64-unknown-linux-gnu | linux-x86_64-deb, linux-x86_64-rpm | .deb, .rpm |
The generator requires exactly one asset per format above, its adjacent .sig file, and release notes. Asset names use a <triple>-- prefix; duplicate names and orphan signatures are refused. Tauri updater 2.13.1 selects the installed package type automatically; Linux uses signed Debian/RPM packages without an AppImage.
Merged manifests must agree on version, publication date, and notes. Publication requires all ten keys above. Windows base keys retain NSIS compatibility for older clients; MSI keys preserve MSI installations. URLs must use HTTPS without credentials, a query or a fragment. Staging checks each target prefix, package suffix, signature and release URL.
Local build options and signing inputs
bun --no-env-file scripts/build.ts --only=compile,resources --target=aarch64-apple-darwin --dry-run
bun --no-env-file scripts/build.ts --skip=install,check --target=x86_64-unknown-linux-gnu --bundles=deb,rpm --no-updater --dry-run Remove --dry-run only when the selected target toolchain and native prerequisites are installed. Cross-compiling the Bun executable does not by itself provide a cross-platform Tauri linker and bundler. --only and --skip are mutually exclusive; --skip-install is a convenience alias. --no-updater omits updater archive/signature creation and its signing gate.
| Input | Used for |
|---|---|
TAURI_SIGNING_PRIVATE_KEY, TAURI_SIGNING_PRIVATE_KEY_PASSWORD | Updater signatures. Environment-supplied keys take precedence over local signing configuration. |
Ignored local tauri-signing.json | Local updater-key fallback when no environment key is supplied. Its declared public key must equal the configured updater public key. |
CADCAM_RELEASE=1 | Enables platform release-signing requirements in the build validation. |
APPLE_CERTIFICATE, APPLE_CERTIFICATE_PASSWORD, APPLE_ID, APPLE_PASSWORD, APPLE_TEAM_ID | macOS release certificate and notarization configuration. |
WINDOWS_CERTIFICATE, WINDOWS_CERTIFICATE_PASSWORD, imported certificate thumbprint | Windows release signing; CI imports and later removes the certificate. |
The build loads local updater credentials only for the bundling step, and passes signing values through the environment to that child process. Do not print local signing files or place real key material in a documentation example. A private key that does not match an installed app's public key cannot produce an accepted update for that app.
Release failure diagnosis
| Failure | Interpretation and next check |
|---|---|
| Tag/application version disagree | The selected v-prefixed tag does not match the version in Tauri configuration. Release validation stops before platform builds. |
| Unsupported target | Use a supported architecture/platform mapping and installed Rust target; manifest publication supports the six keys above. |
| Missing updater signing key | Provide the signing input through the secret store/local configuration or select a deliberate --no-updater development build. |
| Zero or multiple updater assets | Inspect the indexed target directory; stale or mismatched archives make selection ambiguous. |
| Manifest metadata disagree | All platform jobs must use the same resolved version/date/notes and commit. |
| Release already published | The workflow refuses to modify it. Re-running is not an overwrite mechanism. |
| Updater check fails on the installed app | Inspect endpoint reachability, manifest target key, download URL, asset signature, and embedded public key. |
| Install refused with unsaved work | Save or discard the active work through the editor, then retry the pending update. |
A useful release report records which stage failed and which target it was building. Include non-secret artifact names, validation errors, and workflow identifiers. Keep the previous installed version and a verified data backup available during an updater exercise; migration compatibility is checked separately from signature validity.
Implementation anchors: scripts/release-helpers.ts, signing-config.ts, updater-manifest.ts, stage-release.ts, and apps/desktop/src-tauri/src/updater.rs.
The six build steps
| # | Step | Aliases | What it does |
|---|---|---|---|
| 1 | install | deps | bun install --frozen-lockfile |
| 2 | check | checks, test, tests | bun run typecheck |
| 3 | compile | sidecar | Compile apps/sidecar/src/main.ts to src-tauri/binaries/sidecar-<triple> |
| 4 | resources | resource, migrations | Copy packages/db/drizzle/ to src-tauri/resources/migrations/ |
| 5 | bundle | bundling | Desktop vite build, then bunx tauri build |
| 6 | artifacts | artefacts, notes, checksums, release | SHA256SUMS and RELEASE_NOTES.md from commits since the last tag |
Steps always run in this order and a failing step stops the run. The script never reads .env files and never prints secret values.
| Option | Effect |
|---|---|
--only=a,b / --skip=a,b | Run or skip steps (names or aliases) |
--skip-install | Same as --skip=install |
--target=<triple> | Rust target; default is the host triple from rustc -vV |
--bundles=<list> | Forwarded to tauri build --bundles |
--no-updater | Installers without updater archives and signatures |
--out-dir=<path> | Where step 6 writes; default apps/desktop/src-tauri/target/release-artifacts |
--dry-run | Print the plan; write and change nothing |
bun scripts/build.ts --dry-run # whole plan, no side effects
bun scripts/build.ts --only=resources # restage migrations (idempotent)
bun scripts/build.ts --only=compile,resources # rebuild the sidecar and migrations
bun scripts/build.ts --only=notes --dry-run # preview release notes
bun scripts/build.ts --target=aarch64-apple-darwin Local updater signing uses an ignored tauri-signing.json when no signing key is set in the environment. Platform certificates are still required when CADCAM_RELEASE=1.
Release matrix
| Target | Runner | Bundles | Release slug |
|---|---|---|---|
aarch64-apple-darwin | macos-14 | app, dmg | macos-arm64 |
x86_64-apple-darwin | macos-14 | app, dmg | macos-x64 |
x86_64-pc-windows-msvc | windows-latest | msi, nsis | windows-x64 |
aarch64-pc-windows-msvc | windows-11-arm | msi, nsis | windows-arm64 |
x86_64-unknown-linux-gnu | ubuntu-22.04 | deb, rpm | linux-x64 |
aarch64-unknown-linux-gnu | ubuntu-22.04-arm | deb, rpm | linux-arm64 |
- A trusted job validates the release tag against the application version.
- Each matrix job runs
bun --no-env-file scripts/build.ts --target=… --bundles=… --out-dir=release, signs and builds a manifest for its package formats. Windows imports and afterwards removes its optional signing certificate. - A merge job validates all six architecture manifests and their ten package entries into one
latest.json. - One completion job verifies all installer artifacts, signatures, checksums and notes before creating or resuming the single version release and publishing it.
CI on every push runs two jobs: install, typecheck and test (and stages the sidecar binary and migrations), and a Rust job with clippy and the src-tauri tests.