Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"yarn-or-npm": "^3.0.1"
},
"devDependencies": {
"@electron/fuses": ">=1.0.0",
"@knodes/typedoc-plugin-monorepo-readmes": "0.22.5",
"@malept/eslint-config": "^2.0.0",
"@types/chai": "^4.2.12",
Expand Down Expand Up @@ -150,6 +151,9 @@
"electron-wix-msi": "^5.0.0",
"macos-alias": "^0.2.11"
},
"peerDependencies": {
"@electron/fuses": ">=1.0.0"
},
"lint-staged": {
"*.{html,json,md,yml}": "prettier --write",
"*.{js,ts}": [
Expand Down
34 changes: 34 additions & 0 deletions packages/plugin/fuses/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@electron-forge/plugin-fuses
=====

This plugin allows flipping [Electron Fuses](https://github.com/electron/fuses) when packaging your app with Electron Forge.

## Usage

Install `@electron-forge/plugin-fuses` and `@electron/fuses` as dev dependencies and add this plugin to the `plugins` array in your Forge configuration::
```shell
# Yarn
yarn add --dev @electron-forge/plugin-fuses @electron/fuses

# npm
npm i -D @electron-forge/plugin-fuses @electron/fuses
```

```js
// forge.config.js

const { FusesPlugin } = require('@electron-forge/plugin-fuses');
const { FuseV1Options, FuseVersion } = require('@electron/fuses');

const forgeConfig = {
plugins: [
new FusesPlugin({
version: FuseVersion.V1,
[FuseV1Options.RunAsNode]: false,
// ...any other options supported by @electron/fuses
}),
],
};

module.exports = forgeConfig;
```
39 changes: 39 additions & 0 deletions packages/plugin/fuses/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "@electron-forge/plugin-fuses",
"version": "6.0.4",
"description": "A plugin for flipping Electron Fuses in Electron Forge",
"repository": "https://github.com/electron/forge",
"author": "Erik Moura <erikian@erikian.dev>",
"license": "MIT",
"main": "dist/FusesPlugin.js",
"files": [
"dist",
"package.json",
"README.md"
],
"typings": "dist/FusesPlugin.d.ts",
"scripts": {
"test": "xvfb-maybe mocha --config ../../../.mocharc.js test/**/*_spec*.ts"
},
"devDependencies": {
"@electron/fuses": ">=1.0.0",
"@malept/cross-spawn-promise": "^2.0.0",
"chai": "^4.3.3",
"fs-extra": "^10.0.0",
"mocha": "^9.0.1",
"xvfb-maybe": "^0.2.1"
},
"peerDependencies": {
"@electron/fuses": ">=1.0.0"
},
"engines": {
"node": ">= 14.17.5"
},
"dependencies": {
"@electron-forge/plugin-base": "6.0.4",
"@electron-forge/shared-types": "6.0.4"
},
"publishConfig": {
"access": "public"
}
}
45 changes: 45 additions & 0 deletions packages/plugin/fuses/src/FusesPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import path from 'path';

import { namedHookWithTaskFn, PluginBase } from '@electron-forge/plugin-base';
import { ForgeMultiHookMap } from '@electron-forge/shared-types';
import { flipFuses, FuseConfig } from '@electron/fuses';

import { getElectronExecutablePath } from './util/getElectronExecutablePath';

export default class FusesPlugin extends PluginBase<FuseConfig> {
name = 'fuses';

fusesConfig = {} as FuseConfig;

constructor(fusesConfig: FuseConfig) {
super(fusesConfig);

this.fusesConfig = fusesConfig;
}

getHooks(): ForgeMultiHookMap {
return {
packageAfterCopy: namedHookWithTaskFn<'packageAfterCopy'>(async (listrTask, resolvedForgeConfig, resourcesPath, electronVersion, platform, arch) => {
const { fusesConfig } = this;

if (Object.keys(fusesConfig).length) {
const pathToElectronExecutable = getElectronExecutablePath({
Comment thread
erickzhao marked this conversation as resolved.
appName: ['darwin', 'mas'].includes(platform) ? 'Electron' : 'electron',
basePath: path.resolve(resourcesPath, '../..'),
platform,
});

const osxSignConfig = resolvedForgeConfig.packagerConfig.osxSign;
const hasOSXSignConfig = (typeof osxSignConfig === 'object' && Boolean(Object.keys(osxSignConfig).length)) || Boolean(osxSignConfig);

await flipFuses(pathToElectronExecutable, {
resetAdHocDarwinSignature: !hasOSXSignConfig && platform === 'darwin' && arch === 'arm64',
...this.fusesConfig,
});
}
}, 'Flipping Fuses'),
};
}
}

export { FusesPlugin };
16 changes: 16 additions & 0 deletions packages/plugin/fuses/src/util/getElectronExecutablePath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import path from 'path';

import { ForgePlatform } from '@electron-forge/shared-types';

type GetElectronExecutablePathParams = {
appName: string;
basePath: string;
platform: ForgePlatform;
};

export function getElectronExecutablePath({ appName, basePath, platform }: GetElectronExecutablePathParams): string {
return path.join(
basePath,
['darwin', 'mas'].includes(platform) ? path.join('MacOS', appName) : [appName, process.platform === 'win32' ? '.exe' : ''].join('')
);
}
67 changes: 67 additions & 0 deletions packages/plugin/fuses/test/FusesPlugin_spec_slow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import fs from 'fs';
import path from 'path';

import { CrossSpawnOptions, spawn } from '@malept/cross-spawn-promise';
import { expect } from 'chai';
import fsExtra from 'fs-extra';

import { getElectronExecutablePath } from '../src/util/getElectronExecutablePath';

describe('FusesPlugin', () => {
const appPath = path.join(__dirname, 'fixtures', 'app');

const spawnOptions: CrossSpawnOptions = {
cwd: appPath,
shell: true,
};

const packageJSON = JSON.parse(
fs.readFileSync(path.join(appPath, 'package.json'), {
encoding: 'utf-8',
})
);

const { name: appName } = packageJSON;

// @TODO get rid of this once https://github.com/electron/forge/pull/3123 lands
const platformArchSuffix = `${process.platform}-x64`;

const outDir = path.join(appPath, 'out', `${appName}-${platformArchSuffix}`);

before(async () => {
delete process.env.TS_NODE_PROJECT;
await spawn('yarn', ['install'], spawnOptions);
});

after(async () => {
await fsExtra.remove(path.resolve(outDir, '../'));

// @TODO this can be removed once the mock app installs a published version of @electron-forge/plugin-fuses instead of a local package
await fsExtra.remove(path.join(__dirname, './fixtures/app/node_modules'));
});

it('should flip Fuses', async () => {
await spawn('yarn', ['package'], spawnOptions);

const electronExecutablePath = getElectronExecutablePath({
appName,
basePath: path.join(outDir, ...(process.platform === 'darwin' ? [`${appName}.app`, 'Contents'] : [])),
platform: process.platform,
});

/**
* If the `RunAsNode` fuse had not been flipped,
* this would return the Node.js version (e.g. `v14.16.0`)
* instead of the `console.log` from `main.js`.
*/
const output = (
await spawn(electronExecutablePath, ['-v'], {
env: {
ELECTRON_RUN_AS_NODE: '1',
},
})
).trim();

expect(output).to.equals('The Fuses plugin is working');
});
});
31 changes: 31 additions & 0 deletions packages/plugin/fuses/test/fixtures/app/forge.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import path from 'path';

import { FusesPlugin } from '@electron-forge/plugin-fuses';
import { ForgeConfig } from '@electron-forge/shared-types';
import { FuseV1Options, FuseVersion } from '@electron/fuses';
import fsExtra from 'fs-extra';

const forgeConfig: ForgeConfig = {
packagerConfig: {
afterComplete: [
// makes tests a bit simpler by having a single output directory in every platform/arch
async (packagedAppLocation, _electronVersion, _targetPlatform, _targetArch, done) => {
const parentDir = path.resolve(packagedAppLocation, '..');
await fsExtra.move(packagedAppLocation, path.join(parentDir, 'fuses-test-app'), {
overwrite: true,
});

done();
},
],
},

plugins: [
new FusesPlugin({
version: FuseVersion.V1,
[FuseV1Options.RunAsNode]: false,
}),
],
};

export default forgeConfig;
18 changes: 18 additions & 0 deletions packages/plugin/fuses/test/fixtures/app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "fuses-test-app",
"version": "1.0.0",
"main": "./src/main.js",
"scripts": {
"package": "electron-forge package"
},
"dependencies": {
"@electron-forge/plugin-fuses": "file:./../../../../fuses",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if there's a better way to test unreleased plugins

"@electron-forge/shared-types": "6.0.4",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESLint throws a node/no-unpublished-import at me when importing some packages if they're under devDependencies, so I figured moving them to dependencies would be better than adding a bunch of es-ignore comments since it's a mock app anyway

"@electron/fuses": "^1.6.1",
"fs-extra": "^10.0.0"
},
"devDependencies": {
"@electron-forge/cli": "6.0.4",
"electron": "12.0.0"
}
}
5 changes: 5 additions & 0 deletions packages/plugin/fuses/test/fixtures/app/src/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const { app } = require('electron');

console.log('The Fuses plugin is working');

app.exit(0);