Web to Desktop
Package & ship
Lesson 5 of 5
What you'll learn
- Choose between electron-builder and Electron Forge, and know what asar is
- Understand macOS signing + notarization and Windows signing in plain words
- Publish installers plus the feed file that powers auto-updates
Your project is a folder of JavaScript; users expect a .dmg or a Setup.exe. Two tools bridge that gap. Electron Forge is the official toolchain — plugin-based, with templates that wire Vite or webpack into dev and build. electron-builder is the long-standing community tool — one declarative config block, a wide matrix of installer targets, and electron-updater as its companion. Either is a fine choice; builder remains the common pick when auto-update and many targets matter.
// package.json (electron-builder)
"build": {
"appId": "com.example.notes",
"mac": { "target": ["dmg", "zip"], "notarize": true },
"win": { "target": "nsis" },
"linux": { "target": ["AppImage", "deb"] },
"publish": { "provider": "github" }
}
Both tools pack your source into an asar archive — a single file the app reads like a directory. It speeds up install and copy (thousands of files become one) and tidies the bundle, but it is not protection: asar is a plain, extractable format, so never ship secrets in it. The output per platform: a .dmg/.zip on macOS, an NSIS Setup.exe (or MSI) on Windows, AppImage/deb/rpm on Linux.
Signing, in plain words
Operating systems now assume unsigned software is suspicious, so shipping means proving who you are.
macOS is two steps. Signing: you buy an Apple Developer ID certificate and sign the app (with the hardened runtime enabled) so the OS knows who built it. Notarization: you upload the build to Apple, an automated scan checks it for malware, and Apple returns a ticket you staple to the app. Skip this and Gatekeeper tells users the app "can't be opened" — and, as lesson 4 noted, auto-update won't install into an unsigned app. Both tools run sign + notarize for you given credentials in env vars.
Windows uses Authenticode certificates. Unsigned apps still run, but SmartScreen shows a scary "unrecognized app" screen until your certificate builds reputation. Certificates now must live in hardware or a cloud signing service (Azure Trusted Signing is the budget-friendly route) rather than a loose .pfx file.
Sign in CI, not on laptops
Put certificates and Apple credentials in your CI provider's secret store and make releases a tagged-build pipeline. A release only a maintainer's laptop can produce is a bus-factor problem wearing a certificate.
Publishing updates
electron-builder --publish always uploads the installers plus a feed file (latest.yml, latest-mac.yml) to your provider — GitHub Releases, S3, or any static host. That feed is exactly what lesson 4's autoUpdater polls: version, file names, checksums. Ship 1.1.0 and every installed 1.0.0 finds it on next launch.
The challenge is a JS model of that handshake: a publisher writing releases into a feed, and an installed app doing the semver comparison electron-updater does.
Run it. publish() appends to the feed; checkForUpdates() compares semver against it — one app is stale, one is current.
What does macOS notarization actually do?
That's the full arc: two processes with a guarded bridge between them, a native shell, data that survives restarts, updates that install themselves, and signed installers on users' machines — your web skills, shipped as a real desktop app.
Saved on this device. Sign in to sync your progress everywhere.