Build desktop distributables
Produce signed macOS, Windows, and Linux artifacts for a Pear Electron app, then assemble a multi-architecture deployment directory for pear stage.
This guide shows you how to produce per-OS distributables and assemble a deployment directory that pear stage can consume. It assumes you already ship with Electron tooling (for example Electron Forge) and a Pear release line; for the overall pipeline, read Release pipeline and Deploy your application.
Before you begin
- Node.js and npm on each build host (or CI runners) for the target OS.
pearCLI available (npx pear) for laterpear-build/pear stagesteps.- Branding fields filled in
package.json(name,productName,description,author,license) and icons underbuild/where your template expects them.
Checklist (all platforms)
Complete these before you cut binaries you will ship to others:
- Application metadata and icons are final for the brand you are releasing under.
- You know which release line link (development, staging, rc, etc.) this build will track — see Release pipeline.
- For macOS and Windows production paths, plan vendor signing up front (same certificate across builds for MSIX updates).
macOS
-
On a Mac, run your project's macOS make script (often
npm run make:darwinorelectron-forge make --platform=darwin). -
Unsigned local
.appbundles are fine for your machine; other Macs will quarantine unsigned builds. For OTA installs on other machines you need code signing and notarization with Apple credentials. -
Set the environment variables your template documents:
MAC_CODESIGN_IDENTITY=identity APPLE_TEAM_ID=teamid APPLE_ID=id APPLE_PASSWORD=pw npm run make:darwinAPPLE_PASSWORDis an app-specific password, not your Apple ID login password. -
Maintain
build/entitlements.mac.plistfor Hardened Runtime. Notarized apps require it; the defaults cover Bare native-addon compatibility:cs.allow-jit— required for V8/Bare JIT compilation.cs.allow-unsigned-executable-memory— required for the Bare runtime.
Add or remove entitlements here as needed (
com.apple.security.device.camera,com.apple.security.device.microphone, etc.). To load third-party native addons that dynamically link shared libraries built by a different developer, also addcs.disable-library-validation. -
If you are using
pear≤ v2.2.15, add apear.stage.includesentry that keeps.githubfolders so notarization does not break on stripped resources:{ "pear": { "stage": { "includes": [".github"] } } }
Follow the Electron Forge macOS code signing guide for credential setup.
Windows
-
Install Windows SDK and PowerShell 7+ on the build host.
-
Run your Windows make script (often
npm run make:win32orelectron-forge make --platform=win32). -
Without signing credentials,
@electron-forge/maker-msixgenerates a self-signed development certificate matching thePublisherfield inbuild/AppxManifest.xml. The certificate is cached locally and reused across builds on the same machine, but is not portable — building on a different machine or clearing the cert store generates a new one. -
Edit
build/AppxManifest.xmlsoName,Publisher,DisplayName, and the executable path are correct for your brand. Several of these are declared in multiple locations. -
Install with
Add-AppxPackage .\out\<AppName>-win32-x64\<AppName>.msix; uninstall withGet-AppxPackage -Name <AppName> | Remove-AppxPackage. -
For production MSIX, supply a persistent code signing certificate (
WINDOWS_CERTIFICATE_FILEandWINDOWS_CERTIFICATE_PASSWORD):WINDOWS_CERTIFICATE_FILE=path/to/cert.pfx WINDOWS_CERTIFICATE_PASSWORD=password npm run make:win32Keep the
Publisherfield inAppxManifest.xmlexactly equal to the certificate'sCN— Windows rejects MSIX updates when the publisher string drifts, breaking OTA. Use the same certificate across every release for the lifetime of the install base.
See Microsoft's MSIX signing documentation.
Linux
- On Linux (or a Linux CI runner), run your Linux make script (often
npm run make:linux). - Verify the produced AppImage (or your chosen format) launches on a clean VM before you fold it into the deployment directory.
Build the deployment directory
After you have one build per target architecture (darwin arm64/x64, linux arm64/x64, win32 x64, etc.), run pear-build to merge them into a single deployment directory (typically package.json + by-arch/.../app).
- Run
pear-buildoutside the application source tree, or pass--targetto a sibling folder — never nest the deployment folder inside a tree that will itself be staged, or sizes will balloon on every stage. See Troubleshoot desktop releases.
Example shape (paths illustrative only):
pear-build --package=./my-app/package.json \
--darwin-arm64-app ./out/MyApp-darwin-arm64/MyApp.app \
--darwin-x64-app ./out/MyApp-darwin-x64/MyApp.app \
--linux-arm64-app ./out/MyApp-linux-arm64/MyApp.AppImage \
--linux-x64-app ./out/MyApp-linux-x64/MyApp.AppImage \
--win32-x64-app ./out/MyApp-win32-x64/MyApp.msix \
--target my-app-1.2.3Then continue with dry-run and real pear stage as described in Deploy your application.
Where to go next
- Desktop release npm scripts — common
npmentry points in sample repos. - Troubleshoot desktop releases — OTA did not land,
pear stagesizes exploded, or keys were lost. - Release pipeline glossary — terminology for links, lines, and multisig.
Related documentation
Getting Started — Part 1: Chat · Part 2: Production-shaped · Part 3: Ship · Part 4: Update
Operating an app