build(electron): add nsis script for windows installer (#6674)
This pr only includes how to bundle the app into an installer after package step. todo (not in this pr) - [ ] make sure updater can work for both nsis & squirrel - [ ] integrate nsis build into github action workflow Advantage over Squirrel: - allowing user to specify the installation location - better uninstaller   
This commit is contained in:
@@ -6,6 +6,7 @@ import { fileURLToPath } from 'node:url';
|
|||||||
import { utils } from '@electron-forge/core';
|
import { utils } from '@electron-forge/core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
appIdMap,
|
||||||
arch,
|
arch,
|
||||||
buildType,
|
buildType,
|
||||||
icnsPath,
|
icnsPath,
|
||||||
@@ -96,12 +97,7 @@ export default {
|
|||||||
buildIdentifier: buildType,
|
buildIdentifier: buildType,
|
||||||
packagerConfig: {
|
packagerConfig: {
|
||||||
name: productName,
|
name: productName,
|
||||||
appBundleId: fromBuildIdentifier({
|
appBundleId: fromBuildIdentifier(appIdMap),
|
||||||
internal: 'pro.affine.internal',
|
|
||||||
canary: 'pro.affine.canary',
|
|
||||||
beta: 'pro.affine.beta',
|
|
||||||
stable: 'pro.affine.app',
|
|
||||||
}),
|
|
||||||
icon: icnsPath,
|
icon: icnsPath,
|
||||||
osxSign: {
|
osxSign: {
|
||||||
identity: 'Developer ID Application: TOEVERYTHING PTE. LTD.',
|
identity: 'Developer ID Application: TOEVERYTHING PTE. LTD.',
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
"generate-assets": "node --loader ts-node/esm/transpile-only scripts/generate-assets.ts",
|
"generate-assets": "node --loader ts-node/esm/transpile-only scripts/generate-assets.ts",
|
||||||
"package": "cross-env NODE_OPTIONS=\"--loader ts-node/esm/transpile-only\" electron-forge package",
|
"package": "cross-env NODE_OPTIONS=\"--loader ts-node/esm/transpile-only\" electron-forge package",
|
||||||
"make": "cross-env NODE_OPTIONS=\"--loader ts-node/esm/transpile-only\" electron-forge make",
|
"make": "cross-env NODE_OPTIONS=\"--loader ts-node/esm/transpile-only\" electron-forge make",
|
||||||
"make-squirrel": "node --loader ts-node/esm/transpile-only scripts/make-squirrel.ts"
|
"make-squirrel": "node --loader ts-node/esm/transpile-only scripts/make-squirrel.ts",
|
||||||
|
"make-nsis": "node --loader ts-node/esm/transpile-only scripts/make-nsis.ts"
|
||||||
},
|
},
|
||||||
"main": "./dist/main.js",
|
"main": "./dist/main.js",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
BIN
packages/frontend/electron/resources/icons/nsis-sidebar.bmp
Normal file
BIN
packages/frontend/electron/resources/icons/nsis-sidebar.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 805 KiB |
@@ -7,6 +7,7 @@ const ReleaseTypeSchema = z.enum(['stable', 'beta', 'canary', 'internal']);
|
|||||||
|
|
||||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||||
|
|
||||||
|
const REPO_ROOT = path.resolve(__dirname, '..', '..', '..', '..');
|
||||||
const ROOT = path.resolve(__dirname, '..');
|
const ROOT = path.resolve(__dirname, '..');
|
||||||
|
|
||||||
const envBuildType = (process.env.BUILD_TYPE || 'canary').trim().toLowerCase();
|
const envBuildType = (process.env.BUILD_TYPE || 'canary').trim().toLowerCase();
|
||||||
@@ -45,7 +46,15 @@ const platform =
|
|||||||
? process.argv[process.argv.indexOf('--platform') + 1]
|
? process.argv[process.argv.indexOf('--platform') + 1]
|
||||||
: process.platform;
|
: process.platform;
|
||||||
|
|
||||||
|
const appIdMap = {
|
||||||
|
internal: 'pro.affine.internal',
|
||||||
|
canary: 'pro.affine.canary',
|
||||||
|
beta: 'pro.affine.beta',
|
||||||
|
stable: 'pro.affine.app',
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
appIdMap,
|
||||||
arch,
|
arch,
|
||||||
buildType,
|
buildType,
|
||||||
icnsPath,
|
icnsPath,
|
||||||
@@ -55,6 +64,7 @@ export {
|
|||||||
icoPath,
|
icoPath,
|
||||||
platform,
|
platform,
|
||||||
productName,
|
productName,
|
||||||
|
REPO_ROOT,
|
||||||
ROOT,
|
ROOT,
|
||||||
stableBuild,
|
stableBuild,
|
||||||
};
|
};
|
||||||
|
|||||||
90
packages/frontend/electron/scripts/make-nsis.ts
Normal file
90
packages/frontend/electron/scripts/make-nsis.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
import { buildForge } from 'app-builder-lib';
|
||||||
|
import debug from 'debug';
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
|
||||||
|
import {
|
||||||
|
appIdMap,
|
||||||
|
arch,
|
||||||
|
buildType,
|
||||||
|
iconPngPath,
|
||||||
|
icoPath,
|
||||||
|
platform,
|
||||||
|
productName,
|
||||||
|
REPO_ROOT,
|
||||||
|
ROOT,
|
||||||
|
} from './make-env.js';
|
||||||
|
|
||||||
|
const log = debug('electron-forge:make-nsis');
|
||||||
|
|
||||||
|
async function make() {
|
||||||
|
const appName = productName;
|
||||||
|
const makeDir = path.resolve(ROOT, 'out', buildType, 'make');
|
||||||
|
const outPath = path.resolve(makeDir, `nsis.windows/${arch}`);
|
||||||
|
const appDirectory = path.resolve(
|
||||||
|
ROOT,
|
||||||
|
'out',
|
||||||
|
buildType,
|
||||||
|
`${appName}-${platform}-${arch}`
|
||||||
|
);
|
||||||
|
|
||||||
|
await fs.ensureDir(outPath);
|
||||||
|
await fs.emptyDir(outPath);
|
||||||
|
|
||||||
|
// create tmp dir
|
||||||
|
const tmpPath = await fs.mkdtemp(appName);
|
||||||
|
|
||||||
|
// copy app to tmp dir
|
||||||
|
log(`Copying app to ${tmpPath}`);
|
||||||
|
await fs.copy(appDirectory, tmpPath);
|
||||||
|
|
||||||
|
log(`Calling app-builder-lib's buildForge() with ${tmpPath}`);
|
||||||
|
const output = await buildForge(
|
||||||
|
{ dir: tmpPath },
|
||||||
|
{
|
||||||
|
win: [`nsis:${arch}`],
|
||||||
|
config: {
|
||||||
|
appId: appIdMap[buildType],
|
||||||
|
productName,
|
||||||
|
executableName: productName,
|
||||||
|
icon: iconPngPath,
|
||||||
|
extraMetadata: {
|
||||||
|
// do not use package.json's name
|
||||||
|
name: productName,
|
||||||
|
},
|
||||||
|
nsis: {
|
||||||
|
differentialPackage: false,
|
||||||
|
perMachine: false,
|
||||||
|
oneClick: false,
|
||||||
|
license: path.resolve(REPO_ROOT, 'LICENSE'),
|
||||||
|
include: path.resolve(ROOT, 'scripts', 'nsis-installer.nsh'),
|
||||||
|
installerIcon: icoPath,
|
||||||
|
allowToChangeInstallationDirectory: true,
|
||||||
|
installerSidebar: path.resolve(
|
||||||
|
ROOT,
|
||||||
|
'resources',
|
||||||
|
'icons',
|
||||||
|
'nsis-sidebar.bmp'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Move the output to the actual output folder, app-builder-lib might get it wrong
|
||||||
|
log('Received output files', output);
|
||||||
|
|
||||||
|
const result: Array<string> = [];
|
||||||
|
for (const file of output) {
|
||||||
|
const filePath = path.resolve(outPath, path.basename(file));
|
||||||
|
result.push(filePath);
|
||||||
|
|
||||||
|
await fs.move(file, filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
await fs.remove(tmpPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
make();
|
||||||
1
packages/frontend/electron/scripts/nsis-installer.nsh
Normal file
1
packages/frontend/electron/scripts/nsis-installer.nsh
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ManifestDPIAware true
|
||||||
Reference in New Issue
Block a user