feat(desktop): packaging + self-hosted auto-update (electron-builder/updater)
- package.json build config: NSIS installer, app icon, generic publish provider → https://remote.bizgaze.com/downloads/ (self-hosted update feed). - main.js: electron-updater checks the feed on launch + every 6h, downloads in the background, installs on restart. Active only in packaged builds. - build/icon.ico app icon; PACKAGING.md documents build/release/signing. - Key design: web/UI changes reach installed apps instantly (they load the live server); only native shell changes need an auto-updated build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
# Biz Connect Desktop — packaging & updates
|
||||
|
||||
## How updates reach installed apps (two kinds)
|
||||
|
||||
1. **Web / UI / feature / bug-fix changes → instant, no app update.**
|
||||
The app loads the live UI from `https://remote.bizgaze.com`. Deploy the server
|
||||
(`./deploy.sh`) and every installed desktop app has it on next open/reload. This is ~95%
|
||||
of all changes.
|
||||
2. **Native shell changes (`main.js` / `preload.js`) → auto-update.**
|
||||
Baked into the `.exe`. Shipped via **electron-updater** against a **self-hosted feed** on
|
||||
`https://remote.bizgaze.com/downloads/`. On launch (and every 6h) the app checks
|
||||
`latest.yml`, downloads a newer version in the background, and installs on next restart.
|
||||
|
||||
## Build an installer
|
||||
```bash
|
||||
cd desktop
|
||||
npm install
|
||||
npm run dist # electron-builder → dist/ (Win: NSIS .exe + latest.yml + .blockmap)
|
||||
```
|
||||
Output in `desktop/dist/`:
|
||||
- `Biz Connect Setup <version>.exe` — the installer
|
||||
- `latest.yml` — the update manifest electron-updater reads
|
||||
- `*.blockmap` — enables delta downloads
|
||||
|
||||
The release build points at production (`SERVER_URL` defaults to `https://remote.bizgaze.com`
|
||||
in `main.js`).
|
||||
|
||||
## Publish a release (self-hosted feed)
|
||||
1. Bump `version` in `desktop/package.json` (semver — electron-updater compares this).
|
||||
2. `npm run dist`.
|
||||
3. Upload **all** of `dist/` (the `.exe`, `latest.yml`, `.blockmap`) to whatever the server
|
||||
serves at `https://remote.bizgaze.com/downloads/`.
|
||||
- Behind Nginx Proxy Manager: point `/downloads/` at a static folder, or add a static
|
||||
route in the app. The files are large binaries — host them on disk/volume, **not** git.
|
||||
4. Installed apps pick it up within 6h (or on next launch).
|
||||
|
||||
> First release: users install the `.exe` manually (download link on your site). Every
|
||||
> release after that updates automatically.
|
||||
|
||||
## Code signing (add when the cert is ready)
|
||||
Unsigned installers work but trip Windows SmartScreen ("More info → Run anyway"). To sign:
|
||||
- **Azure Trusted Signing** (recommended): set `win.azureSignOptions` (or use the
|
||||
`@electron/windows-sign` path) with the Trusted Signing account/endpoint. Cloud, no token.
|
||||
- **EV/OV cert (.pfx or token)**: set env `CSC_LINK` (path to .pfx) + `CSC_KEY_PASSWORD`,
|
||||
or configure a hardware-token signing tool. electron-builder signs automatically.
|
||||
Once signing is on, auto-updates are silent (no SmartScreen).
|
||||
|
||||
## App identity
|
||||
`appId` = `com.bizgaze.connect.desktop`; the NSIS installer registers this as the
|
||||
AppUserModelID and creates a Start-menu shortcut — which is also the prerequisite for the
|
||||
**Phase D inline-reply Windows Toast notifications** (see ../CLIENTS.md).
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 264 KiB |
@@ -10,6 +10,12 @@
|
||||
// Server origin is configurable so the same build works against prod or a dev server.
|
||||
const { app, BrowserWindow, session, desktopCapturer, shell, Menu, ipcMain, nativeImage } = require('electron');
|
||||
const path = require('path');
|
||||
// Auto-update: only NATIVE shell changes (this .exe) need this — all web/UI changes arrive
|
||||
// live from the server. Checks the self-hosted feed (publish config in package.json →
|
||||
// https://remote.bizgaze.com/downloads/latest.yml), downloads in the background, and installs
|
||||
// on the next restart. No-op in dev (unpackaged).
|
||||
let autoUpdater = null;
|
||||
try { ({ autoUpdater } = require('electron-updater')); } catch (_) { /* not installed in dev */ }
|
||||
|
||||
// Renderer asks (via preload) to raise the window — e.g. when an OS notification is clicked.
|
||||
ipcMain.on('focus-window', () => {
|
||||
@@ -93,6 +99,12 @@ app.whenReady().then(() => {
|
||||
createWindow();
|
||||
Menu.setApplicationMenu(null); // hide the default menu bar; the web UI is the chrome
|
||||
app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); });
|
||||
// Check for shell updates on launch, then every 6 hours. Only in packaged builds.
|
||||
if (app.isPackaged && autoUpdater) {
|
||||
const check = () => autoUpdater.checkForUpdatesAndNotify().catch(() => {});
|
||||
check();
|
||||
setInterval(check, 6 * 60 * 60 * 1000);
|
||||
}
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); });
|
||||
|
||||
Generated
+98
-7
@@ -7,6 +7,9 @@
|
||||
"": {
|
||||
"name": "biz-connect-desktop",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"electron-updater": "^6.3.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^31.0.0",
|
||||
"electron-builder": "^24.13.3"
|
||||
@@ -906,7 +909,6 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"dev": true,
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/assert-plus": {
|
||||
@@ -1515,7 +1517,6 @@
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
@@ -1990,6 +1991,82 @@
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-updater": {
|
||||
"version": "6.8.9",
|
||||
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.8.9.tgz",
|
||||
"integrity": "sha512-ZhVxM9iGONUpZGI1FxdMRgJjUFXi7AYGVa5PwKlO1tV1/4zDxQmfKpXOHVztKrd6L9rLcFjERvi1Mf2vxyTkig==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"builder-util-runtime": "9.7.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"lazy-val": "^1.0.5",
|
||||
"lodash.escaperegexp": "^4.1.2",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"semver": "~7.7.3",
|
||||
"tiny-typed-emitter": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-updater/node_modules/builder-util-runtime": {
|
||||
"version": "9.7.0",
|
||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.7.0.tgz",
|
||||
"integrity": "sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"sax": "^1.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-updater/node_modules/fs-extra": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
||||
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-updater/node_modules/jsonfile": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
|
||||
"integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-updater/node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-updater/node_modules/universalify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
@@ -2476,7 +2553,6 @@
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
@@ -2768,7 +2844,6 @@
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
|
||||
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -2846,7 +2921,6 @@
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz",
|
||||
"integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lazystream": {
|
||||
@@ -2922,6 +2996,12 @@
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash.escaperegexp": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
|
||||
"integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.flatten": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
|
||||
@@ -2930,6 +3010,13 @@
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
|
||||
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
@@ -3116,7 +3203,6 @@
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
@@ -3475,7 +3561,6 @@
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
|
||||
"integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=11.0.0"
|
||||
@@ -3826,6 +3911,12 @@
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tiny-typed-emitter": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz",
|
||||
"integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tmp": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz",
|
||||
|
||||
+18
-4
@@ -7,6 +7,9 @@
|
||||
"start": "electron .",
|
||||
"dist": "electron-builder"
|
||||
},
|
||||
"dependencies": {
|
||||
"electron-updater": "^6.3.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^31.0.0",
|
||||
"electron-builder": "^24.13.3"
|
||||
@@ -14,9 +17,20 @@
|
||||
"build": {
|
||||
"appId": "com.bizgaze.connect.desktop",
|
||||
"productName": "Biz Connect",
|
||||
"files": ["main.js", "preload.js", "assets/**"],
|
||||
"win": { "target": "nsis" },
|
||||
"mac": { "target": "dmg", "category": "public.app-category.business" },
|
||||
"linux": { "target": "AppImage" }
|
||||
"directories": { "buildResources": "build", "output": "dist" },
|
||||
"publish": [
|
||||
{ "provider": "generic", "url": "https://remote.bizgaze.com/downloads/" }
|
||||
],
|
||||
"win": { "target": "nsis", "icon": "build/icon.ico" },
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"perMachine": false,
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"createDesktopShortcut": true,
|
||||
"createStartMenuShortcut": true,
|
||||
"shortcutName": "Biz Connect"
|
||||
},
|
||||
"mac": { "target": "dmg", "category": "public.app-category.business", "icon": "build/icon.ico" },
|
||||
"linux": { "target": "AppImage", "category": "Network" }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user