// 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: ".", }, ], // STAND-IN ICON, not a dedicated app icon: public/icon-512x512.png is the // PWA manifest icon (512x512 square PNG). electron-builder can generate // .icns/.ico from a single square PNG at build time (see // node_modules/app-builder-lib/out/util/iconConverter.js), so this // produces working icons for every target below - but at only 512x512, // the largest macOS icns representation (1024x1024 "ICON512@2x") gets // upsampled and will look soft compared to a real 1024x1024+ source. // public/branding/Bulwark_Icon_App.svg looks like the intended master for // this (as opposed to Bulwark_Favicon.png, sized for browser tabs), but // it's vector and this environment has no SVG rasterizer (rsvg-convert / // ImageMagick / Inkscape) to turn it into a proper 1024x1024 PNG. A human // (or a follow-up step with the right tooling) should export // Bulwark_Icon_App.svg at 1024x1024 and point `icon` at that instead. icon: "public/icon-512x512.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", }, };