mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-17 23:14:03 +01:00
From SEA to regular NodeJS
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
"name": "@yaak/plugin-runtime",
|
||||
"scripts": {
|
||||
"dev": "nodemon",
|
||||
"build": "run-p build:* && node scripts/generate-sea.cjs",
|
||||
"build:main": "esbuild src/index.ts --bundle --platform=node --outfile=build/index.js",
|
||||
"build:worker": "esbuild src/index.worker.ts --bundle --platform=node --outfile=build/index.worker.js",
|
||||
"build": "run-p build:*",
|
||||
"build:main": "esbuild src/index.ts --bundle --platform=node --outfile=build/index.cjs",
|
||||
"build:worker": "esbuild src/index.worker.ts --bundle --platform=node --outfile=build/index.worker.cjs",
|
||||
"build:proto": "grpc_tools_node_protoc --ts_proto_out=src/gen --ts_proto_opt=outputServices=nice-grpc,outputServices=generic-definitions,useExactTypes=false --proto_path=../proto ../proto/plugins/*.proto"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
const path = require('node:path');
|
||||
const {execSync} = require('node:child_process');
|
||||
const {cpSync, mkdirSync, chmodSync, unlinkSync, rmSync, readdirSync, statSync} = require('node:fs');
|
||||
const pluginRuntimeDir = path.join(__dirname, '..');
|
||||
const destDir = path.join(__dirname, '..', '..', 'src-tauri', 'vendored', 'plugin-runtime');
|
||||
const blobPath = path.join(pluginRuntimeDir, 'yaak-plugins.blob');
|
||||
|
||||
const DST_BIN_MAP = {
|
||||
darwin_arm64: 'yaakplugins-aarch64-apple-darwin',
|
||||
darwin_x64: 'yaakplugins-x86_64-apple-darwin',
|
||||
linux_x64: 'yaakplugins-x86_64-unknown-linux-gnu',
|
||||
win32_x64: 'yaakplugins-x86_64-pc-windows-msvc.exe',
|
||||
};
|
||||
|
||||
// Build the sea
|
||||
console.log('Building SEA blob');
|
||||
execSync('node --experimental-sea-config sea-config.json', {cwd: pluginRuntimeDir});
|
||||
|
||||
const tmp = path.join(__dirname, 'tmp', `${Math.random()}`);
|
||||
mkdirSync(tmp, {recursive: true});
|
||||
|
||||
let tmpNodePath = process.platform === 'win32' ? path.join(tmp, 'node.exe') : path.join(tmp, 'node');
|
||||
|
||||
console.log('Copying Node.js binary');
|
||||
cpSync(process.execPath, tmpNodePath);
|
||||
|
||||
console.log('Changing Node.js binary permissions');
|
||||
chmodSync(tmpNodePath, 0o755);
|
||||
|
||||
console.log('Removing Node.js code signature');
|
||||
try {
|
||||
if (process.platform === 'darwin') execSync(`codesign --remove-signature ${tmpNodePath}`);
|
||||
else if (process.platform === 'win32') execSync(`"${getSigntoolLocation()}" remove /s ${tmpNodePath}`);
|
||||
/* Nothing for Linux */
|
||||
} catch (err) {
|
||||
console.log('Failed remove signature', err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('Injecting sea blob into Node.js');
|
||||
if (process.platform === 'win32') execSync(`npx postject ${tmpNodePath} NODE_SEA_BLOB ${blobPath} --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2`);
|
||||
else if (process.platform === 'darwin') execSync(`npx postject ${tmpNodePath} NODE_SEA_BLOB ${blobPath} --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 --macho-segment-name NODE_SEA`);
|
||||
else if (process.platform === 'linux') execSync(`npx postject ${tmpNodePath} NODE_SEA_BLOB ${blobPath} --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2`);
|
||||
} catch (err) {
|
||||
console.log('Failed to inject blob', err.stdout.toString());
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
unlinkSync(blobPath);
|
||||
|
||||
console.log('Re-signing Node.js');
|
||||
try {
|
||||
if (process.platform === 'darwin') execSync(`codesign --sign - ${tmpNodePath}`);
|
||||
// NOTE: Don't need to resign, as Tauri will sign the sidecar binaries during release
|
||||
// else if (process.platform === 'win32') execSync(`"${getSigntoolLocation()}" sign /fd SHA256 ${tmpNodePath}`);
|
||||
/* Nothing for Linux */
|
||||
} catch (err) {
|
||||
console.log('Failed sign', err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const key = `${process.platform}_${process.env.NODE_ARCH ?? process.arch}`;
|
||||
const dstPath = path.join(destDir, DST_BIN_MAP[key]);
|
||||
cpSync(tmpNodePath, dstPath);
|
||||
|
||||
rmSync(tmp, {recursive: true, force: true});
|
||||
|
||||
console.log(`Copied sea to ${dstPath}`)
|
||||
|
||||
|
||||
// https://github.com/skymatic/code-sign-action/blob/a2a8833d4e9202556539b564a2a4af5b6da3e8b2/index.ts
|
||||
function getSigntoolLocation() {
|
||||
const windowsKitsFolder = 'C:/Program Files (x86)/Windows Kits/10/bin/';
|
||||
const folders = readdirSync(windowsKitsFolder);
|
||||
let fileName = '';
|
||||
let maxVersion = 0;
|
||||
for (const folder of folders) {
|
||||
if (!folder.endsWith('.0')) {
|
||||
continue;
|
||||
}
|
||||
const folderVersion = parseInt(folder.replace(/\./g, ''));
|
||||
if (folderVersion > maxVersion) {
|
||||
const signtoolFilename = `${windowsKitsFolder}${folder}/x64/signtool.exe`;
|
||||
try {
|
||||
const stat = statSync(signtoolFilename);
|
||||
if (stat.isFile()) {
|
||||
fileName = signtoolFilename;
|
||||
maxVersion = folderVersion;
|
||||
}
|
||||
} catch {
|
||||
console.warn('Skipping %s due to error.', signtoolFilename);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fileName === '') {
|
||||
throw new Error('Unable to find signtool.exe in ' + windowsKitsFolder);
|
||||
}
|
||||
|
||||
console.log(`Signtool location is ${fileName}.`);
|
||||
return fileName;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"main": "build/index.js",
|
||||
"disableExperimentalSEAWarning": true,
|
||||
"output": "yaak-plugins.blob",
|
||||
"assets": {
|
||||
"worker": "build/index.worker.js"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import path from 'node:path';
|
||||
import { Worker } from 'node:worker_threads';
|
||||
import { PluginInfo } from './plugins';
|
||||
|
||||
@@ -24,10 +25,11 @@ export class PluginHandle {
|
||||
readonly pluginDir: string;
|
||||
readonly #worker: Worker;
|
||||
|
||||
constructor({ pluginDir, workerJsPath }: { pluginDir: string; workerJsPath: string }) {
|
||||
constructor(pluginDir: string) {
|
||||
this.pluginDir = pluginDir;
|
||||
|
||||
this.#worker = new Worker(workerJsPath, {
|
||||
const workerPath = path.join(__dirname, 'index.worker.cjs');
|
||||
this.#worker = new Worker(workerPath, {
|
||||
workerData: {
|
||||
pluginDir: this.pluginDir,
|
||||
},
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import { existsSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { getAsset } from 'node:sea';
|
||||
import { PluginHandle } from './PluginHandle';
|
||||
import { loadPlugins, PluginInfo } from './plugins';
|
||||
|
||||
export class PluginManager {
|
||||
#handles: PluginHandle[] | null = null;
|
||||
static #instance: PluginManager | null = null;
|
||||
static #workerPath = path.join(tmpdir(), `index.${Math.random()}.worker.js`);
|
||||
|
||||
public static instance(): PluginManager {
|
||||
if (PluginManager.#instance == null) {
|
||||
@@ -19,22 +14,10 @@ export class PluginManager {
|
||||
}
|
||||
|
||||
async plugins(): Promise<PluginHandle[]> {
|
||||
await this.#ensureWorkerForSea();
|
||||
this.#handles = this.#handles ?? loadPlugins(PluginManager.#workerPath);
|
||||
this.#handles = this.#handles ?? loadPlugins();
|
||||
return this.#handles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy worker JS asset to filesystem if we're in single-executable-application (SEA)
|
||||
* @private
|
||||
*/
|
||||
async #ensureWorkerForSea() {
|
||||
if (existsSync(PluginManager.#workerPath)) return;
|
||||
|
||||
console.log('Writing worker file to', PluginManager.#workerPath);
|
||||
writeFileSync(PluginManager.#workerPath, getAsset('worker', 'utf8'));
|
||||
}
|
||||
|
||||
async #pluginsWithInfo(): Promise<{ plugin: PluginHandle; info: PluginInfo }[]> {
|
||||
const plugins = await this.plugins();
|
||||
return Promise.all(plugins.map(async (plugin) => ({ plugin, info: await plugin.getInfo() })));
|
||||
|
||||
@@ -8,11 +8,11 @@ export interface PluginInfo {
|
||||
capabilities: ('import' | 'export' | 'filter')[];
|
||||
}
|
||||
|
||||
export function loadPlugins(workerJsPath: string): PluginHandle[] {
|
||||
export function loadPlugins(): PluginHandle[] {
|
||||
const pluginsDir = process.env.PLUGINS_DIR;
|
||||
if (!pluginsDir) throw new Error('PLUGINS_DIR is not set');
|
||||
console.log('Loading plugins from', pluginsDir);
|
||||
|
||||
const pluginDirs = fs.readdirSync(pluginsDir).map((p) => path.join(pluginsDir, p));
|
||||
return pluginDirs.map((pluginDir) => new PluginHandle({ pluginDir, workerJsPath }));
|
||||
return pluginDirs.map((pluginDir) => new PluginHandle(pluginDir));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user