desktop-packaging-tauri
Tauri 2.x bundling, code signing, auto-updater, platform installers, CI/CD
Install
npx skills add https://github.com/agents-inc/skills/tree/main/dist/plugins/desktop-packaging-tauri/skills/desktop-packaging-tauri
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install agents-inc-skills@llmmart
git clone https://github.com/agents-inc/skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole agents-inc/skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Tauri 2.x Bundling & Distribution
Quick Guide: Configure bundling in
tauri.conf.jsonunderbundle. Platform targets: NSIS/MSI (Windows), DMG/app bundle (macOS), deb/rpm/AppImage (Linux). Code signing is required for macOS distribution (Apple notarization) and recommended for Windows (SmartScreen). The auto-updater uses Ed25519 (Minisign) signatures -- generate keys withcargo tauri signer generate, setTAURI_SIGNING_PRIVATE_KEYat build time. Cross-platform CI usestauri-apps/tauri-actionwith a matrix strategy. Optimize binary size with[profile.release]settings inCargo.toml.Current version: Tauri 2.x (stable). Updater artifacts use
createUpdaterArtifacts: true(not the v1"v1Compatible"unless migrating).
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST set TAURI_SIGNING_PRIVATE_KEY as an environment variable at build time for updater artifacts -- never commit the private key)
(You MUST configure code signing for macOS distribution -- unsigned apps are blocked by Gatekeeper)
(You MUST use bundle.identifier as a valid reverse-domain string -- it is used for code signing, app data paths, and store submissions)
(You MUST build platform-specific installers on their native OS -- cross-compilation is limited to NSIS via cargo-xwin)
(You MUST set createUpdaterArtifacts: true in bundle to generate .sig files alongside installers)
</critical_requirements>
Auto-detection: tauri.conf.json bundle, cargo tauri build, bundle targets, NSIS, MSI, DMG, AppImage, deb, rpm, code signing, notarization, APPLE_SIGNING_IDENTITY, certificateThumbprint, tauri-plugin-updater, createUpdaterArtifacts, TAURI_SIGNING_PRIVATE_KEY, sidecar, externalBin, tauri-action, cargo tauri signer, Minisign, installer hooks
When to use:
- Configuring
tauri.conf.jsonbundle settings (targets, icons, resources, identifier) - Building platform-specific installers (NSIS, MSI, DMG, deb, rpm, AppImage)
- Setting up macOS code signing and Apple notarization
- Setting up Windows code signing (OV/EV certificates, Azure Trusted Signing)
- Configuring the auto-updater plugin with Ed25519 signature verification
- Optimizing Tauri app binary size (Rust release profile, frontend bundle)
- Bundling sidecar binaries or extra resources
- Creating GitHub Actions CI/CD for cross-platform builds
- Customizing NSIS installers with hooks or templates
When NOT to use:
- Tauri command/IPC bridge, permissions, plugins, window management (use the Tauri framework skill)
- Frontend framework or build tool configuration (separate skills)
- General Rust programming or Cargo configuration not specific to Tauri bundling
- Mobile distribution to App Store / Google Play (different workflow)
Key patterns covered:
- Bundle configuration in
tauri.conf.json(examples/core.md) - Platform-specific installer options (examples/core.md)
- macOS code signing and notarization (examples/code-signing.md)
- Windows code signing (examples/code-signing.md)
- Auto-updater setup with Ed25519 signatures (examples/updater.md)
- Binary size optimization (examples/core.md)
- Sidecar binaries and resources (examples/core.md)
- GitHub Actions cross-platform CI/CD (examples/ci-cd.md)
- NSIS installer hooks and customization (examples/core.md)
Detailed resources:
- examples/core.md - Bundle config, platform targets, size optimization, sidecars, NSIS hooks
- examples/code-signing.md - macOS notarization, Windows signing, CI/CD signing setup
- examples/updater.md - Auto-updater plugin, key generation, endpoint format, JS/Rust usage
- examples/ci-cd.md - GitHub Actions workflow, matrix strategy, secrets
- reference.md - Bundle config field reference, CLI commands, platform target table
<decision_framework>
Decision Framework
Which Installer Format?
Target platform?
|-- Windows
| +-- Need MSI for enterprise deployment? -> msi (WiX, Windows-only build)
| +-- General distribution? -> nsis (recommended, cross-compilable)
|-- macOS
| +-- App Store? -> app bundle + App Store signing
| +-- Direct download? -> dmg + Developer ID + notarization
|-- Linux
| +-- Targeting Debian/Ubuntu? -> deb
| +-- Targeting Fedora/RHEL? -> rpm
| +-- Maximum portability? -> appimage (larger, ~70+ MB)
| +-- Sandboxed distribution? -> snap or flatpak (manual setup)
+-- All platforms? -> Use "all" target with CI matrix
Code Signing Decision
Distributing publicly?
|-- macOS
| +-- App Store? -> Apple Distribution certificate
| +-- Direct download? -> Developer ID Application + notarization (REQUIRED)
| +-- Internal/testing only? -> Ad-hoc signing (signingIdentity: "-")
|-- Windows
| +-- Microsoft Store? -> Store signing
| +-- Direct download? -> OV or EV certificate (prevents SmartScreen warnings)
| +-- Internal only? -> Optional but recommended
+-- Linux
+-- Code signing is not required for Linux distribution
Updater Strategy
Need auto-updates?
|-- YES -> tauri-plugin-updater
| +-- Simple static hosting? -> Static JSON endpoint (GitHub Releases, S3)
| +-- Dynamic update logic? -> Dynamic endpoint (returns 200/204)
| +-- Need update UI? -> JS-side check() + downloadAndInstall()
| +-- Background updates? -> Rust-side updater with AppHandle
+-- NO -> Skip updater config, omit createUpdaterArtifacts
</decision_framework>
<red_flags>
RED FLAGS
High Priority Issues:
- Committing
TAURI_SIGNING_PRIVATE_KEYto source control -- store as CI secret, never in repo - Missing
createUpdaterArtifacts: truewhen using the updater -- no.sigfiles generated, updates fail - Building macOS installer on Linux/Windows -- cross-compilation not supported for DMG/app bundle
- Using
"targets": "all"in CI without a matrix strategy -- builds all formats for the current OS only - Missing
bundle.identifieror using an invalid format -- breaks code signing, app data paths, and store submissions - Distributing unsigned macOS app -- Gatekeeper blocks it, users cannot open it
Medium Priority Issues:
- AppImage on Ubuntu 22.04+ targeting older distros -- higher glibc requirement breaks compatibility
- Missing
strip = truein release profile -- debug symbols inflate binary by 10-20% - Using
opt-level = 3instead of"s"or"z"when binary size matters -- optimizes for speed, not size - WebView2
skipinstall mode without guarantee runtime is present -- app crashes on startup - NSIS
perUsermode when app needs system-wide installation -- installs to%LOCALAPPDATA%, not Program Files
Gotchas & Edge Cases:
opt-level = "s"vs"z"-- sometimes"z"produces smaller binaries, sometimes"s"does. Test both.- macOS ad-hoc signing (
signingIdentity: "-") still triggers Gatekeeper warnings -- only useful for development - NSIS is the only format supporting cross-compilation from Linux/macOS to Windows (via
cargo-xwin) - Sidecar binary filenames must include the Rust target triple suffix -- Tauri resolves the correct one at runtime
- Updater endpoint template variables (
{{target}},{{arch}},{{current_version}}) are Tauri-specific, not environment variables - AppImage bundles are ~70+ MB because they include all dependencies -- deb/rpm are 2-6 MB but require system packages
- Windows WebView2 runtime is bundled by default with
embedBootstrapper-- olderdownloadBootstrappermode requires internet at install time - Snap/Flatpak packages run in a sandbox -- DBus communication is blocked unless declared in the manifest
removeUnusedCommands: true(Tauri 2.4+) strips commands not in capability files -- ensure all needed commands are listed in ACL- Free Apple Developer accounts cannot notarize apps -- a paid $99/year account is required for distribution
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST set TAURI_SIGNING_PRIVATE_KEY as an environment variable at build time for updater artifacts -- never commit the private key)
(You MUST configure code signing for macOS distribution -- unsigned apps are blocked by Gatekeeper)
(You MUST use bundle.identifier as a valid reverse-domain string -- it is used for code signing, app data paths, and store submissions)
(You MUST build platform-specific installers on their native OS -- cross-compilation is limited to NSIS via cargo-xwin)
(You MUST set createUpdaterArtifacts: true in bundle to generate .sig files alongside installers)
Failure to follow these rules will produce unsigned binaries, missing update signatures, or broken cross-platform builds.
</critical_reminders>
Files (skills)
-
examples
-
ci-cd.md 7.7 KB
# Tauri Bundling - CI/CD > GitHub Actions cross-platform build workflow, matrix strategy, code signing in CI, updater artifact publishing. See [SKILL.md](../SKILL.md) for decision frameworks. See [code-signing.md](code-signing.md) for signing details. --- ## GitHub Actions Workflow ### Complete Cross-Platform Build ```yaml name: "Build & Release" on: workflow_dispatch: push: branches: - release jobs: publish-tauri: permissions: contents: write strategy: fail-fast: false matrix: include: - platform: "macos-latest" args: "--target aarch64-apple-darwin" - platform: "macos-latest" args: "--target x86_64-apple-darwin" - platform: "ubuntu-22.04" args: "" - platform: "ubuntu-22.04-arm" args: "" - platform: "windows-latest" args: "" runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v4 # Install Linux dependencies - name: Install Linux dependencies if: runner.os == 'Linux' run: | sudo apt-get update sudo apt-get install -y \ libwebkit2gtk-4.1-dev \ libappindicator3-dev \ librsvg2-dev \ patchelf # Setup Node.js - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: lts/* cache: "npm" # Setup Rust - name: Setup Rust uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} # Cache Rust build artifacts - name: Cache Rust uses: swatinem/rust-cache@v2 # Install frontend dependencies - name: Install dependencies run: npm ci # Import Windows certificate - name: Import Windows certificate if: matrix.platform == 'windows-latest' env: WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }} WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} run: | $bytes = [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE) [IO.File]::WriteAllBytes("cert.pfx", $bytes) Import-PfxCertificate -FilePath cert.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString $env:WINDOWS_CERTIFICATE_PASSWORD -AsPlainText -Force) Remove-Item cert.pfx # Build and release - name: Build Tauri app uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # macOS signing APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} APPLE_API_KEY_PATH: ${{ secrets.APPLE_API_KEY_PATH }} # Updater signing TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} with: tagName: app-v__VERSION__ releaseName: "App v__VERSION__" releaseBody: "See the assets to download and install this version." releaseDraft: true prerelease: false args: ${{ matrix.args }} ``` ### Key Workflow Details **Matrix strategy:** - `fail-fast: false` -- one platform failure does not cancel other builds - macOS builds target both Intel and Apple Silicon separately - Ubuntu 22.04 for Linux (22.04-arm for ARM64 builds) - `tauri-action` handles the build, bundling, and release upload **Platform dependencies:** - Linux requires `libwebkit2gtk-4.1-dev`, `libappindicator3-dev`, `librsvg2-dev`, `patchelf` - macOS and Windows have no additional system dependencies **Secrets required:** | Secret | Platform | Purpose | | ------------------------------------ | -------- | ---------------------------- | | `APPLE_CERTIFICATE` | macOS | Base64-encoded `.p12` | | `APPLE_CERTIFICATE_PASSWORD` | macOS | `.p12` password | | `APPLE_SIGNING_IDENTITY` | macOS | Signing identity string | | `APPLE_API_ISSUER` | macOS | Notarization API issuer | | `APPLE_API_KEY` | macOS | Notarization API key ID | | `APPLE_API_KEY_PATH` | macOS | Path to `.p8` key file | | `WINDOWS_CERTIFICATE` | Windows | Base64-encoded `.pfx` | | `WINDOWS_CERTIFICATE_PASSWORD` | Windows | `.pfx` password | | `TAURI_SIGNING_PRIVATE_KEY` | All | Ed25519 key for updater sigs | | `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` | All | Key password (optional) | --- ## Tauri Action Configuration ### Common Options ```yaml - uses: tauri-apps/tauri-action@v0 with: # Release settings tagName: app-v__VERSION__ # __VERSION__ is replaced with app version releaseName: "App v__VERSION__" releaseBody: "Release notes here" releaseDraft: true # Create as draft (review before publishing) prerelease: false # Build settings args: "--target aarch64-apple-darwin" # Rust target tauriScript: "npx tauri" # Custom tauri command # Updater updaterJsonPreferNsis: true # Prefer NSIS over MSI for Windows ``` ### macOS Universal Binary To create a universal binary (Intel + Apple Silicon in one file), build both architectures and combine: ```yaml matrix: include: - platform: "macos-latest" args: "--target universal-apple-darwin" ``` Or build separately and use `lipo`: ```yaml - platform: "macos-latest" args: "--target aarch64-apple-darwin" - platform: "macos-latest" args: "--target x86_64-apple-darwin" ``` --- ## Separated Build and Bundle For complex pipelines, split the build and bundle steps: ```yaml # Step 1: Build without bundling - name: Build binary run: npx tauri build --no-bundle # Step 2: Bundle separately (can be a different job) - name: Bundle run: npx tauri bundle ``` This is useful when you need to run additional steps between compilation and packaging (e.g., signing, testing the binary). --- ## Caching Strategy ```yaml # Rust build cache (significant speedup) - uses: swatinem/rust-cache@v2 # Node.js dependency cache - uses: actions/setup-node@v4 with: cache: "npm" ``` **Impact:** Rust caching reduces subsequent build times from 10-30 minutes to 2-5 minutes. Node.js caching saves 30-60 seconds on dependency installation. --- ## Common CI Pitfalls - **macOS notarization timeout** -- Notarization can take 5-60 minutes. Ensure your CI job timeout is sufficient (default GitHub timeout is 6 hours). - **ARM Linux builds** -- Use `ubuntu-22.04-arm` runners (available for public repos). Previous workarounds with emulation are no longer needed. - **Missing Linux dependencies** -- `libwebkit2gtk-4.1-dev` is the critical one. Missing it produces cryptic Rust compile errors. - **Certificate not found in Windows CI** -- The certificate import step must run before `tauri-action`. The PowerShell script imports into `Cert:\CurrentUser\My`. - **`GITHUB_TOKEN` permissions** -- The job needs `contents: write` permission to create releases. - **Version mismatch** -- `__VERSION__` in `tagName` is replaced with the version from `tauri.conf.json`. Ensure `tauri.conf.json` version matches your intended release. --- See [code-signing.md](code-signing.md) for detailed signing setup and [updater.md](updater.md) for auto-updater configuration. -
code-signing.md 6.2 KB
# Tauri Bundling - Code Signing > macOS code signing and notarization, Windows code signing, CI/CD signing setup. See [SKILL.md](../SKILL.md) for decision frameworks. See [reference.md](../reference.md) for environment variable reference. --- ## macOS Code Signing ### Certificate Types | Certificate | Distribution Method | Notarization Required | | ------------------------------- | --------------------- | --------------------- | | Apple Distribution | App Store | No (Apple handles it) | | Developer ID Application | Direct download (DMG) | Yes | | Ad-hoc (`signingIdentity: "-"`) | Development/testing | No (Gatekeeper warns) | ### Local Setup 1. Create a Certificate Signing Request from Keychain Access 2. Upload CSR to Apple Developer > Certificates, IDs & Profiles 3. Download and install the `.cer` file 4. Find your signing identity: ```sh security find-identity -v -p codesigning ``` 5. Configure in `tauri.conf.json`: ```json { "bundle": { "macOS": { "signingIdentity": "Developer ID Application: My Company (TEAMID)" } } } ``` Or set the `APPLE_SIGNING_IDENTITY` environment variable. ### Notarization Setup Required when using Developer ID Application certificates (apps distributed outside the App Store). **App Store Connect API (recommended for CI):** ```sh export APPLE_API_ISSUER="issuer-uuid-from-app-store-connect" export APPLE_API_KEY="key-id" export APPLE_API_KEY_PATH="/path/to/AuthKey_KEYID.p8" ``` **Apple ID method (alternative):** ```sh export APPLE_ID="developer@example.com" export APPLE_PASSWORD="app-specific-password" export APPLE_TEAM_ID="TEAMID" ``` **Key points:** - Generate an app-specific password at appleid.apple.com (not your Apple ID password) - Team ID is found in Apple Developer > Membership - App Store Connect API is preferred because it avoids 2FA issues in CI ### CI/CD Setup for macOS Export your certificate as base64 for use in CI secrets: ```sh # Export certificate from Keychain as .p12 # Then encode as base64 openssl base64 -A -in certificate.p12 -out certificate-base64.txt ``` Required CI secrets: ```yaml # GitHub Actions secrets APPLE_CERTIFICATE: <base64-encoded .p12 file> APPLE_CERTIFICATE_PASSWORD: <password for .p12> APPLE_SIGNING_IDENTITY: "Developer ID Application: My Company (TEAMID)" APPLE_API_ISSUER: <App Store Connect API issuer> APPLE_API_KEY: <API key ID> APPLE_API_KEY_PATH: <path to .p8 key file> ``` The certificate is imported into a temporary keychain during the build. The `tauri-action` handles this automatically when the environment variables are set. --- ## Windows Code Signing ### Certificate Types | Certificate Type | SmartScreen Behavior | Availability | | --------------------------- | -------------------------------- | ------------------ | | EV (Extended Validation) | Immediate reputation | More expensive | | OV (Organization Validated) | Builds reputation over time | Pre-June 2023 only | | Azure Trusted Signing | Immediate reputation | Azure subscription | | None | SmartScreen warning to all users | Free | ### Method 1: Certificate Thumbprint (Local/CI) 1. Obtain a code signing certificate (not SSL) from a trusted CA 2. Convert to `.pfx` if needed: ```sh openssl pkcs12 -export -in cert.cer -inkey private.key -out cert.pfx ``` 3. Import into Windows certificate store and find the thumbprint via `certmgr.msc` 4. Configure in `tauri.conf.json`: ```json { "bundle": { "windows": { "certificateThumbprint": "A1B2C3D4E5F6...", "digestAlgorithm": "sha256", "timestampUrl": "http://timestamp.comodoca.com" } } } ``` ### Method 2: Custom Sign Command (Azure, relic) For Azure Key Vault, Azure Trusted Signing, or other signing tools, use `signCommand`: ```json { "bundle": { "windows": { "signCommand": "trusted-signing-cli -e https://wus2.codesigning.azure.net -a MyAccount -c MyProfile -d \"My App\" %1" } } } ``` The `%1` placeholder is replaced with the file path to sign. **Azure Trusted Signing setup:** ```sh # Install the CLI tool cargo install trusted-signing-cli # Set Azure credentials export AZURE_CLIENT_ID="..." export AZURE_CLIENT_SECRET="..." export AZURE_TENANT_ID="..." ``` ### CI/CD Setup for Windows ```yaml # GitHub Actions -- import certificate before build - name: Import Windows certificate if: matrix.platform == 'windows-latest' env: WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }} WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} run: | $bytes = [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE) [IO.File]::WriteAllBytes("cert.pfx", $bytes) Import-PfxCertificate -FilePath cert.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString $env:WINDOWS_CERTIFICATE_PASSWORD -AsPlainText -Force) Remove-Item cert.pfx ``` **Key points:** - The certificate must be in the Windows certificate store before building - `timestampUrl` ensures the signature remains valid after the certificate expires - EV certificates provide immediate SmartScreen trust; OV certificates build reputation over time --- ## Common Signing Pitfalls - **macOS: "certificate not found"** -- The signing identity string must match exactly. Run `security find-identity -v -p codesigning` to verify. - **macOS: notarization stuck** -- Notarization can take 5-60 minutes. The Tauri CLI waits automatically but CI may time out. - **macOS: free account** -- Free Apple Developer accounts cannot notarize. A paid $99/year account is required. - **macOS: private key lost** -- The private key download from Apple is only available once. Export from Keychain before it is lost. - **Windows: "certificate not trusted"** -- The certificate must be from a CA trusted by Windows. Self-signed certificates will not work. - **Windows: SmartScreen warning** -- New OV certificates have no reputation. Microsoft offers a manual review process, or use an EV certificate. - **Timestamping:** Always configure a timestamp URL. Without it, signatures become invalid when the certificate expires. --- See [core.md](core.md) for bundle configuration and [ci-cd.md](ci-cd.md) for the full GitHub Actions workflow. -
core.md 9.3 KB
# Tauri Bundling - Core Patterns > Bundle configuration, platform-specific settings, size optimization, sidecars, and NSIS customization. See [SKILL.md](../SKILL.md) for decision frameworks. See [reference.md](../reference.md) for config field reference. --- ## Bundle Configuration ### Complete tauri.conf.json Bundle Section ```json { "productName": "My App", "version": "1.0.0", "identifier": "com.mycompany.myapp", "build": { "devUrl": "http://localhost:5173", "frontendDist": "../dist" }, "bundle": { "active": true, "targets": "all", "icon": [ "icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico" ], "resources": { "locales/*": "locales/", "data/defaults.json": "data/" }, "createUpdaterArtifacts": true, "windows": { "certificateThumbprint": null, "digestAlgorithm": "sha256", "timestampUrl": "http://timestamp.digicert.com", "webviewInstallMode": { "type": "embedBootstrapper" }, "nsis": { "installMode": "both", "displayLanguageSelector": true } }, "macOS": { "signingIdentity": null, "entitlements": null, "minimumSystemVersion": "10.13", "frameworks": [] }, "linux": { "deb": { "depends": ["libwebkit2gtk-4.1-0"], "section": "utils" }, "appimage": { "bundleMediaFramework": false } } } } ``` **Key points:** - `identifier` must be unique reverse-domain -- used for app data paths, code signing, and store submissions - Generate icons from a single 1024x1024 PNG: `cargo tauri icon path/to/icon.png` - `createUpdaterArtifacts: true` produces `.sig` signature files alongside each installer - `resources` bundles extra files accessible at runtime via Tauri path resolver - Set `certificateThumbprint` and `signingIdentity` to `null` during development, configure for release via env vars or CI --- ## Platform-Specific Targets ### Building for Specific Formats ```sh # Build all formats for current platform cargo tauri build # Build specific format(s) cargo tauri build --bundles nsis cargo tauri build --bundles deb,appimage # Debug build (faster compile, includes debug symbols) cargo tauri build --debug # Cross-compile NSIS installer from Linux/macOS cargo tauri build --runner cargo-xwin --target x86_64-pc-windows-msvc --bundles nsis # Build for specific architecture cargo tauri build --target aarch64-apple-darwin cargo tauri build --target x86_64-apple-darwin ``` ### Targeting Specific Formats in Config ```json { "bundle": { "targets": ["nsis", "msi"] } } ``` **Trade-offs by format:** - **NSIS** (Windows): Recommended. Supports `perUser`/`perMachine`/`both` modes, custom hooks, language selector. Cross-compilable via `cargo-xwin`. - **MSI** (Windows): WiX-based, Windows-only build. Better for enterprise deployment with Group Policy. - **DMG** (macOS): Standard drag-to-install disk image. Requires code signing + notarization for distribution. - **deb** (Linux): Small package (~2-6 MB), relies on system packages. Ideal for Debian/Ubuntu. - **rpm** (Linux): Small package, for Fedora/RHEL/openSUSE. - **AppImage** (Linux): Portable (~70+ MB), bundles all dependencies. No installation needed. --- ## Binary Size Optimization ### Rust Release Profile ```toml # src-tauri/Cargo.toml [profile.release] codegen-units = 1 # Better optimization (slower compile) lto = true # Link-time optimization opt-level = "s" # Optimize for size ("z" may also work -- test both) panic = "abort" # Remove panic unwinding code strip = true # Remove debug symbols ``` **Impact:** These settings typically reduce binary size by 30-50% but increase clean build time to 10-30 minutes. ### Nightly Toolchain Additions (Optional) ```toml # Only with nightly Rust toolchain [profile.release] trim-paths = "all" ``` ### Frontend Bundle Size The frontend JavaScript bundle is embedded in the binary. Minimize it: - Use a bundle analyzer to identify large dependencies - Tree-shake unused code (most bundlers do this by default) - Consider lighter alternatives for heavy dependencies - Lazy-load routes and heavy components ### Remove Unused Commands (Tauri 2.4+) ```json { "build": { "removeUnusedCommands": true } } ``` This strips commands not listed in your capability files from the binary. Ensure all needed commands are explicitly allowed in your ACL. --- ## Sidecar Binaries ### Configuration ```json { "bundle": { "externalBin": ["binaries/ffmpeg"] } } ``` ### File Naming Convention Sidecar filenames must include the Rust target triple. Tauri resolves the correct one at runtime. ``` binaries/ffmpeg-x86_64-pc-windows-msvc.exe binaries/ffmpeg-x86_64-apple-darwin binaries/ffmpeg-x86_64-unknown-linux-gnu binaries/ffmpeg-aarch64-apple-darwin ``` ### Executing a Sidecar from Rust ```rust use tauri_plugin_shell::ShellExt; #[tauri::command] async fn run_sidecar(app: tauri::AppHandle, input: String) -> Result<String, String> { let output = app.shell() .sidecar("ffmpeg") .map_err(|e| e.to_string())? .args(["-i", &input, "-o", "output.mp4"]) .output() .await .map_err(|e| e.to_string())?; if output.status.success() { Ok(String::from_utf8_lossy(&output.stdout).to_string()) } else { Err(String::from_utf8_lossy(&output.stderr).to_string()) } } ``` **Key points:** - The shell plugin (`tauri-plugin-shell`) is required for sidecar execution - Add `shell:allow-execute` or `shell:allow-spawn` permission to your capability file - The `externalBin` path is relative to `src-tauri/` and omits the target triple suffix --- ## NSIS Installer Customization ### Install Modes ```json { "bundle": { "windows": { "nsis": { "installMode": "both" } } } } ``` - `"perUser"` (default): No admin required, installs to `%LOCALAPPDATA%` - `"perMachine"`: Requires admin, installs to `Program Files` - `"both"`: User chooses at install time, requires admin ### Installer Hooks Create a `.nsh` file with NSIS macros for the four lifecycle hooks: ```nsis ; hooks.nsh !macro NSIS_HOOK_PREINSTALL ; Runs before copying files, registry keys, and shortcuts DetailPrint "Preparing installation..." !macroend !macro NSIS_HOOK_POSTINSTALL ; Runs after all installation steps complete ; Example: register file association WriteRegStr HKCU "Software\Classes\.myext" "" "MyApp.Document" WriteRegStr HKCU "Software\Classes\MyApp.Document\shell\open\command" "" '"$INSTDIR\MyApp.exe" "%1"' !macroend !macro NSIS_HOOK_PREUNINSTALL ; Runs before removing files and registry entries DetailPrint "Cleaning up..." !macroend !macro NSIS_HOOK_POSTUNINSTALL ; Runs after removal operations finish DeleteRegKey HKCU "Software\Classes\.myext" DeleteRegKey HKCU "Software\Classes\MyApp.Document" !macroend ``` Reference the hook file in config: ```json { "bundle": { "windows": { "nsis": { "installerHooks": "hooks.nsh" } } } } ``` ### Custom NSIS Template For complete control over the installer, provide a custom `.nsi` template: ```json { "bundle": { "windows": { "nsis": { "template": "custom-installer.nsi" } } } } ``` The template uses Handlebars syntax. Tauri injects variables from your `tauri.conf.json` into the template at build time. --- ## Resources and Extra Files ### Bundling Static Files ```json { "bundle": { "resources": { "assets/*": "assets/", "config/defaults.json": "config/", "models/weights.bin": "models/" } } } ``` ### Accessing Resources at Runtime (Rust) ```rust use tauri::Manager; #[tauri::command] fn read_default_config(app: tauri::AppHandle) -> Result<String, String> { let resource_path = app.path() .resolve("config/defaults.json", tauri::path::BaseDirectory::Resource) .map_err(|e| e.to_string())?; std::fs::read_to_string(resource_path).map_err(|e| e.to_string()) } ``` **Key point:** Resource paths are resolved relative to the bundle's resource directory. In development (`cargo tauri dev`), they resolve relative to `src-tauri/`. --- ## Snap and Flatpak (Manual Setup) Snap and Flatpak are not built by `cargo tauri build`. They require manual manifest files. ### Snap Packaging Requires a `snapcraft.yaml` manifest: ```yaml name: my-app base: core22 version: "1.0.0" summary: My Tauri app description: | A desktop application built with Tauri. grade: stable confinement: strict apps: my-app: command: usr/bin/my-app desktop: usr/share/applications/my-app.desktop extensions: [gnome] parts: my-app: plugin: dump source: src-tauri/target/release/bundle/deb/my-app_1.0.0_amd64.deb source-type: deb ``` Build: `sudo snapcraft` ### Flatpak Packaging Requires a Flatpak manifest (e.g., `com.mycompany.myapp.yml`). Flatpak runs in a sandbox -- DBus communication must be explicitly declared. **Key constraints for sandboxed packages:** - Most DBus service communication is blocked by default - Declare `--talk-name` and `--own-name` finish args for needed services - Tray icon, notifications, and single-instance plugins may need additional manifest configuration --- See [code-signing.md](code-signing.md) for signing setup, [updater.md](updater.md) for auto-update configuration, and [ci-cd.md](ci-cd.md) for GitHub Actions workflows. -
updater.md 8.4 KB
# Tauri Bundling - Auto-Updater > Auto-updater plugin setup, Ed25519 key generation, endpoint format, JS and Rust usage, CI integration. See [SKILL.md](../SKILL.md) for decision frameworks. See [reference.md](../reference.md) for endpoint response format and platform target strings. --- ## Setup ### Install the Plugin ```sh # Using Tauri CLI (registers in Cargo.toml + lib.rs) cargo tauri add updater # Or manually: cargo add tauri-plugin-updater npm add @tauri-apps/plugin-updater ``` ### Register in Rust ```rust // src-tauri/src/lib.rs #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_updater::Builder::new().build()) .run(tauri::generate_context!()) .expect("error while running tauri application"); } ``` **Note:** The updater is desktop-only. Use the `#[cfg(desktop)]` gate if your app targets both desktop and mobile: ```rust #[cfg(desktop)] app.handle().plugin(tauri_plugin_updater::Builder::new().build()); ``` ### Add Permissions ```json { "permissions": ["updater:default"] } ``` Individual permissions: `updater:allow-check`, `updater:allow-download`, `updater:allow-install`, `updater:allow-download-and-install`. --- ## Key Generation Generate an Ed25519 (Minisign) key pair: ```sh cargo tauri signer generate -w ~/.tauri/myapp.key ``` This produces: - **Private key** -- store securely, never commit to source control - **Public key** -- safe to commit, goes in `tauri.conf.json` ### Build-Time Environment Variables Set before running `cargo tauri build`: ```sh # Path to key file or raw key content export TAURI_SIGNING_PRIVATE_KEY="~/.tauri/myapp.key" # Optional password (if key is password-protected) export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="" ``` **CRITICAL:** Without `TAURI_SIGNING_PRIVATE_KEY`, the build will not generate `.sig` signature files even if `createUpdaterArtifacts: true` is set. --- ## Configuration ### tauri.conf.json ```json { "bundle": { "createUpdaterArtifacts": true }, "plugins": { "updater": { "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6...", "endpoints": [ "https://releases.example.com/{{target}}/{{arch}}/{{current_version}}" ] } } } ``` **Template variables** (resolved at runtime): - `{{current_version}}` -- app version from `tauri.conf.json` - `{{target}}` -- `linux`, `windows`, or `darwin` - `{{arch}}` -- `x86_64`, `aarch64`, `i686`, `armv7` ### Windows Update Behavior ```json { "plugins": { "updater": { "windows": { "installMode": "passive" } } } } ``` - `"passive"` (default): Shows progress bar, no user interaction - `"basicUi"`: Shows basic installer UI - `"quiet"`: Silent installation --- ## Build Artifacts When `createUpdaterArtifacts: true` and `TAURI_SIGNING_PRIVATE_KEY` is set: | Platform | Installer | Signature File | | -------- | ------------------------- | ------------------------------ | | Linux | `.AppImage` | `.AppImage.sig` | | macOS | `.app` (as `.app.tar.gz`) | `.app.tar.gz.sig` | | Windows | `-setup.exe` or `.msi` | `-setup.exe.sig` or `.msi.sig` | --- ## JavaScript Usage ### Check and Install Updates ```typescript import { check } from "@tauri-apps/plugin-updater"; import { relaunch } from "@tauri-apps/plugin-process"; async function checkForUpdates(): Promise<void> { const update = await check(); if (!update) { console.log("No update available"); return; } console.log(`Update available: ${update.version}`); console.log(`Release notes: ${update.body}`); await update.downloadAndInstall((event) => { switch (event.event) { case "Started": console.log( `Downloading ${event.data.contentLength ?? "unknown"} bytes`, ); break; case "Progress": console.log(`Downloaded chunk: ${event.data.chunkLength} bytes`); break; case "Finished": console.log("Download complete, installing..."); break; } }); // Restart the app to apply the update await relaunch(); } ``` ### Runtime Configuration (JS) ```typescript import { check } from "@tauri-apps/plugin-updater"; const update = await check({ proxy: "https://proxy.example.com", timeout: 30000, headers: { Authorization: "Bearer token", }, target: "macos-universal", }); ``` --- ## Rust Usage ### Check and Install from Backend ```rust use tauri_plugin_updater::UpdaterExt; #[tauri::command] async fn check_for_update(app: tauri::AppHandle) -> Result<Option<String>, String> { let update = app.updater() .map_err(|e| e.to_string())? .check() .await .map_err(|e| e.to_string())?; match update { Some(update) => { let version = update.version.clone(); update.download_and_install( |chunk_length, content_length| { println!("downloaded {chunk_length} of {content_length:?}"); }, || { println!("download finished"); }, ).await.map_err(|e| e.to_string())?; Ok(Some(version)) } None => Ok(None), } } ``` ### Runtime Configuration (Rust) ```rust use tauri_plugin_updater::UpdaterExt; // Custom endpoints at runtime let update = app.updater_builder() .endpoints(vec!["https://custom.endpoint.com/update/{{target}}/{{arch}}/{{current_version}}".parse().unwrap()])? .build()? .check() .await?; // Custom target (e.g., universal macOS binary) let update = app.updater_builder() .target("macos-universal") .build()? .check() .await?; // Custom version comparator (allow downgrades) let update = app.updater_builder() .version_comparator(|current, update| update.version != current) .build()? .check() .await?; ``` --- ## Update Endpoint ### Static JSON (GitHub Releases, S3) Host a JSON file per platform, or a single JSON with all platforms: ```json { "version": "1.2.0", "notes": "Bug fixes and improvements", "pub_date": "2025-06-15T00:00:00Z", "platforms": { "linux-x86_64": { "signature": "dW50cnVzdGVkIGNvbW1lbnQ6...", "url": "https://github.com/myorg/myapp/releases/download/v1.2.0/my-app_1.2.0_amd64.AppImage" }, "darwin-aarch64": { "signature": "dW50cnVzdGVkIGNvbW1lbnQ6...", "url": "https://github.com/myorg/myapp/releases/download/v1.2.0/my-app.app.tar.gz" }, "windows-x86_64": { "signature": "dW50cnVzdGVkIGNvbW1lbnQ6...", "url": "https://github.com/myorg/myapp/releases/download/v1.2.0/my-app_1.2.0_x64-setup.exe" } } } ``` Required fields: `version`, `platforms.[target].url`, `platforms.[target].signature`. ### Dynamic Endpoint A server that returns HTTP 204 (no update) or HTTP 200 with: ```json { "version": "1.2.0", "url": "https://cdn.example.com/my-app-setup.exe", "signature": "dW50cnVzdGVkIGNvbW1lbnQ6...", "notes": "Bug fixes", "pub_date": "2025-06-15T00:00:00Z" } ``` Required fields: `version`, `url`, `signature`. ### Using GitHub Releases with tauri-action The `tauri-action` automatically generates the update JSON when configured: ```yaml - uses: tauri-apps/tauri-action@v0 with: tagName: app-v__VERSION__ releaseName: "App v__VERSION__" updaterJsonPreferNsis: true # Prefer NSIS over MSI for Windows updates ``` The action creates a `latest.json` artifact compatible with the static endpoint format. Point your `endpoints` config at the release asset URL. --- ## Common Updater Pitfalls - **Missing `TAURI_SIGNING_PRIVATE_KEY`** at build time -- `.sig` files are not generated, updates will fail signature verification - **Lost private key** -- Users with the app already installed cannot receive updates. Generate a new key pair and distribute a fresh build. - **Windows: app exits during update** -- This is expected behavior on Windows. The installer takes over and restarts the app. - **HTTP endpoints** -- Set `dangerousInsecureTransportProtocol: true` for non-HTTPS endpoints (not recommended for production) - **Version format** -- Must be valid semver. The updater compares versions; non-semver strings cause parse errors. - **macOS update artifact** -- The updater expects `.app.tar.gz` (not `.dmg`). The build produces this automatically. --- See [core.md](core.md) for bundle configuration, [code-signing.md](code-signing.md) for signing setup, and [ci-cd.md](ci-cd.md) for the GitHub Actions workflow.
-
-
reference.md 9 KB
# Tauri Bundling & Distribution Reference > Quick-lookup tables for bundle configuration, CLI commands, and platform targets. See [SKILL.md](SKILL.md) for decision frameworks and red flags. See [examples/](examples/) for full code examples. --- ## Build CLI Commands | Command | Purpose | | ------------------------------------------------ | ------------------------------------------- | | `cargo tauri build` | Build production binary + installers | | `cargo tauri build --debug` | Debug build (faster compile, larger binary) | | `cargo tauri build --bundles nsis` | Build specific installer format | | `cargo tauri build --bundles deb,appimage` | Build multiple formats | | `cargo tauri build --no-bundle` | Build binary without packaging | | `cargo tauri build --target x86_64-apple-darwin` | Build for specific Rust target | | `cargo tauri icon path/to/1024.png` | Generate all icon sizes from source image | | `cargo tauri signer generate -w key.key` | Generate Ed25519 key pair for updater | --- ## Bundle Config Field Reference ### Top-Level Bundle Fields | Field | Type | Default | Purpose | | ------------------------------- | ---------- | ------- | -------------------------------------------- | | `bundle.active` | `boolean` | `true` | Enable/disable bundling | | `bundle.targets` | `string[]` | `"all"` | Installer formats to build | | `bundle.identifier` | `string` | -- | Reverse-domain app ID (required) | | `bundle.icon` | `string[]` | `[]` | Icon file paths | | `bundle.resources` | `object` | -- | Extra files/dirs to include in bundle | | `bundle.externalBin` | `string[]` | -- | Sidecar binary paths (without target triple) | | `bundle.createUpdaterArtifacts` | `boolean` | `false` | Generate `.sig` files for updater | ### Windows Bundle Fields | Field | Purpose | | --------------------------------------------- | ---------------------------------------- | | `bundle.windows.certificateThumbprint` | Code signing certificate thumbprint | | `bundle.windows.digestAlgorithm` | Signing digest (typically `"sha256"`) | | `bundle.windows.timestampUrl` | Timestamp server URL | | `bundle.windows.signCommand` | Custom sign command (Azure, relic, etc.) | | `bundle.windows.nsis.installMode` | `"perUser"` / `"perMachine"` / `"both"` | | `bundle.windows.nsis.installerHooks` | Path to `.nsh` hook file | | `bundle.windows.nsis.template` | Path to custom NSIS template | | `bundle.windows.nsis.displayLanguageSelector` | Show language picker | | `bundle.windows.nsis.languages` | NSIS language list | | `bundle.windows.nsis.minimumWebview2Version` | Minimum WebView2 runtime version | | `bundle.windows.webviewInstallMode` | WebView2 bundling strategy | ### macOS Bundle Fields | Field | Purpose | | ----------------------------------- | ------------------------------------------- | | `bundle.macOS.signingIdentity` | Code signing identity (or `"-"` for ad-hoc) | | `bundle.macOS.entitlements` | Path to entitlements plist | | `bundle.macOS.frameworks` | Native frameworks to bundle | | `bundle.macOS.minimumSystemVersion` | Minimum macOS version (default `"10.13"`) | ### Linux Bundle Fields | Field | Purpose | | -------------------------------------------- | ------------------------------------ | | `bundle.linux.deb.depends` | Debian package dependencies | | `bundle.linux.deb.section` | Package section (e.g., `"utils"`) | | `bundle.linux.appimage.bundleMediaFramework` | Include GStreamer for media playback | | `bundle.linux.appimage.files` | Extra files to include in AppImage | | `bundle.linux.rpm.epoch` | RPM epoch number | | `bundle.linux.rpm.release` | RPM release string | --- ## WebView2 Install Modes | Mode | Internet | Size Impact | Use Case | | ---------------------- | -------- | ----------- | ------------------------------- | | `downloadBootstrapper` | Yes | +0 MB | Default, smallest installer | | `embedBootstrapper` | Yes | +1.8 MB | Better Windows 7 support | | `offlineInstaller` | No | +127 MB | Offline/air-gapped environments | | `fixedVersion` | No | +180 MB | Controlled runtime management | | `skip` | No | +0 MB | Assumes runtime pre-installed | --- ## NSIS Install Modes | Mode | Admin Required | Install Path | Use Case | | ------------ | -------------- | ---------------- | ---------------------- | | `perUser` | No | `%LOCALAPPDATA%` | Default, no UAC prompt | | `perMachine` | Yes | `Program Files` | Enterprise/system-wide | | `both` | Yes | User chooses | Flexible installer | --- ## Code Signing Environment Variables ### macOS | Variable | Purpose | | ---------------------------- | -------------------------------------- | | `APPLE_CERTIFICATE` | Base64-encoded `.p12` certificate | | `APPLE_CERTIFICATE_PASSWORD` | Password for the `.p12` file | | `APPLE_SIGNING_IDENTITY` | Signing identity string | | `APPLE_API_ISSUER` | App Store Connect API issuer ID | | `APPLE_API_KEY` | App Store Connect API key ID | | `APPLE_API_KEY_PATH` | Path to `.p8` API key file | | `APPLE_ID` | Apple account email (alternate auth) | | `APPLE_PASSWORD` | App-specific password (alternate auth) | | `APPLE_TEAM_ID` | Apple Developer team ID | ### Windows | Variable | Purpose | | ------------------------------ | --------------------------------- | | `WINDOWS_CERTIFICATE` | Base64-encoded `.pfx` certificate | | `WINDOWS_CERTIFICATE_PASSWORD` | Password for the `.pfx` file | ### Updater | Variable | Purpose | | ------------------------------------ | ------------------------------------- | | `TAURI_SIGNING_PRIVATE_KEY` | Ed25519 private key (path or content) | | `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` | Private key password (optional) | --- ## Updater Endpoint Response Format ### Static JSON (GitHub Releases, S3) ```json { "version": "1.2.0", "notes": "Bug fixes and performance improvements", "pub_date": "2025-01-15T00:00:00Z", "platforms": { "linux-x86_64": { "signature": "MINISIGN_SIGNATURE", "url": "https://cdn.example.com/app_1.2.0_amd64.AppImage" }, "windows-x86_64": { "signature": "MINISIGN_SIGNATURE", "url": "https://cdn.example.com/app_1.2.0_x64-setup.exe" }, "darwin-x86_64": { "signature": "MINISIGN_SIGNATURE", "url": "https://cdn.example.com/app.app.tar.gz" }, "darwin-aarch64": { "signature": "MINISIGN_SIGNATURE", "url": "https://cdn.example.com/app-aarch64.app.tar.gz" } } } ``` ### Dynamic Server Response - **No update available:** HTTP 204 (No Content) - **Update available:** HTTP 200 with: ```json { "version": "1.2.0", "url": "https://cdn.example.com/bundle", "signature": "MINISIGN_SIGNATURE", "notes": "Release notes", "pub_date": "2025-01-15T00:00:00Z" } ``` Required fields: `version`, `url`, `signature`. --- ## Platform Target Strings (Updater) | `{{target}}` | `{{arch}}` | Platform | | ------------ | ---------- | ------------------- | | `linux` | `x86_64` | Linux x64 | | `linux` | `aarch64` | Linux ARM64 | | `windows` | `x86_64` | Windows x64 | | `windows` | `i686` | Windows x86 | | `windows` | `aarch64` | Windows ARM64 | | `darwin` | `x86_64` | macOS Intel | | `darwin` | `aarch64` | macOS Apple Silicon | --- ## See Also - [Tauri v2 Distribute Guide](https://v2.tauri.app/distribute/) - [Tauri v2 Config Reference](https://v2.tauri.app/reference/config/) - [Tauri GitHub Action](https://github.com/tauri-apps/tauri-action) - [Tauri App Size Guide](https://v2.tauri.app/concept/size/) -
SKILL.md 16.5 KB
--- name: desktop-packaging-tauri description: Tauri 2.x bundling, code signing, auto-updater, platform installers, CI/CD --- # Tauri 2.x Bundling & Distribution > **Quick Guide:** Configure bundling in `tauri.conf.json` under `bundle`. Platform targets: NSIS/MSI (Windows), DMG/app bundle (macOS), deb/rpm/AppImage (Linux). Code signing is required for macOS distribution (Apple notarization) and recommended for Windows (SmartScreen). The auto-updater uses Ed25519 (Minisign) signatures -- generate keys with `cargo tauri signer generate`, set `TAURI_SIGNING_PRIVATE_KEY` at build time. Cross-platform CI uses `tauri-apps/tauri-action` with a matrix strategy. Optimize binary size with `[profile.release]` settings in `Cargo.toml`. > > **Current version:** Tauri 2.x (stable). Updater artifacts use `createUpdaterArtifacts: true` (not the v1 `"v1Compatible"` unless migrating). --- <critical_requirements> ## CRITICAL: Before Using This Skill > **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants) **(You MUST set `TAURI_SIGNING_PRIVATE_KEY` as an environment variable at build time for updater artifacts -- never commit the private key)** **(You MUST configure code signing for macOS distribution -- unsigned apps are blocked by Gatekeeper)** **(You MUST use `bundle.identifier` as a valid reverse-domain string -- it is used for code signing, app data paths, and store submissions)** **(You MUST build platform-specific installers on their native OS -- cross-compilation is limited to NSIS via `cargo-xwin`)** **(You MUST set `createUpdaterArtifacts: true` in `bundle` to generate `.sig` files alongside installers)** </critical_requirements> --- **Auto-detection:** tauri.conf.json bundle, cargo tauri build, bundle targets, NSIS, MSI, DMG, AppImage, deb, rpm, code signing, notarization, APPLE_SIGNING_IDENTITY, certificateThumbprint, tauri-plugin-updater, createUpdaterArtifacts, TAURI_SIGNING_PRIVATE_KEY, sidecar, externalBin, tauri-action, cargo tauri signer, Minisign, installer hooks **When to use:** - Configuring `tauri.conf.json` bundle settings (targets, icons, resources, identifier) - Building platform-specific installers (NSIS, MSI, DMG, deb, rpm, AppImage) - Setting up macOS code signing and Apple notarization - Setting up Windows code signing (OV/EV certificates, Azure Trusted Signing) - Configuring the auto-updater plugin with Ed25519 signature verification - Optimizing Tauri app binary size (Rust release profile, frontend bundle) - Bundling sidecar binaries or extra resources - Creating GitHub Actions CI/CD for cross-platform builds - Customizing NSIS installers with hooks or templates **When NOT to use:** - Tauri command/IPC bridge, permissions, plugins, window management (use the Tauri framework skill) - Frontend framework or build tool configuration (separate skills) - General Rust programming or Cargo configuration not specific to Tauri bundling - Mobile distribution to App Store / Google Play (different workflow) **Key patterns covered:** - Bundle configuration in `tauri.conf.json` ([examples/core.md](examples/core.md)) - Platform-specific installer options ([examples/core.md](examples/core.md)) - macOS code signing and notarization ([examples/code-signing.md](examples/code-signing.md)) - Windows code signing ([examples/code-signing.md](examples/code-signing.md)) - Auto-updater setup with Ed25519 signatures ([examples/updater.md](examples/updater.md)) - Binary size optimization ([examples/core.md](examples/core.md)) - Sidecar binaries and resources ([examples/core.md](examples/core.md)) - GitHub Actions cross-platform CI/CD ([examples/ci-cd.md](examples/ci-cd.md)) - NSIS installer hooks and customization ([examples/core.md](examples/core.md)) **Detailed resources:** - [examples/core.md](examples/core.md) - Bundle config, platform targets, size optimization, sidecars, NSIS hooks - [examples/code-signing.md](examples/code-signing.md) - macOS notarization, Windows signing, CI/CD signing setup - [examples/updater.md](examples/updater.md) - Auto-updater plugin, key generation, endpoint format, JS/Rust usage - [examples/ci-cd.md](examples/ci-cd.md) - GitHub Actions workflow, matrix strategy, secrets - [reference.md](reference.md) - Bundle config field reference, CLI commands, platform target table --- <philosophy> ## Philosophy Tauri's bundling system produces small, native installers by leveraging the OS system webview instead of bundling a browser engine. The typical binary is 5-15 MB compared to 150+ MB for alternatives. Distribution requires platform-specific steps: code signing and notarization for macOS, SmartScreen-friendly signing for Windows, and package manager formats for Linux. **The bundling workflow:** 1. Configure `tauri.conf.json` bundle section (identifier, icons, targets) 2. Set up code signing for target platforms 3. Configure the updater plugin with Ed25519 keys 4. Build with `cargo tauri build` (produces installer + `.sig` files) 5. Distribute via CI/CD pipeline with platform matrix **Key constraints:** - Cross-compilation is limited -- build macOS on macOS, Windows on Windows (NSIS is the exception via `cargo-xwin`) - Code signing requires platform-specific certificates and accounts (Apple Developer, Windows code signing cert) - The updater requires Ed25519 signatures -- this cannot be disabled - AppImage bundles all dependencies (~70+ MB) while deb/rpm rely on system packages (~2-6 MB) </philosophy> --- <patterns> ## Core Patterns ### Pattern 1: Bundle Configuration The `bundle` section in `tauri.conf.json` controls all packaging behavior. The `identifier` is the most important field -- it must be a unique reverse-domain string used for app data paths, code signing, and store submissions. ```json { "bundle": { "active": true, "targets": "all", "identifier": "com.mycompany.myapp", "icon": [ "icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico" ], "resources": { "locales/*": "locales/" }, "createUpdaterArtifacts": true } } ``` **Key points:** `"targets": "all"` builds all formats for the current OS. Use `cargo tauri icon path/to/1024x1024.png` to generate all icon sizes. Set `createUpdaterArtifacts: true` to produce `.sig` signature files alongside installers. See [examples/core.md](examples/core.md) for full config with platform-specific sections. --- ### Pattern 2: Platform-Specific Installers Each platform has distinct installer formats with different trade-offs. | Platform | Format | Output | Size | Notes | | -------- | ---------- | ------------- | ------- | -------------------------- | | Windows | `nsis` | `-setup.exe` | ~2-5 MB | Recommended, cross-compile | | Windows | `msi` | `.msi` | ~2-5 MB | WiX, Windows-only build | | macOS | `dmg` | `.dmg` | ~5-8 MB | Drag-to-install disk image | | macOS | `app` | `.app` bundle | ~5-8 MB | Raw app, no installer | | Linux | `deb` | `.deb` | ~2-6 MB | Debian/Ubuntu | | Linux | `rpm` | `.rpm` | ~2-6 MB | Fedora/RHEL | | Linux | `appimage` | `.AppImage` | ~70+ MB | Portable, bundles all deps | ```sh # Build all formats for current platform cargo tauri build # Build specific format cargo tauri build --bundles nsis # Debug build (faster compile, larger binary) cargo tauri build --debug ``` **Key point:** You cannot build `.msi` on Linux or `.dmg` on Windows. NSIS is the only cross-compilable format (via `cargo-xwin`). See [examples/core.md](examples/core.md) for platform-specific config sections. --- ### Pattern 3: Code Signing macOS requires both code signing and notarization for distribution outside the App Store. Windows signing prevents SmartScreen warnings. **macOS** -- set environment variables for CI: - `APPLE_CERTIFICATE` (base64-encoded `.p12`) - `APPLE_CERTIFICATE_PASSWORD` - `APPLE_SIGNING_IDENTITY` - Notarization: `APPLE_API_ISSUER`, `APPLE_API_KEY`, `APPLE_API_KEY_PATH` (App Store Connect API) **Windows** -- configure in `tauri.conf.json` or use `signCommand`: ```json { "bundle": { "windows": { "certificateThumbprint": "YOUR_THUMBPRINT", "digestAlgorithm": "sha256", "timestampUrl": "http://timestamp.comodoca.com" } } } ``` **Key point:** macOS notarization is mandatory for apps distributed outside the App Store -- without it, Gatekeeper blocks the app. See [examples/code-signing.md](examples/code-signing.md) for full setup and CI integration. --- ### Pattern 4: Auto-Updater The updater plugin uses Ed25519 (Minisign) signatures to verify update authenticity. Signature verification cannot be disabled. ```sh # Generate key pair (store private key securely) cargo tauri signer generate -w ~/.tauri/myapp.key ``` ```json { "plugins": { "updater": { "pubkey": "YOUR_ED25519_PUBLIC_KEY", "endpoints": [ "https://releases.example.com/{{target}}/{{arch}}/{{current_version}}" ] } } } ``` **Key points:** Set `TAURI_SIGNING_PRIVATE_KEY` at build time (never commit it). The endpoint template variables `{{target}}`, `{{arch}}`, `{{current_version}}` are resolved at runtime. The server returns HTTP 204 for no update, HTTP 200 with update JSON for available updates. See [examples/updater.md](examples/updater.md) for endpoint response format and JS/Rust usage. --- ### Pattern 5: Binary Size Optimization Tauri binaries are already small (5-15 MB) but can be further optimized with Rust release profile settings. ```toml # src-tauri/Cargo.toml [profile.release] codegen-units = 1 lto = true opt-level = "s" panic = "abort" strip = true ``` | Setting | Impact | Trade-off | | ------------------- | ------------------- | ------------------------ | | `strip = true` | ~10-20% smaller | No debug symbols | | `lto = true` | ~10-20% smaller | Slower compile | | `opt-level = "s"` | Optimize for size | May be slower at runtime | | `codegen-units = 1` | Better optimization | Slower compile | | `panic = "abort"` | Smaller binary | No panic unwinding | **Key point:** These settings significantly increase compile time (10-30 min clean builds). Use `cargo tauri build --debug` during development. Tauri 2.4+ also supports `removeUnusedCommands: true` in `build` config. See [examples/core.md](examples/core.md) for frontend optimization tips. --- ### Pattern 6: Sidecar Binaries Bundle external executables that run alongside your app. Filenames must include the Rust target triple. ```json { "bundle": { "externalBin": ["binaries/ffmpeg"] } } ``` ``` binaries/ffmpeg-x86_64-pc-windows-msvc.exe binaries/ffmpeg-x86_64-apple-darwin binaries/ffmpeg-x86_64-unknown-linux-gnu binaries/ffmpeg-aarch64-apple-darwin ``` **Key point:** Tauri resolves the correct platform binary at runtime. The shell plugin (`tauri-plugin-shell`) is required for sidecar execution. See [examples/core.md](examples/core.md) for the Rust sidecar execution pattern. --- ### Pattern 7: NSIS Installer Customization Extend NSIS installers with hooks or replace the template entirely. Four lifecycle hooks: `NSIS_HOOK_PREINSTALL`, `NSIS_HOOK_POSTINSTALL`, `NSIS_HOOK_PREUNINSTALL`, `NSIS_HOOK_POSTUNINSTALL`. ```json { "bundle": { "windows": { "nsis": { "installerHooks": "hooks.nsh", "installMode": "both", "displayLanguageSelector": true } } } } ``` **Key point:** Use hooks for targeted changes (registry keys, file associations). Use a custom template (`nsis.template`) only if hooks are insufficient. See [examples/core.md](examples/core.md) for hook examples. </patterns> --- <decision_framework> ## Decision Framework ### Which Installer Format? ``` Target platform? |-- Windows | +-- Need MSI for enterprise deployment? -> msi (WiX, Windows-only build) | +-- General distribution? -> nsis (recommended, cross-compilable) |-- macOS | +-- App Store? -> app bundle + App Store signing | +-- Direct download? -> dmg + Developer ID + notarization |-- Linux | +-- Targeting Debian/Ubuntu? -> deb | +-- Targeting Fedora/RHEL? -> rpm | +-- Maximum portability? -> appimage (larger, ~70+ MB) | +-- Sandboxed distribution? -> snap or flatpak (manual setup) +-- All platforms? -> Use "all" target with CI matrix ``` ### Code Signing Decision ``` Distributing publicly? |-- macOS | +-- App Store? -> Apple Distribution certificate | +-- Direct download? -> Developer ID Application + notarization (REQUIRED) | +-- Internal/testing only? -> Ad-hoc signing (signingIdentity: "-") |-- Windows | +-- Microsoft Store? -> Store signing | +-- Direct download? -> OV or EV certificate (prevents SmartScreen warnings) | +-- Internal only? -> Optional but recommended +-- Linux +-- Code signing is not required for Linux distribution ``` ### Updater Strategy ``` Need auto-updates? |-- YES -> tauri-plugin-updater | +-- Simple static hosting? -> Static JSON endpoint (GitHub Releases, S3) | +-- Dynamic update logic? -> Dynamic endpoint (returns 200/204) | +-- Need update UI? -> JS-side check() + downloadAndInstall() | +-- Background updates? -> Rust-side updater with AppHandle +-- NO -> Skip updater config, omit createUpdaterArtifacts ``` </decision_framework> --- <red_flags> ## RED FLAGS **High Priority Issues:** - Committing `TAURI_SIGNING_PRIVATE_KEY` to source control -- store as CI secret, never in repo - Missing `createUpdaterArtifacts: true` when using the updater -- no `.sig` files generated, updates fail - Building macOS installer on Linux/Windows -- cross-compilation not supported for DMG/app bundle - Using `"targets": "all"` in CI without a matrix strategy -- builds all formats for the current OS only - Missing `bundle.identifier` or using an invalid format -- breaks code signing, app data paths, and store submissions - Distributing unsigned macOS app -- Gatekeeper blocks it, users cannot open it **Medium Priority Issues:** - AppImage on Ubuntu 22.04+ targeting older distros -- higher glibc requirement breaks compatibility - Missing `strip = true` in release profile -- debug symbols inflate binary by 10-20% - Using `opt-level = 3` instead of `"s"` or `"z"` when binary size matters -- optimizes for speed, not size - WebView2 `skip` install mode without guarantee runtime is present -- app crashes on startup - NSIS `perUser` mode when app needs system-wide installation -- installs to `%LOCALAPPDATA%`, not Program Files **Gotchas & Edge Cases:** - `opt-level = "s"` vs `"z"` -- sometimes `"z"` produces smaller binaries, sometimes `"s"` does. Test both. - macOS ad-hoc signing (`signingIdentity: "-"`) still triggers Gatekeeper warnings -- only useful for development - NSIS is the only format supporting cross-compilation from Linux/macOS to Windows (via `cargo-xwin`) - Sidecar binary filenames must include the Rust target triple suffix -- Tauri resolves the correct one at runtime - Updater endpoint template variables (`{{target}}`, `{{arch}}`, `{{current_version}}`) are Tauri-specific, not environment variables - AppImage bundles are ~70+ MB because they include all dependencies -- deb/rpm are 2-6 MB but require system packages - Windows WebView2 runtime is bundled by default with `embedBootstrapper` -- older `downloadBootstrapper` mode requires internet at install time - Snap/Flatpak packages run in a sandbox -- DBus communication is blocked unless declared in the manifest - `removeUnusedCommands: true` (Tauri 2.4+) strips commands not in capability files -- ensure all needed commands are listed in ACL - Free Apple Developer accounts cannot notarize apps -- a paid $99/year account is required for distribution </red_flags> --- <critical_reminders> ## CRITICAL REMINDERS > **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants) **(You MUST set `TAURI_SIGNING_PRIVATE_KEY` as an environment variable at build time for updater artifacts -- never commit the private key)** **(You MUST configure code signing for macOS distribution -- unsigned apps are blocked by Gatekeeper)** **(You MUST use `bundle.identifier` as a valid reverse-domain string -- it is used for code signing, app data paths, and store submissions)** **(You MUST build platform-specific installers on their native OS -- cross-compilation is limited to NSIS via `cargo-xwin`)** **(You MUST set `createUpdaterArtifacts: true` in `bundle` to generate `.sig` files alongside installers)** **Failure to follow these rules will produce unsigned binaries, missing update signatures, or broken cross-platform builds.** </critical_reminders>
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.