diff --git a/apps/desktop/electron-builder.js b/apps/desktop/electron-builder.js new file mode 100644 index 00000000000..bd719644cce --- /dev/null +++ b/apps/desktop/electron-builder.js @@ -0,0 +1,149 @@ +/** + * Electron Builder Configuration + * @see https://www.electron.build/configuration/configuration + */ + +const { join } = require("node:path"); +const pkg = require("./package.json"); + +const currentYear = new Date().getFullYear(); +const author = pkg.author?.name ?? pkg.author; +const productName = pkg.productName; + +/** @type {import('electron-builder').Configuration} */ +const config = { + appId: "com.superset.desktop", + productName, + copyright: `Copyright © ${currentYear} — ${author}`, + electronVersion: pkg.devDependencies.electron.replace(/^\^/, ""), + + // Generate latest-mac.yml for auto-update (workflow handles actual upload) + publish: { + provider: "github", + owner: "superset-sh", + repo: "superset", + }, + + // Directories + directories: { + output: "release", + buildResources: join(pkg.resources, "build"), + }, + + // ASAR configuration for native modules and external resources + asar: true, + asarUnpack: [ + "**/node_modules/better-sqlite3/**/*", + // better-sqlite3 uses `bindings` to locate native modules - must be unpacked together + "**/node_modules/bindings/**/*", + "**/node_modules/file-uri-to-path/**/*", + "**/node_modules/node-pty/**/*", + // Sound files must be unpacked so external audio players (afplay, paplay, etc.) can access them + "**/resources/sounds/**/*", + ], + + // Extra resources placed outside asar archive (accessible via process.resourcesPath) + extraResources: [ + // Database migrations - must be outside asar for drizzle-orm to read + { + from: "dist/resources/migrations", + to: "resources/migrations", + filter: ["**/*"], + }, + ], + + files: [ + "dist/**/*", + "package.json", + { + from: pkg.resources, + to: "resources", + filter: ["**/*"], + }, + // Native modules that can't be bundled by Vite. + // bun creates symlinks for direct deps in workspace node_modules. + // The copy:native-modules script replaces symlinks with real files + // before building (required for Bun 1.3+ isolated installs). + { + from: "node_modules/better-sqlite3", + to: "node_modules/better-sqlite3", + filter: ["**/*"], + }, + // better-sqlite3 uses `bindings` package to locate its native .node file + { + from: "node_modules/bindings", + to: "node_modules/bindings", + filter: ["**/*"], + }, + // `bindings` requires `file-uri-to-path` for file:// URL handling + { + from: "node_modules/file-uri-to-path", + to: "node_modules/file-uri-to-path", + filter: ["**/*"], + }, + { + from: "node_modules/node-pty", + to: "node_modules/node-pty", + filter: ["**/*"], + }, + "!**/.DS_Store", + ], + + // Rebuild native modules for Electron's Node.js version + // Disabled on Windows - native modules already copied by copy-native-modules script + npmRebuild: false, + + // macOS + mac: { + icon: join(pkg.resources, "build/icons/icon.icns"), + category: "public.app-category.utilities", + target: [ + { + target: "default", + arch: ["arm64"], + }, + ], + hardenedRuntime: true, + gatekeeperAssess: false, + notarize: true, + extendInfo: { + CFBundleName: productName, + CFBundleDisplayName: productName, + }, + }, + + // Deep linking protocol + protocols: { + name: productName, + schemes: ["superset"], + }, + + // Linux + linux: { + icon: join(pkg.resources, "build/icons"), + category: "Utility", + synopsis: pkg.description, + target: ["AppImage", "deb"], + artifactName: `superset-\${version}-\${arch}.\${ext}`, + }, + + // Windows + win: { + icon: join(pkg.resources, "build/icons/icon.ico"), + target: [ + { + target: "nsis", + arch: ["x64"], + }, + ], + artifactName: `${productName}-${pkg.version}-\${arch}.\${ext}`, + }, + + // NSIS installer (Windows) + nsis: { + oneClick: false, + allowToChangeInstallationDirectory: true, + }, +}; + +module.exports = config; diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 738586d1158..8350f587789 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -21,9 +21,9 @@ "compile:app": "cross-env NODE_OPTIONS=--max-old-space-size=4096 electron-vite build", "copy:native-modules": "bun run scripts/copy-native-modules.ts", "prebuild": "bun run clean:dev && bun run compile:app && bun run copy:native-modules", - "build": "cross-env CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --publish never", + "build": "cross-env CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --config electron-builder.js --publish never", "prepackage": "bun run copy:native-modules", - "package": "electron-builder --config electron-builder.ts", + "package": "electron-builder --config electron-builder.js", "install:deps": "electron-builder install-app-deps", "release": "electron-builder --publish always", "clean:dev": "rimraf ./node_modules/.dev", diff --git a/apps/desktop/scripts/copy-native-modules.ts b/apps/desktop/scripts/copy-native-modules.ts index 0f28a56b4bd..7cf62ec492c 100644 --- a/apps/desktop/scripts/copy-native-modules.ts +++ b/apps/desktop/scripts/copy-native-modules.ts @@ -13,7 +13,7 @@ * This is safe because bun install will recreate the symlinks on next install. */ -import { cpSync, existsSync, lstatSync, realpathSync, rmSync } from "node:fs"; +import { cpSync, existsSync, lstatSync, realpathSync, rmSync, unlinkSync } from "node:fs"; import { dirname, join } from "node:path"; // Native modules that must exist for the app to work @@ -46,8 +46,13 @@ function copyModuleIfSymlink( console.log(` ${moduleName}: symlink -> replacing with real files`); console.log(` Real path: ${realPath}`); - // Remove the symlink - rmSync(modulePath); + // Remove the symlink (use unlinkSync for Windows compatibility) + try { + unlinkSync(modulePath); + } catch (error) { + // Fallback to rmSync with force option + rmSync(modulePath, { force: true, recursive: false }); + } // Copy the actual files cpSync(realPath, modulePath, { recursive: true });