diff --git a/packages/fether-electron/package.json b/packages/fether-electron/package.json index 5b21a6c79dcb3e77d0a2bc721eb086a16a9a78f4..a659904efb30b2ca15e6d68149fe29225da5e7a1 100644 --- a/packages/fether-electron/package.json +++ b/packages/fether-electron/package.json @@ -41,6 +41,7 @@ "dependencies": { "@parity/electron": "^0.1.0", "commander": "^2.15.1", + "commander-remaining-args": "^1.2.0", "fether-react": "^0.1.0", "menubar": "^5.2.3", "pino": "^4.16.1", @@ -55,4 +56,4 @@ "electron-webpack": "^2.1.2", "webpack": "^4.7.0" } -} \ No newline at end of file +} diff --git a/packages/fether-electron/src/main/cli/index.js b/packages/fether-electron/src/main/cli/index.js index 928f06c483e10c33538a92e02479fa916bd65515..06a1e81b810ab0c54ded94377711076ff4558813 100644 --- a/packages/fether-electron/src/main/cli/index.js +++ b/packages/fether-electron/src/main/cli/index.js @@ -23,8 +23,9 @@ cli .version(version) .allowUnknownOption() .option( - '--chain', - 'The network to connect to, can be one of "foundation", "kovan" or "ropsten". (default: "kovan")' + '--chain ', + 'The network to connect to, can be one of "foundation", "kovan" or "ropsten". (default: "kovan")', + 'kovan' ) .option( '--no-run-parity', @@ -32,12 +33,16 @@ cli ) .option( '--ws-interface ', - `Specify the hostname portion of the WebSockets server ${productName} will connect to. IP should be an interface's IP address. (default: 127.0.0.1)` + `Specify the hostname portion of the WebSockets server ${productName} will connect to. IP should be an interface's IP address. (default: 127.0.0.1)`, + '127.0.0.1' ) .option( '--ws-port ', - `Specify the port portion of the WebSockets server ${productName} will connect to. (default: 8546)` + `Specify the port portion of the WebSockets server ${productName} will connect to. (default: 8546)`, + 8546 ) - .parse(process.argv); + // `electron-webpack dev` runs Electron with the `--inspect` flag for HMR; + // we want to ignore this flag and not pass it down to Parity + .parse(process.argv.filter(arg => !arg.startsWith('--inspect'))); export default cli; diff --git a/packages/fether-electron/src/main/index.js b/packages/fether-electron/src/main/index.js index 10d54619eb2930d5de204713ff4f63d0ab6f7e4d..c9b8f0dc9d7acc4312123605daa9ecab754f5bee 100644 --- a/packages/fether-electron/src/main/index.js +++ b/packages/fether-electron/src/main/index.js @@ -6,10 +6,12 @@ import parityElectron, { getParityPath, fetchParity, + isParityRunning, runParity, killParity } from '@parity/electron'; import electron from 'electron'; +import getRemainingArgs from 'commander-remaining-args'; import path from 'path'; import url from 'url'; @@ -42,7 +44,6 @@ function createWindow () { // Set options for @parity/electron parityElectron({ - cli, logger: namespace => log => Pino({ name: namespace }).info(log) }); @@ -57,12 +58,33 @@ function createWindow () { parityChannel: parity.channel }) ) - .then(() => + .then(async () => { // Run parity when installed - runParity(['--light', '--chain', cli.chain || 'kovan'], err => - handleError(err, 'An error occured with Parity.') - ) - ) + + // Don't run parity if the user ran fether with --no-run-parity + if (!cli.runParity) { + return; + } + + if (await isParityRunning({ + wsInterface: cli.wsInterface, + wsPort: cli.wsPort + })) { + return; + } + + return runParity({ + flags: [ + ...getRemainingArgs(cli), + '--light', + '--chain', cli.chain, + '--ws-interface', cli.wsInterface, + '--ws-port', cli.wsPort + ], + onParityError: err => + handleError(err, 'An error occured with Parity.') + }); + }) .then(() => { // Notify the renderers mainWindow.webContents.send('parity-running', true); @@ -74,11 +96,11 @@ function createWindow () { // passed to ELECTRON_START_URL mainWindow.loadURL( process.env.ELECTRON_START_URL || - url.format({ - pathname: path.join(staticPath, 'build', 'index.html'), - protocol: 'file:', - slashes: true - }) + url.format({ + pathname: path.join(staticPath, 'build', 'index.html'), + protocol: 'file:', + slashes: true + }) ); // Listen to messages from renderer process diff --git a/packages/parity-electron/README.md b/packages/parity-electron/README.md index 48f155121b5f1c8254f6f7e63eda3b0f2994b486..7eec0c66112810772bebaa999efcce5a614b1800 100644 --- a/packages/parity-electron/README.md +++ b/packages/parity-electron/README.md @@ -15,7 +15,6 @@ import parityElectron, { isParityRunning } from '@parity/electron'; // Optional: override default options parityElectron({ - cli: myOwnCliObject, logger: myCustomLoggerFunction }) @@ -31,7 +30,6 @@ If you don't want to override the default options, there's no need to call this | Option | Default Value | Description | | ---------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `options.cli` | `{}` | An object where key/values are --flags passed to the binary. The `cli` object returned by `yargs` or [`commander.js`](https://github.com/tj/commander.js/) would fit here. | | `options.logger` | `require('debug')` | A function with the same signature as [`debug`](https://github.com/visionmedia/debug). All logs inside `@parity/electron` will then be logged by this function. | #### `fetchParity(mainWindow: BrowserWindow, options: Object): Promise` @@ -52,18 +50,23 @@ Returns the path to Parity. It checks (in this order) if Parity is in `$PATH`, i Returns the path to the Parity path inside Electron's `userData` folder, even if that binary doesn't exist. It's the default download location for [`fetchParity`](#fetchParitymainWindow-BrowserWindow-options-Object-PromiseltStringgt0). -#### `isParityRunning(): Promise` +#### `isParityRunning(options: Object): Promise` + +| Option | Type | Description | +| ------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `options.wsInterface` | `String` | Hostname portion of the WebSockets server Fether will try to connect to. It should be an interface's IP address. (default: 127.0.0.1) | +| `options.wsPort` | `Number | String` | Port portion of the WebSockets server Fether will try to connect to. (default: 8546) | Resolves to `true` if Parity is currently running, or to `false` if not. -#### `runParity(additionalFlags: Array, onParityError: Function): Promise` +#### `runParity(options: Object): Promise` -Spawns a child process to run Parity. If some `cli` flags are passed into the options in `parityElectron`, then those flags will be passed down to Parity itself. +Spawns a child process to run Parity, with optional additional flags. -| Option | Type | Description | -| ----------------- | --------------- | --------------------------------------------------------------------------------------------- | -| `additionalFlasg` | `Array` | Addtional flags to pass to Parity, listed as an array, to be passed to `child_process.spawn`. | -| `onParityError` | `Function` | Callback with `error` as argument when Parity encounters an error. | +| Option | Type | Description | +| ------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------ | +| `options.flags` | `Array` | Additional flags to pass to Parity, listed as an array, to be passed to `child_process.spawn`. | +| `options.onParityError` | `Function` | Callback with `error` as argument when Parity encounters an error. | #### `killParity(): Promise` diff --git a/packages/parity-electron/src/getParityPath.ts b/packages/parity-electron/src/getParityPath.ts index 6a21e227a4d19200e3995db10ff502b7ac967db6..59865c66f2dbc8f10e8b0385711b5e7d90b2e92c 100644 --- a/packages/parity-electron/src/getParityPath.ts +++ b/packages/parity-electron/src/getParityPath.ts @@ -6,7 +6,7 @@ import { app } from 'electron'; import commandExists from 'command-exists'; import { stat } from 'fs'; -import promiseAny from 'promise-any'; +import * as promiseAny from 'promise-any'; import { promisify } from 'util'; import logger from './utils/logger'; diff --git a/packages/parity-electron/src/index.ts b/packages/parity-electron/src/index.ts index a7852affd064a9f0fd1c8b7e2bfe6812561628e3..e50ef57ec66b7c210238dc125068d489b74209f1 100644 --- a/packages/parity-electron/src/index.ts +++ b/packages/parity-electron/src/index.ts @@ -3,8 +3,7 @@ // // SPDX-License-Identifier: BSD-3-Clause -import { CliObject, LoggerFunction } from './types'; -import { setCli } from './utils/cli'; +import { LoggerFunction } from './types'; import { setLogger } from './utils/logger'; export * from './checkClockSync'; @@ -17,11 +16,7 @@ export * from './signerNewToken'; /** * Set default options for @parity/electron. */ -export default (opts: { cli: CliObject; logger: LoggerFunction }) => { - if (opts.cli) { - setCli(opts.cli); - } - +export default (opts: { logger?: LoggerFunction }) => { if (opts.logger) { setLogger(opts.logger); } diff --git a/packages/parity-electron/src/isParityRunning.ts b/packages/parity-electron/src/isParityRunning.ts index c2f86743f7cc85f70f39979e59c535ad24bb430a..2f9bbafbab21e818be2d3d2de9c17b88d34b3d1b 100644 --- a/packages/parity-electron/src/isParityRunning.ts +++ b/packages/parity-electron/src/isParityRunning.ts @@ -6,25 +6,28 @@ import axios from 'axios'; import * as retry from 'async-retry'; -import { cli } from './utils/cli'; import logger from './utils/logger'; -/** - * Try to ping these hosts to test if Parity is running. - */ -const hostsToPing = ['http://127.0.0.1:8545', 'http://127.0.0.1:8546']; -if (cli.wsInterface || cli.wsPort) { - // Also try custom host/port if a --ws-interface or --ws-port flag is passed - hostsToPing.push( - `http://${cli.wsInterface || '127.0.0.1'}:${cli.wsPort || '8546'}` - ); -} - /** * Detect if another instance of parity is already running or not. To achieve * that, we just ping on the common hosts, see {@link hostsToPing} array. */ -export const isParityRunning = async () => { +export const isParityRunning = async ({ + wsInterface = '127.0.0.1', + wsPort = '8546' +}: { + wsInterface: string; + wsPort: number | string; + }) => { + /** + * Try to ping these hosts to test if Parity is running. + */ + const hostsToPing = [ + 'http://127.0.0.1:8545', + 'http://127.0.0.1:8546', + `http://${wsInterface}:${wsPort}` + ]; + try { // Retry to ping as many times as there are hosts in `hostsToPing` await retry( diff --git a/packages/parity-electron/src/runParity.ts b/packages/parity-electron/src/runParity.ts index 159bc1ce5c4507bd934325b3b8ec2e218d03b088..54844c5704ebad4a2796d323e4498a30a07857dc 100644 --- a/packages/parity-electron/src/runParity.ts +++ b/packages/parity-electron/src/runParity.ts @@ -7,9 +7,7 @@ import { chmod } from 'fs'; import { spawn } from 'child_process'; import { promisify } from 'util'; -import { cli, parityArgv } from './utils/cli'; import { getParityPath } from './getParityPath'; -import { isParityRunning } from './isParityRunning'; import logCommand from './utils/logCommand'; import logger from './utils/logger'; @@ -26,25 +24,15 @@ const catchableErrors = [ ]; /** - * Spawns a child process to run Parity. If some cli flags are passed into the - * options in parityElectron, then those flags will be passed down to Parity - * itself. + * Spawns a child process to run Parity. */ -export const runParity = async ( - additionalFlags: string[], - onParityError: (error: Error) => void = () => {} -) => { - // Do not run parity with --no-run-parity - if (cli.runParity === false) { - return; - } - - // Do not run parity if there is already another instance running - const isRunning = await isParityRunning(); - if (isRunning) { - return; - } - +export const runParity = async ({ + flags = [], + onParityError = () => { } +}: { + flags: string[]; + onParityError: (error: Error) => void + }) => { const parityPath = await getParityPath(); // Some users somehow had no +x on the parity binary after downloading @@ -52,14 +40,13 @@ export const runParity = async ( // have rights to do it). try { await fsChmod(parityPath, '755'); - } catch (e) {} + } catch (e) { } let logLastLine = ''; // Always contains last line of the Parity logs - // Run an instance of parity with the correct args - const args = [...parityArgv(), ...additionalFlags]; - parity = spawn(parityPath, args); - logger()('@parity/electron:main')(logCommand(parityPath, args)); + // Run an instance of parity with the correct flags + parity = spawn(parityPath, flags); + logger()('@parity/electron:main')(logCommand(parityPath, flags)); // Save in memory the last line of the log file, for handling error const callback = data => { diff --git a/packages/parity-electron/src/types.d.ts b/packages/parity-electron/src/types.d.ts index 4efe9a064017c21db44e91a63a03b9b839eb65dc..484e5d164f12a0b5dbed8dbed6e4975622ad39d3 100644 --- a/packages/parity-electron/src/types.d.ts +++ b/packages/parity-electron/src/types.d.ts @@ -3,14 +3,4 @@ // // SPDX-License-Identifier: BSD-3-Clause -export type CliObject = { - rawArgs?: string[]; - [key: string]: string | boolean | string[]; -}; - export type LoggerFunction = (namespace: string) => (log: string) => void; - -export type Options = { - cli: CliObject; - logger: LoggerFunction; -}; diff --git a/packages/parity-electron/src/utils/cli.ts b/packages/parity-electron/src/utils/cli.ts deleted file mode 100644 index 1f61f8f2573702b99c4b65473574b118c10a6115..0000000000000000000000000000000000000000 --- a/packages/parity-electron/src/utils/cli.ts +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. -// -// SPDX-License-Identifier: BSD-3-Clause - -import { CliObject } from '../types'; - -let cli: CliObject = {}; - -/** - * Set custom cli options. - * - * @param {*} cliOptions - */ -export const setCli = (cliObject: CliObject) => { - cli = cliObject; -}; - -/** - * Camel-case the given `flag` - * - * @see https://github.com/tj/commander.js/blob/dcddf698c5463795401ad3d6382f5ec5ec060478/index.js#L1160-L1172 - */ -const camelcase = (flag: string) => - flag - .split('-') - .reduce((str, word) => str + word[0].toUpperCase() + word.slice(1)); - -// Now we must think which arguments passed to cli must be passed down to -// parity. -export const parityArgv = () => - cli.rawArgs - .splice(2) // Remove first 2 arguments which are program path - .filter((item, index, array) => { - const key = camelcase(item.replace('--', '').replace('no-', '')); // Remove '--' and then camelCase - - if (key in cli) { - // If the option is consumed by commander.js, then we don't pass down to parity - return false; - } - - // If it's not consumed by commander.js, and starts with '--', then we keep - // it. - if (item.startsWith('--')) { - return true; - } - - // If it's the 1st argument and did not start with --, then we skip it - if (index === 0) { - return false; - } - - const previousKey = camelcase( - array[index - 1].replace('--', '').replace('no-', '') - ); - if (cli[previousKey] === item) { - // If it's an argument of an option consumed by commander.js, then we - // skip it too - return false; - } - - return true; - }); - -export { cli }; diff --git a/yarn.lock b/yarn.lock index de316cfba16fbc1b4b33f5f55d39522a2b905247..44abb8b5610dd6eda8fe3e3c0fa29ec57ef1fde5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3244,6 +3244,10 @@ command-join@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/command-join/-/command-join-2.0.0.tgz#52e8b984f4872d952ff1bdc8b98397d27c7144cf" +commander-remaining-args@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/commander-remaining-args/-/commander-remaining-args-1.2.0.tgz#6fab4cce4a59db1698121f59105364adcb0b4c68" + commander@2.15.x, commander@^2.11.0, commander@^2.15.1, commander@^2.8.1, commander@^2.9.0, commander@~2.15.0: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"