Skip to content

Commit

Permalink
feat: add Fuses plugin (#3132)
Browse files Browse the repository at this point in the history
  • Loading branch information
erikian authored Feb 21, 2023
1 parent 6856204 commit 133ad63
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 0 deletions.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,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 @@ -151,6 +152,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({
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",
"@electron-forge/shared-types": "6.0.4",
"@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);

0 comments on commit 133ad63

Please sign in to comment.