mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
perf(electron): speed up Windows packaging with ASAR (#1190)
* perf(electron): package Windows app with ASAR * fix(electron): use portable ASAR unpack glob * fix(electron): unpack native modules from ASAR
This commit is contained in:
@@ -476,13 +476,37 @@ jobs:
|
|||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
run: yarn electron:prepare-package
|
run: yarn electron:prepare-package
|
||||||
|
|
||||||
- name: Package Electron app
|
- name: Make Electron installer
|
||||||
shell: bash
|
shell: bash
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
run: |
|
run: |
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
yarn electron-forge package 2>&1 | tee forge-output.log
|
yarn electron-forge make --platform=win32 --arch=x64 2>&1 | tee forge-output.log
|
||||||
ls -la out/
|
ls -la out/make/
|
||||||
|
|
||||||
|
- name: Verify ASAR runtime files
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
APP_ASAR=$(find out -path '*/resources/app.asar' | head -n 1)
|
||||||
|
KUBO_EXE=$(find out -path '*/resources/app.asar.unpacked/bin/win/ipfs.exe' | head -n 1)
|
||||||
|
SQLITE_MODULE=$(find out -path '*/resources/app.asar.unpacked/node_modules/better-sqlite3/build/Release/better_sqlite3.node' | head -n 1)
|
||||||
|
DATACHANNEL_MODULE=$(find out -path '*/resources/app.asar.unpacked/node_modules/node-datachannel/build/Release/node_datachannel.node' | head -n 1)
|
||||||
|
echo "Found ASAR: $APP_ASAR"
|
||||||
|
echo "Found Kubo: $KUBO_EXE"
|
||||||
|
echo "Found SQLite module: $SQLITE_MODULE"
|
||||||
|
echo "Found data channel module: $DATACHANNEL_MODULE"
|
||||||
|
test -f "$APP_ASAR"
|
||||||
|
test -f "$KUBO_EXE"
|
||||||
|
test -f "$SQLITE_MODULE"
|
||||||
|
test -f "$DATACHANNEL_MODULE"
|
||||||
|
"$KUBO_EXE" version
|
||||||
|
|
||||||
|
- name: Verify Squirrel installer
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
SETUP_EXE=$(find out/make -iname '*setup*.exe' | head -n 1)
|
||||||
|
echo "Found installer: $SETUP_EXE"
|
||||||
|
test -n "$SETUP_EXE" && test -f "$SETUP_EXE" && echo "Installer is valid"
|
||||||
|
|
||||||
- name: Verify executable
|
- name: Verify executable
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
export const getIpfsBinaryName = (platform = process.platform) => (platform === 'win32' ? 'ipfs.exe' : 'ipfs');
|
||||||
|
|
||||||
|
const getPlatformDirectory = (platform) => {
|
||||||
|
if (platform === 'win32') return 'win';
|
||||||
|
if (platform === 'darwin') return 'mac';
|
||||||
|
return 'linux';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBundledKuboPath = (rootPath, platform = process.platform) => path.join(rootPath, 'bin', getPlatformDirectory(platform), getIpfsBinaryName(platform));
|
||||||
|
|
||||||
|
export const getPackagedKuboPaths = (resourcesPath, platform = process.platform) => [
|
||||||
|
getBundledKuboPath(path.join(resourcesPath, 'app.asar.unpacked'), platform),
|
||||||
|
getBundledKuboPath(path.join(resourcesPath, 'app'), platform),
|
||||||
|
];
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import path from 'node:path';
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { getPackagedKuboPaths } from './kubo-paths.js';
|
||||||
|
|
||||||
|
describe('getPackagedKuboPaths', () => {
|
||||||
|
it.each([
|
||||||
|
['win32', 'win', 'ipfs.exe'],
|
||||||
|
['darwin', 'mac', 'ipfs'],
|
||||||
|
['linux', 'linux', 'ipfs'],
|
||||||
|
])('prefers the unpacked ASAR binary on %s and retains the loose-package fallback', (platform, platformDirectory, binaryName) => {
|
||||||
|
const resourcesPath = path.join('/tmp', '5chan', 'resources');
|
||||||
|
|
||||||
|
expect(getPackagedKuboPaths(resourcesPath, platform)).toEqual([
|
||||||
|
path.join(resourcesPath, 'app.asar.unpacked', 'bin', platformDirectory, binaryName),
|
||||||
|
path.join(resourcesPath, 'app', 'bin', platformDirectory, binaryName),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
+7
-19
@@ -6,22 +6,11 @@ import ps from 'node:process';
|
|||||||
import proxyServer from './proxy-server.js';
|
import proxyServer from './proxy-server.js';
|
||||||
import tcpPortUsed from 'tcp-port-used';
|
import tcpPortUsed from 'tcp-port-used';
|
||||||
import { fileURLToPath, pathToFileURL } from 'url';
|
import { fileURLToPath, pathToFileURL } from 'url';
|
||||||
|
import { getBundledKuboPath, getIpfsBinaryName, getPackagedKuboPaths } from './kubo-paths.js';
|
||||||
import { getPkcDataPath } from './pkc-paths.js';
|
import { getPkcDataPath } from './pkc-paths.js';
|
||||||
const dirname = path.join(path.dirname(fileURLToPath(import.meta.url)));
|
const dirname = path.join(path.dirname(fileURLToPath(import.meta.url)));
|
||||||
const projectRoot = path.join(dirname, '..');
|
const projectRoot = path.join(dirname, '..');
|
||||||
|
|
||||||
// Get platform-specific binary name
|
|
||||||
const getIpfsBinaryName = () => (process.platform === 'win32' ? 'ipfs.exe' : 'ipfs');
|
|
||||||
|
|
||||||
// Get platform subdirectory name for bin/ folder
|
|
||||||
const getPlatformDir = () => {
|
|
||||||
if (process.platform === 'win32') return 'win';
|
|
||||||
if (process.platform === 'darwin') return 'mac';
|
|
||||||
return 'linux';
|
|
||||||
};
|
|
||||||
|
|
||||||
const getBundledKuboPath = (rootPath) => path.join(rootPath, 'bin', getPlatformDir(), getIpfsBinaryName());
|
|
||||||
|
|
||||||
const downloadBundledIpfsClients = async () => {
|
const downloadBundledIpfsClients = async () => {
|
||||||
// before-pack.js is excluded from packaged builds, so only load it in dev.
|
// before-pack.js is excluded from packaged builds, so only load it in dev.
|
||||||
const { downloadIpfsClients } = await import('./before-pack.js');
|
const { downloadIpfsClients } = await import('./before-pack.js');
|
||||||
@@ -50,17 +39,16 @@ const getKuboPath = async () => {
|
|||||||
throw new Error(`Kubo binary download completed but '${bundledKuboPath}' was not found`);
|
throw new Error(`Kubo binary download completed but '${bundledKuboPath}' was not found`);
|
||||||
} else {
|
} else {
|
||||||
// In production, the binary is downloaded to bin/<platform>/ipfs by generateAssets hook
|
// In production, the binary is downloaded to bin/<platform>/ipfs by generateAssets hook
|
||||||
// With asar: false, files are at resources/app/ instead of resources/app.asar.unpacked
|
|
||||||
const appPath = process.resourcesPath;
|
const appPath = process.resourcesPath;
|
||||||
const binaryName = getIpfsBinaryName();
|
const binaryName = getIpfsBinaryName();
|
||||||
|
|
||||||
// Try the bin/ directory first (where generateAssets downloads binaries)
|
// Prefer the unpacked ASAR path, then support older loose-file packages.
|
||||||
const binDirPath = getBundledKuboPath(path.join(appPath, 'app'));
|
const packagedBinPaths = getPackagedKuboPaths(appPath);
|
||||||
if (fs.existsSync(binDirPath)) {
|
const packagedBinPath = packagedBinPaths.find((candidatePath) => fs.existsSync(candidatePath));
|
||||||
return binDirPath;
|
if (packagedBinPath) {
|
||||||
|
return packagedBinPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: try app.asar.unpacked for ASAR builds (if we ever re-enable ASAR)
|
|
||||||
const unpackedPath = path.join(appPath, 'app.asar.unpacked');
|
const unpackedPath = path.join(appPath, 'app.asar.unpacked');
|
||||||
const kuboModulePath = path.join(unpackedPath, 'node_modules', 'kubo');
|
const kuboModulePath = path.join(unpackedPath, 'node_modules', 'kubo');
|
||||||
|
|
||||||
@@ -84,7 +72,7 @@ const getKuboPath = async () => {
|
|||||||
return appKuboBinPath;
|
return appKuboBinPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Could not find kubo binary. Checked: ${binDirPath}, ${kuboBinPath}, ${appKuboBinPath}`);
|
throw new Error(`Could not find kubo binary. Checked: ${[...packagedBinPaths, kuboBinPath, appKuboBinPath].join(', ')}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+13
-4
@@ -11,10 +11,12 @@ const config = {
|
|||||||
appBundleId: '5chan.desktop',
|
appBundleId: '5chan.desktop',
|
||||||
icon: './public/icon', // electron-forge adds the correct extension per platform
|
icon: './public/icon', // electron-forge adds the correct extension per platform
|
||||||
|
|
||||||
// NOTE: asar is disabled because of a bug where electron-packager silently fails
|
// Keep Squirrel from processing the deeply nested dependency tree as loose files.
|
||||||
// during asar creation with 5chan's large node_modules. The app works fine without it.
|
// Kubo and native modules still need real filesystem paths at runtime.
|
||||||
// TODO: investigate and fix the asar creation issue
|
asar: {
|
||||||
asar: false,
|
// Use forward slashes so minimatch also recognizes the glob on Windows.
|
||||||
|
unpackDir: 'bin/**/*',
|
||||||
|
},
|
||||||
|
|
||||||
// Exclude unnecessary files from the package
|
// Exclude unnecessary files from the package
|
||||||
ignore: [
|
ignore: [
|
||||||
@@ -81,6 +83,13 @@ const config = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
name: '@electron-forge/plugin-auto-unpack-natives',
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
makers: [
|
makers: [
|
||||||
// macOS
|
// macOS
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -144,6 +144,7 @@
|
|||||||
"@electron-forge/maker-dmg": "7.8.0",
|
"@electron-forge/maker-dmg": "7.8.0",
|
||||||
"@electron-forge/maker-squirrel": "7.8.0",
|
"@electron-forge/maker-squirrel": "7.8.0",
|
||||||
"@electron-forge/maker-zip": "7.8.0",
|
"@electron-forge/maker-zip": "7.8.0",
|
||||||
|
"@electron-forge/plugin-auto-unpack-natives": "7.8.0",
|
||||||
"@electron/rebuild": "3.7.2",
|
"@electron/rebuild": "3.7.2",
|
||||||
"@reforged/maker-appimage": "5.1.1",
|
"@reforged/maker-appimage": "5.1.1",
|
||||||
"@types/lodash": "4.17.24",
|
"@types/lodash": "4.17.24",
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ __metadata:
|
|||||||
"@electron-forge/maker-dmg": "npm:7.8.0"
|
"@electron-forge/maker-dmg": "npm:7.8.0"
|
||||||
"@electron-forge/maker-squirrel": "npm:7.8.0"
|
"@electron-forge/maker-squirrel": "npm:7.8.0"
|
||||||
"@electron-forge/maker-zip": "npm:7.8.0"
|
"@electron-forge/maker-zip": "npm:7.8.0"
|
||||||
|
"@electron-forge/plugin-auto-unpack-natives": "npm:7.8.0"
|
||||||
"@electron/rebuild": "npm:3.7.2"
|
"@electron/rebuild": "npm:3.7.2"
|
||||||
"@floating-ui/react": "npm:0.26.1"
|
"@floating-ui/react": "npm:0.26.1"
|
||||||
"@pkcprotocol/pkc-js": "npm:0.0.73"
|
"@pkcprotocol/pkc-js": "npm:0.0.73"
|
||||||
@@ -2054,6 +2055,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@electron-forge/plugin-auto-unpack-natives@npm:7.8.0":
|
||||||
|
version: 7.8.0
|
||||||
|
resolution: "@electron-forge/plugin-auto-unpack-natives@npm:7.8.0"
|
||||||
|
dependencies:
|
||||||
|
"@electron-forge/plugin-base": "npm:7.8.0"
|
||||||
|
"@electron-forge/shared-types": "npm:7.8.0"
|
||||||
|
checksum: 10c0/d684741844c8809e20130d737024d060be7e7f4395006065aa6c62461ce6a5c24d86c856370075c426f89304f4ef380dbe2aee51fffe10d3e734c24f28fda811
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@electron-forge/plugin-base@npm:7.8.0":
|
"@electron-forge/plugin-base@npm:7.8.0":
|
||||||
version: 7.8.0
|
version: 7.8.0
|
||||||
resolution: "@electron-forge/plugin-base@npm:7.8.0"
|
resolution: "@electron-forge/plugin-base@npm:7.8.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user