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

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/b75f1076-62e7-445c-bbf9-d7be00dbfc59.png)

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/c9ddc58c-512e-487e-80c8-7c4bd51482a8.png)

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/cfc5c281-e044-4929-a261-b02a4619117b.png)
This commit is contained in:
pengx17
2024-04-25 03:30:04 +00:00
parent a697ebe340
commit 0b380f94c7
6 changed files with 105 additions and 7 deletions

View File

@@ -6,6 +6,7 @@ import { fileURLToPath } from 'node:url';
import { utils } from '@electron-forge/core';
import {
appIdMap,
arch,
buildType,
icnsPath,
@@ -96,12 +97,7 @@ export default {
buildIdentifier: buildType,
packagerConfig: {
name: productName,
appBundleId: fromBuildIdentifier({
internal: 'pro.affine.internal',
canary: 'pro.affine.canary',
beta: 'pro.affine.beta',
stable: 'pro.affine.app',
}),
appBundleId: fromBuildIdentifier(appIdMap),
icon: icnsPath,
osxSign: {
identity: 'Developer ID Application: TOEVERYTHING PTE. LTD.',

View File

@@ -18,7 +18,8 @@
"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",
"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",
"devDependencies": {

Binary file not shown.

After

Width:  |  Height:  |  Size: 805 KiB

View File

@@ -7,6 +7,7 @@ const ReleaseTypeSchema = z.enum(['stable', 'beta', 'canary', 'internal']);
const __dirname = fileURLToPath(new URL('.', import.meta.url));
const REPO_ROOT = path.resolve(__dirname, '..', '..', '..', '..');
const ROOT = path.resolve(__dirname, '..');
const envBuildType = (process.env.BUILD_TYPE || 'canary').trim().toLowerCase();
@@ -45,7 +46,15 @@ const platform =
? process.argv[process.argv.indexOf('--platform') + 1]
: process.platform;
const appIdMap = {
internal: 'pro.affine.internal',
canary: 'pro.affine.canary',
beta: 'pro.affine.beta',
stable: 'pro.affine.app',
};
export {
appIdMap,
arch,
buildType,
icnsPath,
@@ -55,6 +64,7 @@ export {
icoPath,
platform,
productName,
REPO_ROOT,
ROOT,
stableBuild,
};

View 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();

View File

@@ -0,0 +1 @@
ManifestDPIAware true