// electron-builder config for the VNCmail+ (Bulwark) desktop shell. // // Phase 1 of the VNCprodbuild rollout (~/.claude/skills/VNCprodbuild/SKILL.md): // step 1 - base config, no targets (superseded by this file) // step 6 - this file: real packaging targets + branding icon (below) // step 7 - this file's `publish` block + electron/main.ts's // setupAutoUpdater() - electron-updater against GitHub Releases. // step 9 - still open: code signing / notarization (Apple Developer ID, // optional Windows cert) - both are human-owned purchases, not // configured here. Builds below ship UNSIGNED. module.exports = { appId: "de.vnc.vncmailplus", productName: "VNCmail+", copyright: "Copyright © VNC AG", directories: { output: "dist-electron-builds", }, // The packaged app (`files` below) is plain esbuild-bundled JS - no native // node modules of its own. The one native dependency anywhere in the repo, // @signalapp/sqlcipher (used by lib/mail-index/**), ships its own prebuilt // .node binaries for every platform/arch and is copied in wholesale by // scripts/assemble-standalone.mjs as part of the extraResources standalone // bundle below - it is never rebuilt by electron-builder. // // Without this, electron-builder's default @electron/rebuild pass scans // the ENTIRE node_modules tree (not just what's actually packaged) for // anything with a native binding and tries to recompile it from source // against Electron's ABI via node-gyp. That caught @parcel/watcher - a // transitive devDependency of some dev tool, never shipped in this app - // and hard-failed the whole packaging step on any machine without a full // Xcode Command Line Tools install (`gyp: No Xcode or CLT version // detected!`), even though nothing that rebuild step touches is part of // the artifact. Verified by execution: builds failed with npmRebuild at // its default (true) and succeeded once set to false. npmRebuild: false, files: ["dist-electron/**/*", "package.json"], extraResources: [ { // Same artifact the Dockerfile bakes into the container image (see // Dockerfile + scripts/assemble-standalone.mjs). electron/main.ts // reads it from process.resourcesPath in packaged builds. // // Deliberately `from: ".next"` (not ".next/standalone") + a filter, // not the more obvious `from: ".next/standalone"` alone: // app-builder-lib's copy filter unconditionally drops a directory // literally named "node_modules" sitting at the copy root (see // node_modules/app-builder-lib/out/util/filter.js's // `relative === "node_modules"` check - it assumes extraResources are // hand-authored assets, not a pre-built server with a traced // node_modules of its own). Copying from one level up so // "standalone/node_modules" is never the literal copy root sidesteps // that check, so the standalone server's node_modules actually // survives into the packaged app instead of getting silently // stripped (caught by manually launching a --dir build - the packaged // server crashed with "Cannot find module 'next'"). from: ".next", filter: ["standalone/**/*"], to: ".", }, ], // Dedicated 1024x1024 app icon: the SRC symbol centred on the SRC dark // ground (#09090b), generated from public/branding/SRC_Symbol.png into // build-resources/app-icon.png (NOT build/ - that's electron-builder's own // gitignored output dir; a source asset living inside it would never get // committed, which is exactly the bug this comment is warning about one // paragraph down). 1024 is the size macOS actually wants for the largest // icns representation ("ICON512@2x"), so nothing gets upsampled. // // Deliberately NOT public/icon-512x512.png (what this used to point at): // that file is the *web* PWA manifest icon, so retouching it for the // desktop app silently changes the browser/PWA install icon too. Separate // source, separate concern. // // NOTE for whoever runs this next: electron-builder does NOT auto-detect a // file named `electron-builder.config.js` - its search list is // electron-builder.{yml,yaml,json,json5,js,cjs,mjs,ts}. Packaging must be // invoked with an explicit `--config electron-builder.config.js`, or every // setting in this file is silently ignored and you get stock defaults // (default Electron atom icon, `dist/` output, productName taken from // package.json's `name`). See the `dist:*` scripts in package.json, which // exist so nobody has to remember that. icon: "build-resources/app-icon.png", mac: { target: [ { target: "dmg", arch: ["x64", "arm64"] }, { target: "zip", arch: ["x64", "arm64"] }, ], category: "public.app-category.productivity", // No Apple Developer ID yet (VNCprodbuild step 9) - ship unsigned/ // un-notarized for now. hardenedRuntime is meaningless without signing // but left explicit so it's obvious what step 9 needs to flip on. hardenedRuntime: false, }, afterSign: "scripts/after-sign.cjs", win: { target: [{ target: "nsis", arch: ["x64"] }], }, nsis: { oneClick: false, allowToChangeInstallationDirectory: true, }, linux: { target: [ { target: "AppImage", arch: ["x64"] }, { target: "deb", arch: ["x64"] }, ], category: "Network;Email;", }, // electron-updater feed (see electron/main.ts's setupAutoUpdater()). // GitHub Releases, not a new distribution channel - the skill's // recommendation since this repo is already private and this needs no // extra infrastructure. "Light decision" per VNCprodbuild step 7, not // blocking, but flagged: switching later (e.g. to a self-hosted update // server) would mean revisiting this block and the `provider` electron- // updater talks to. publish: { provider: "github", owner: "brvncde-dotcom", repo: "vncmail-plus", }, };