From f4f798584f47adaabd459dbcd650ac4354fcf165 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Thu, 28 Jun 2018 12:38:16 +0200 Subject: [PATCH 1/7] Update git repo --- package.json | 6 +++--- packages/fether-electron/package.json | 7 ++++--- packages/fether-react/package.json | 4 ++-- packages/fether-ui/package.json | 6 +++--- packages/light-hoc/package.json | 6 +++--- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 0b1f034d..6fe3080e 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,10 @@ "license": "BSD-3-Clause", "repository": { "type": "git", - "url": "git+https://github.com/parity-js/light.git" + "url": "git+https://github.com/parity-js/fether.git" }, "bugs": { - "url": "https://github.com/parity-js/light/issues" + "url": "https://github.com/parity-js/fether/issues" }, "keywords": [ "Ethereum", @@ -23,7 +23,7 @@ "Light Client", "Parity" ], - "homepage": "https://github.com/parity-js/light", + "homepage": "https://github.com/parity-js/fether", "workspaces": { "packages": [ "packages/*" diff --git a/packages/fether-electron/package.json b/packages/fether-electron/package.json index 7911506b..ab3da414 100644 --- a/packages/fether-electron/package.json +++ b/packages/fether-electron/package.json @@ -12,10 +12,10 @@ "license": "BSD-3-Clause", "repository": { "type": "git", - "url": "git+https://github.com/parity-js/light.git" + "url": "git+https://github.com/parity-js/fether.git" }, "bugs": { - "url": "https://github.com/parity-js/light/issues" + "url": "https://github.com/parity-js/fether/issues" }, "keywords": [ "Ethereum", @@ -23,7 +23,7 @@ "Light Client", "Parity" ], - "homepage": "https://github.com/parity-js/light", + "homepage": "https://github.com/parity-js/fether", "parity": { "channel": "beta" }, @@ -40,6 +40,7 @@ "test": "echo Skipped." }, "dependencies": { + "@parity/electron": "^0.1.0", "async-retry": "^1.2.1", "axios": "^0.18.0", "checksum": "^0.1.1", diff --git a/packages/fether-react/package.json b/packages/fether-react/package.json index a42cf022..e5e7b473 100644 --- a/packages/fether-react/package.json +++ b/packages/fether-react/package.json @@ -12,10 +12,10 @@ "license": "BSD-3-Clause", "repository": { "type": "git", - "url": "git+https://github.com/parity-js/light.git" + "url": "git+https://github.com/parity-js/fether.git" }, "bugs": { - "url": "https://github.com/parity-js/light/issues" + "url": "https://github.com/parity-js/fether/issues" }, "keywords": [ "Ethereum", diff --git a/packages/fether-ui/package.json b/packages/fether-ui/package.json index 546bfe2c..9096b9b1 100644 --- a/packages/fether-ui/package.json +++ b/packages/fether-ui/package.json @@ -12,10 +12,10 @@ "license": "BSD-3-Clause", "repository": { "type": "git", - "url": "git+https://github.com/parity-js/light.git" + "url": "git+https://github.com/parity-js/fether.git" }, "bugs": { - "url": "https://github.com/parity-js/light/issues" + "url": "https://github.com/parity-js/fether/issues" }, "keywords": [ "Ethereum", @@ -23,7 +23,7 @@ "Light Client", "Parity" ], - "homepage": "https://github.com/parity-js/light", + "homepage": "https://github.com/parity-js/fether", "main": "lib/index.js", "scripts": { "prebuild": "rimraf lib", diff --git a/packages/light-hoc/package.json b/packages/light-hoc/package.json index c100f1c0..3f22122c 100644 --- a/packages/light-hoc/package.json +++ b/packages/light-hoc/package.json @@ -12,10 +12,10 @@ "license": "BSD-3-Clause", "repository": { "type": "git", - "url": "git+https://github.com/parity-js/light.git" + "url": "git+https://github.com/parity-js/fether.git" }, "bugs": { - "url": "https://github.com/parity-js/light/issues" + "url": "https://github.com/parity-js/fether/issues" }, "keywords": [ "Ethereum", @@ -23,7 +23,7 @@ "Light Client", "Parity" ], - "homepage": "https://github.com/parity-js/light", + "homepage": "https://github.com/parity-js/fether", "main": "lib/index.js", "scripts": { "prebuild": "rimraf lib", -- GitLab From 8bea07b57ab987bc46fa891103795ee2502524e8 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Thu, 28 Jun 2018 17:00:18 +0200 Subject: [PATCH 2/7] Create separate package for @parity/electron --- packages/fether-electron/package.json | 2 + .../fether-electron/src/main/cli/index.js | 49 +------ packages/fether-electron/src/main/index.js | 26 ++-- .../src/main/messages/index.js | 8 +- .../src/main/operations/doesParityExist.js | 69 ---------- .../src/main/operations/runParity.js | 124 ------------------ .../src/main/operations/signerNewToken.js | 39 ------ .../main/{operations => utils}/handleError.js | 5 +- .../fether-electron/src/main/utils/pino.js | 29 ++-- packages/parity-electron/README.md | 3 + packages/parity-electron/babel.config.js | 19 +++ packages/parity-electron/package.json | 60 +++++++++ .../src}/fetchParity.js | 59 +++++---- packages/parity-electron/src/getParityPath.js | 107 +++++++++++++++ packages/parity-electron/src/index.js | 24 ++++ .../src}/isParityRunning.js | 13 +- packages/parity-electron/src/runParity.js | 112 ++++++++++++++++ .../parity-electron/src/signerNewToken.js | 49 +++++++ packages/parity-electron/src/utils/cli.js | 65 +++++++++ .../src}/utils/logCommand.js | 0 .../src/utils/parityChannel.js | 17 +++ yarn.lock | 16 ++- 22 files changed, 552 insertions(+), 343 deletions(-) delete mode 100644 packages/fether-electron/src/main/operations/doesParityExist.js delete mode 100644 packages/fether-electron/src/main/operations/runParity.js delete mode 100644 packages/fether-electron/src/main/operations/signerNewToken.js rename packages/fether-electron/src/main/{operations => utils}/handleError.js (92%) create mode 100644 packages/parity-electron/README.md create mode 100644 packages/parity-electron/babel.config.js create mode 100644 packages/parity-electron/package.json rename packages/{fether-electron/src/main/operations => parity-electron/src}/fetchParity.js (68%) create mode 100644 packages/parity-electron/src/getParityPath.js create mode 100644 packages/parity-electron/src/index.js rename packages/{fether-electron/src/main/operations => parity-electron/src}/isParityRunning.js (86%) create mode 100644 packages/parity-electron/src/runParity.js create mode 100644 packages/parity-electron/src/signerNewToken.js create mode 100644 packages/parity-electron/src/utils/cli.js rename packages/{fether-electron/src/main => parity-electron/src}/utils/logCommand.js (100%) create mode 100644 packages/parity-electron/src/utils/parityChannel.js diff --git a/packages/fether-electron/package.json b/packages/fether-electron/package.json index ab3da414..3ddcc51b 100644 --- a/packages/fether-electron/package.json +++ b/packages/fether-electron/package.json @@ -46,10 +46,12 @@ "checksum": "^0.1.1", "command-exists": "^1.2.6", "commander": "^2.15.1", + "debug": "^3.1.0", "electron-dl": "^1.11.0", "fether-react": "^0.1.0", "menubar": "^5.2.3", "pino": "^4.16.1", + "pino-debug": "^1.1.1", "pino-multi-stream": "^3.1.2", "promise-any": "^0.2.0", "source-map-support": "^0.5.6" diff --git a/packages/fether-electron/src/main/cli/index.js b/packages/fether-electron/src/main/cli/index.js index 27d2ef5d..87098297 100644 --- a/packages/fether-electron/src/main/cli/index.js +++ b/packages/fether-electron/src/main/cli/index.js @@ -36,51 +36,4 @@ cli ) .parse(process.argv); -/** - * Camel-case the given `flag` - * - * @param {String} flag - * @return {String} - * @see https://github.com/tj/commander.js/blob/dcddf698c5463795401ad3d6382f5ec5ec060478/index.js#L1160-L1172 - */ -const camelcase = flag => - 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 }; +export default cli; diff --git a/packages/fether-electron/src/main/index.js b/packages/fether-electron/src/main/index.js index 2f2e8e4d..3aaae717 100644 --- a/packages/fether-electron/src/main/index.js +++ b/packages/fether-electron/src/main/index.js @@ -3,23 +3,27 @@ // // SPDX-License-Identifier: BSD-3-Clause +import parityElectron, { + getParityPath, + fetchParity, + runParity, + killParity +} from '@parity/electron'; import electron from 'electron'; import path from 'path'; import url from 'url'; import addMenu from './menu'; -import { doesParityExist } from './operations/doesParityExist'; -import fetchParity from './operations/fetchParity'; -import handleError from './operations/handleError'; +import cli from './cli'; +import handleError from './utils/handleError'; import messages from './messages'; +import { parity } from '../../package.json'; +import pino from './utils/pino'; import { productName } from '../../electron-builder.json'; -import Pino from './utils/pino'; -import { runParity, killParity } from './operations/runParity'; import staticPath from './utils/staticPath'; const { app, BrowserWindow, ipcMain, session } = electron; let mainWindow; -const pino = Pino(); function createWindow () { pino.info(`Starting ${productName}...`); @@ -29,10 +33,16 @@ function createWindow () { width: 360 }); - doesParityExist() + // Set options for @parity/electron + parityElectron({ + cli, + parityChannel: parity.channel + }); + + getParityPath() .catch(() => fetchParity(mainWindow)) // Install parity if not present .then(() => runParity(mainWindow)) - .catch(handleError); // Errors should be handled before, this is really just in case + .catch(handleError); // Opens file:///path/to/build/index.html in prod mode, or whatever is // passed to ELECTRON_START_URL diff --git a/packages/fether-electron/src/main/messages/index.js b/packages/fether-electron/src/main/messages/index.js index a4028594..3f1687e3 100644 --- a/packages/fether-electron/src/main/messages/index.js +++ b/packages/fether-electron/src/main/messages/index.js @@ -3,15 +3,17 @@ // // SPDX-License-Identifier: BSD-3-Clause -import signerNewToken from '../operations/signerNewToken'; +import { signerNewToken } from '@parity/electron'; /** * Handle all asynchronous messages from renderer to main. */ -export default (event, arg) => { +export default async (event, arg) => { switch (arg) { case 'signer-new-token': { - signerNewToken(event); + const token = await signerNewToken(); + // Send back the token to the renderer process + event.sender.send('asynchronous-reply', token); break; } default: diff --git a/packages/fether-electron/src/main/operations/doesParityExist.js b/packages/fether-electron/src/main/operations/doesParityExist.js deleted file mode 100644 index 1beab61b..00000000 --- a/packages/fether-electron/src/main/operations/doesParityExist.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. -// -// SPDX-License-Identifier: BSD-3-Clause - -import { app } from 'electron'; -import commandExists from 'command-exists'; -import fs from 'fs'; -import promiseAny from 'promise-any'; -import { promisify } from 'util'; - -import Pino from '../utils/pino'; - -const fsExists = promisify(fs.stat); -const pino = Pino(); - -// The default path to install parity, in case there's no other instance found -// on the machine. -export const defaultParityPath = `${app.getPath('userData')}/parity${ - process.platform === 'win32' ? '.exe' : '' -}`; - -let parityPath; // The real parity path, will be populated after doesParityExist Promise resolves - -// OS locations to test if parity binary exists -const locations = { - linux: ['/bin/parity', '/usr/bin/parity', '/usr/local/bin/parity'], - darwin: ['/Applications/Parity Ethereum.app/Contents/MacOS/parity'], - win32: ['C:\\Program Files\\Parity Technologies\\Parity\\parity.exe'] -}; - -/** - * This function checks if parity has been installed on the local machine: - * - first check if the program is in $PATH, using `command-exists` - * - then check the OS default installation dir if a parity folder exists - * - finally check parity-ui's own userData folder - * This function should run in node env. - * - * @return Promise - Resolves to a string which is the command to run parity. - */ -export const doesParityExist = () => { - return commandExists('parity') // First test if `parity` command exists - .then(() => 'parity') // If yes, return `parity` as command to launch parity - .catch(() => - // Then test if OS-specific locations contain parity - promiseAny( - locations[process.platform].map(location => - fsExists(location).then(() => location) - ) - ) - ) - .catch(() => - // Finally test userData folder - fsExists(defaultParityPath).then(() => defaultParityPath) - ) - .then(path => { - parityPath = path; // Save the final result in module variable - pino.info(`Parity found on machine, can be run with "${path}".`); - return path; - }) - .catch(err => { - pino.info(`Parity not found on machine.`); - throw err; - }); -}; - -export const getParityPath = () => { - return parityPath; -}; diff --git a/packages/fether-electron/src/main/operations/runParity.js b/packages/fether-electron/src/main/operations/runParity.js deleted file mode 100644 index 50035340..00000000 --- a/packages/fether-electron/src/main/operations/runParity.js +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. -// -// SPDX-License-Identifier: BSD-3-Clause - -import { app } from 'electron'; -import fs from 'fs'; -import { spawn } from 'child_process'; -import { promisify } from 'util'; - -import { cli, parityArgv } from '../cli'; -import isParityRunning from './isParityRunning'; -import handleError from './handleError'; -import { getParityPath } from './doesParityExist'; -import logCommand from '../utils/logCommand'; -import Pino from '../utils/pino'; - -const fsChmod = promisify(fs.chmod); -const pino = Pino(); -const pinoParity = Pino({ name: 'parity' }); - -let parity = null; // Will hold the running parity instance - -// These are errors output by parity, which Parity UI ignores (i.e. doesn't -// panic). They happen when an instance of parity is already running, and -// parity-ui tries to launch another one. -const catchableErrors = [ - 'is already in use, make sure that another instance of an Ethereum client is not running', - 'IO error: While lock file:' -]; - -export const runParity = async mainWindow => { - try { - // 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(mainWindow); - if (isRunning) { - return; - } - - // Do not run parity if parityPath has not been calculated. Shouldn't - // happen as we always run runParity after doesParityExist resolves. - if (!getParityPath()) { - throw new Error('Attempting to run Parity before parityPath is set.'); - } - - // Some users somehow had no +x on the parity binary after downloading - // it. We try to set it here (no guarantee it will work, we might not - // have rights to do it). - try { - await fsChmod(getParityPath(), '755'); - } catch (e) {} - - let logLastLine; // Always contains last line of the Parity logs - - // Run an instance of parity with the correct args - const args = [...parityArgv, '--light']; - parity = spawn(getParityPath(), args); - pino.info(logCommand(getParityPath(), args)); - - // Save in memory the last line of the log file, for handling error - const callback = data => { - if (data && data.length) { - logLastLine = data.toString(); - } - pinoParity.info(data.toString()); - }; - parity.stdout.on('data', callback); - parity.stderr.on('data', callback); - - parity.on('error', err => { - handleError(err, 'An error occured while running parity.'); - }); - parity.on('close', (exitCode, signal) => { - if (exitCode === 0) { - return; - } - - // When there's already an instance of parity running, then the log - // is logging a particular line, see below. In this case, we just - // silently ignore our local instance, and let the 1st parity - // instance be the main one. - if ( - logLastLine && - catchableErrors.some(error => logLastLine.includes(error)) - ) { - pino.warn( - 'Another instance of parity is running, closing local instance.' - ); - return; - } - - // If the exit code is not 0, then we show some error message - if (Object.keys(parityArgv).length > 0) { - app.exit(1); - } else { - handleError( - new Error(`Exit code ${exitCode}, with signal ${signal}.`), - 'An error occured while running parity.' - ); - } - }); - - // Notify the renderers - mainWindow.webContents.send('parity-running', true); - global.isParityRunning = true; // Send this variable to renderes via IPC - - return Promise.resolve(); - } catch (err) { - handleError(err, 'An error occured while running parity.'); - } -}; - -export const killParity = () => { - if (parity) { - pino.info('Stopping parity.'); - parity.kill(); - parity = null; - } -}; diff --git a/packages/fether-electron/src/main/operations/signerNewToken.js b/packages/fether-electron/src/main/operations/signerNewToken.js deleted file mode 100644 index 633e4197..00000000 --- a/packages/fether-electron/src/main/operations/signerNewToken.js +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. -// -// SPDX-License-Identifier: BSD-3-Clause - -import { spawn } from 'child_process'; - -import { getParityPath } from './doesParityExist'; -import logCommand from '../utils/logCommand'; -import Pino from '../utils/pino'; - -const pino = Pino(); - -export default event => { - pino.info('Requesting new token.'); - - // Generate a new token - const paritySigner = spawn(getParityPath(), ['signer', 'new-token']); - - pino.info(logCommand(getParityPath(), ['signer', 'new-token'])); - - // Listen to the output of the previous command - paritySigner.stdout.on('data', data => { - // If the output line is xxxx-xxxx-xxxx-xxxx, then it's our token - const match = data - .toString() - .match( - /[a-zA-Z0-9]{4}(-)?[a-zA-Z0-9]{4}(-)?[a-zA-Z0-9]{4}(-)?[a-zA-Z0-9]{4}/ - ); - - if (match) { - const token = match[0]; - - // Send back the token to the renderer process - event.sender.send('asynchronous-reply', token); - paritySigner.kill(); // We don't need the signer anymore - } - }); -}; diff --git a/packages/fether-electron/src/main/operations/handleError.js b/packages/fether-electron/src/main/utils/handleError.js similarity index 92% rename from packages/fether-electron/src/main/operations/handleError.js rename to packages/fether-electron/src/main/utils/handleError.js index 3c73698b..8fbd608e 100644 --- a/packages/fether-electron/src/main/operations/handleError.js +++ b/packages/fether-electron/src/main/utils/handleError.js @@ -6,9 +6,8 @@ import { app, dialog, shell } from 'electron'; import { bugs, name, parity } from '../../../package.json'; -import Pino from '../utils/pino'; +import pino from './pino'; -const pino = Pino(); const logFile = `${app.getPath('userData')}/${name}.log`; export default (err, message = 'An error occurred.') => { @@ -16,7 +15,7 @@ export default (err, message = 'An error occurred.') => { dialog.showMessageBox( { buttons: ['OK', 'Open logs'], - defaultId: 0, + defaultId: 0, // Default button id detail: `Please attach the following debugging info: OS: ${process.platform} Arch: ${process.arch} diff --git a/packages/fether-electron/src/main/utils/pino.js b/packages/fether-electron/src/main/utils/pino.js index cab0e5f0..fff8af9d 100644 --- a/packages/fether-electron/src/main/utils/pino.js +++ b/packages/fether-electron/src/main/utils/pino.js @@ -4,14 +4,16 @@ // SPDX-License-Identifier: BSD-3-Clause import { app } from 'electron'; +import debug from 'debug'; import fs from 'fs'; import { multistream } from 'pino-multi-stream'; -import pino from 'pino'; +import Pino from 'pino'; +import pinoDebug from 'pino-debug'; import { name } from '../../../package.json'; // Pino by default outputs JSON. We prettify that. -const pretty = pino.pretty(); +const pretty = Pino.pretty(); pretty.pipe(process.stdout); // Create userData folder if it doesn't exist @@ -32,14 +34,15 @@ const streams = [ { level: 'info', stream: pretty } ]; -/** - * Create a pino instance - * - * @param {Object} opts - Options to pass to pino. Defaults to { name: 'electron' }. - * @example - * import Pino from './utils/pino'; - * const pino1 = Pino(); - * const pino2 = Pino({ name: 'parity' }); - */ -export default opts => - pino({ name: 'electron', ...opts }, multistream(streams)); +const pino = Pino({ name }, multistream(streams)); + +// @parity/electron's debug logs are in namespace "@parity/log", we enable +// them by default. +debug.enable('@parity/electron'); +pinoDebug(pino, { + map: { + '@parity/electron': 'debug' + } +}); + +export default pino; diff --git a/packages/parity-electron/README.md b/packages/parity-electron/README.md new file mode 100644 index 00000000..5ef12eb6 --- /dev/null +++ b/packages/parity-electron/README.md @@ -0,0 +1,3 @@ +# @parity/electron + +Control the Parity client from electron. diff --git a/packages/parity-electron/babel.config.js b/packages/parity-electron/babel.config.js new file mode 100644 index 00000000..9fae1bf2 --- /dev/null +++ b/packages/parity-electron/babel.config.js @@ -0,0 +1,19 @@ +module.exports = { + plugins: [ + '@babel/plugin-proposal-class-properties', + [ + '@babel/plugin-transform-runtime', + { + helpers: false, + polyfill: false, + regenerator: true, + moduleName: '@babel/runtime' + } + ] + ], + presets: [ + '@babel/preset-env', + '@babel/preset-react', + ['@babel/preset-stage-0', { decoratorsLegacy: true }] + ] +}; diff --git a/packages/parity-electron/package.json b/packages/parity-electron/package.json new file mode 100644 index 00000000..b36a483e --- /dev/null +++ b/packages/parity-electron/package.json @@ -0,0 +1,60 @@ +{ + "name": "@parity/electron", + "description": "Fether Wallet", + "version": "0.1.0", + "private": true, + "author": "Parity Team ", + "maintainers": [ + "Jaco Greeff", + "Amaury Martiny" + ], + "contributors": [], + "license": "BSD-3-Clause", + "repository": { + "type": "git", + "url": "git+https://github.com/parity-js/fether.git" + }, + "bugs": { + "url": "https://github.com/parity-js/fether/issues" + }, + "keywords": [ + "Ethereum", + "Light", + "Light Client", + "Parity" + ], + "homepage": "https://github.com/parity-js/fether", + "main": "lib/index.js", + "scripts": { + "prebuild": "rimraf lib", + "build": "babel src --out-dir lib", + "lint": "semistandard 'src/**/*.js' --parser babel-eslint", + "start": "yarn build --watch" + }, + "dependencies": { + "async-retry": "^1.2.1", + "axios": "^0.18.0", + "checksum": "^0.1.1", + "command-exists": "^1.2.6", + "commander": "^2.15.1", + "debug": "^3.1.0", + "electron-dl": "^1.11.0", + "fether-react": "^0.1.0", + "menubar": "^5.2.3", + "pino": "^4.16.1", + "pino-multi-stream": "^3.1.2", + "promise-any": "^0.2.0", + "source-map-support": "^0.5.6" + }, + "devDependencies": { + "@babel/plugin-transform-runtime": "^7.0.0-beta.51", + "babel-eslint": "^8.2.3", + "copyfiles": "^2.0.0", + "cross-env": "^5.2.0", + "electron": "^2.0.2", + "electron-builder": "^20.15.1", + "electron-webpack": "^2.1.2", + "semistandard": "^12.0.1", + "webpack": "^4.7.0" + } +} diff --git a/packages/fether-electron/src/main/operations/fetchParity.js b/packages/parity-electron/src/fetchParity.js similarity index 68% rename from packages/fether-electron/src/main/operations/fetchParity.js rename to packages/parity-electron/src/fetchParity.js index e95cda34..01296305 100644 --- a/packages/fether-electron/src/main/operations/fetchParity.js +++ b/packages/parity-electron/src/fetchParity.js @@ -6,19 +6,21 @@ import { app } from 'electron'; import axios from 'axios'; import cs from 'checksum'; +import debug from 'debug'; import { download } from 'electron-dl'; import fs from 'fs'; import { promisify } from 'util'; import retry from 'async-retry'; -import { defaultParityPath, doesParityExist } from './doesParityExist'; -import handleError from './handleError'; -import { parity } from '../../../package.json'; -import Pino from '../utils/pino'; +import { defaultParityPath, getParityPath } from './getParityPath'; +import { name } from '../package.json'; +import parityChannel from './utils/parityChannel'; const checksum = promisify(cs.file); +const logger = debug(`${name}:main`); const fsChmod = promisify(fs.chmod); -const pino = Pino(); +const fsStat = promisify(fs.stat); +const fsUnlink = promisify(fs.unlink); const VANITY_URL = 'https://vanity-service.parity.io/parity-binaries'; @@ -56,32 +58,27 @@ const getOs = () => { /** * Remove parity binary in the userData folder */ -const deleteParity = () => { - if (fs.statSync(defaultParityPath)) { - fs.unlinkSync(defaultParityPath); - } +export const deleteParity = async () => { + try { + const parityPath = await defaultParityPath(); + await fsStat(parityPath); + await fsUnlink(parityPath); + } catch (e) {} }; // Fetch parity from https://vanity-service.parity.io/parity-binaries -export default mainWindow => { +export const fetchParity = mainWindow => { try { return retry( async (_, attempt) => { if (attempt > 1) { - pino.warn(`Retrying.`); + logger('Retrying.'); } // Fetch the metadata of the correct version of parity - pino.info( - `Downloading from ${VANITY_URL}?version=${ - parity.channel - }&os=${getOs()}&architecture=${getArch()}.` - ); - const { data } = await axios.get( - `${VANITY_URL}?version=${ - parity.channel - }&os=${getOs()}&architecture=${getArch()}` - ); + const metadataUrl = `${VANITY_URL}?version=${parityChannel}&os=${getOs()}&architecture=${getArch()}`; + logger(`Downloading from ${metadataUrl}.`); + const { data } = await axios.get(metadataUrl); // Get the binary's url const { downloadUrl, checksum: expectedChecksum } = data[0].files.find( @@ -113,21 +110,27 @@ export default mainWindow => { await fsChmod(downloadPath, '755'); // Double-check that Parity exists now. - return doesParityExist(); + console.log('fetchParity'); + const parityPath = await getParityPath(); + return parityPath; }, { - onRetry: err => { - pino.warn(err); + onRetry: async err => { + console.log('onRetry'); + debug(err); // Everytime we retry, we remove the parity file we just downloaded. - // This needs to be done syncly, since onRetry is sync - deleteParity(); + // This needs to be done syncly normally, since onRetry is sync + // https://github.com/zeit/async-retry/issues/43 + return deleteParity(); }, retries: 3 } ); } catch (err) { - deleteParity(); - handleError(err, 'An error occured while fetching parity.'); + console.log('error in fetchParity'); + return deleteParity().then(() => { + Promise.reject(err); + }); } }; diff --git a/packages/parity-electron/src/getParityPath.js b/packages/parity-electron/src/getParityPath.js new file mode 100644 index 00000000..f4ad5530 --- /dev/null +++ b/packages/parity-electron/src/getParityPath.js @@ -0,0 +1,107 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + +import { app } from 'electron'; +import commandExists from 'command-exists'; +import debug from 'debug'; +import fs from 'fs'; +import promiseAny from 'promise-any'; +import { promisify } from 'util'; + +import { name } from '../package.json'; + +const logger = debug(`${name}:main`); +const fsStat = promisify(fs.stat); + +// The default path to install parity, in case there's no other instance found +// on the machine. +export const defaultParityPath = () => + Promise.resolve( + `${app.getPath('userData')}/parity${ + process.platform === 'win32' ? '.exe' : '' + }` + ); + +let parityPath; // The real parity path, will be populated after doesParityExist Promise resolves + +/** + * Test if `parity` command is in $PATH. + */ +const isParityInPath = async () => { + const parityCommandExists = await commandExists('parity'); + if (parityCommandExists) { + // If yes, return `parity` as command to launch parity + return 'parity'; + } +}; + +/** + * Test if Parity is in the common OS locations. + */ +const isParityInOs = async () => { + // OS locations to test if parity binary exists + const locations = { + linux: ['/bin/parity', '/usr/bin/parity', '/usr/local/bin/parity'], + darwin: ['/Applications/Parity Ethereum.app/Contents/MacOS/parity'], + win32: ['C:\\Program Files\\Parity Technologies\\Parity\\parity.exe'] + }; + return promiseAny( + locations[process.platform].map(location => + fsStat(location).then(() => location) + ) + ); +}; + +/** + * Test is Parity is already downloaded in electron app's userData folder. + */ +const isParityInUserData = async () => { + const parityPath = await defaultParityPath(); + await fsStat(parityPath); + return parityPath; +}; + +/** + * This function checks if parity has been installed on the local machine: + * - first check if the program is in $PATH, using `command-exists` + * - then check the OS default installation dir if a parity folder exists + * - finally check parity-ui's own userData folder + * This function should run in node env. + * + * @return Promise - Resolves to a string which is the command to run parity. + */ +const doesParityExist = async () => { + try { + // First test if `parity` command exists + return await isParityInPath(); + } catch (e) {} + + try { + // Then test if parity is in OS + return await isParityInOs(); + } catch (e) {} + + try { + // Finally test userData folder + return await isParityInUserData(); + } catch (e) { + throw new Error('Parity not found.'); + } +}; + +export const getParityPath = async () => { + if (parityPath) { + return parityPath; + } + try { + const path = await doesParityExist(); + parityPath = path; // Save the final result in module variable + logger(`Parity found on machine, can be run with "${path}".`); + return path; + } catch (err) { + logger(`Parity not found on machine.`); + throw err; + } +}; diff --git a/packages/parity-electron/src/index.js b/packages/parity-electron/src/index.js new file mode 100644 index 00000000..00ac3bd6 --- /dev/null +++ b/packages/parity-electron/src/index.js @@ -0,0 +1,24 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + +import { setCli } from './utils/cli'; +import { setParityChannel } from './utils/parityChannel'; + +export * from './getParityPath'; +export * from './fetchParity'; +export * from './isParityRunning'; +export * from './runParity'; +export * from './signerNewToken'; + +// Set default options for @parity/electron +export default opts => { + if (opts.cli) { + setCli(opts.cli); + } + + if (opts.parityChannel) { + setParityChannel(opts.parityChannel); + } +}; diff --git a/packages/fether-electron/src/main/operations/isParityRunning.js b/packages/parity-electron/src/isParityRunning.js similarity index 86% rename from packages/fether-electron/src/main/operations/isParityRunning.js rename to packages/parity-electron/src/isParityRunning.js index c17cf2e0..0e5e3d33 100644 --- a/packages/fether-electron/src/main/operations/isParityRunning.js +++ b/packages/parity-electron/src/isParityRunning.js @@ -4,12 +4,13 @@ // SPDX-License-Identifier: BSD-3-Clause import axios from 'axios'; +import debug from 'debug'; import retry from 'async-retry'; -import { cli } from '../cli'; -import Pino from '../utils/pino'; +import { cli } from './utils/cli'; +import { name } from '../package.json'; -const pino = Pino(); +const logger = debug(`${name}:main`); // Try to ping these hosts const hostsToPing = ['http://127.0.0.1:8545', 'http://127.0.0.1:8546']; @@ -26,14 +27,14 @@ if (cli.wsInterface || cli.wsPort) { * * @return [Promise] - Promise that resolves to true or false. */ -const isParityRunning = async mainWindow => { +export const isParityRunning = async mainWindow => { try { // Retry to ping as many times as there are hosts in `hostsToPing` await retry( async (_, attempt) => { const host = hostsToPing[attempt - 1]; // Attempt starts with 1 await axios.get(host); - pino.info( + logger( `Another instance of parity is already running on ${host}, skip running local instance.` ); @@ -49,5 +50,3 @@ const isParityRunning = async mainWindow => { return false; } }; - -export default isParityRunning; diff --git a/packages/parity-electron/src/runParity.js b/packages/parity-electron/src/runParity.js new file mode 100644 index 00000000..8648137a --- /dev/null +++ b/packages/parity-electron/src/runParity.js @@ -0,0 +1,112 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + +import { app } from 'electron'; +import debug from 'debug'; +import fs 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 { name } from '../package.json'; + +const logger = debug(`${name}:main`); + +const fsChmod = promisify(fs.chmod); + +let parity = null; // Will hold the running parity instance + +// These are errors output by parity, which we should ignore (i.e. don't +// panic). They happen when an instance of parity is already running, and +// parity-electron tries to launch another one. +const catchableErrors = [ + 'is already in use, make sure that another instance of an Ethereum client is not running', + 'IO error: While lock file:' +]; + +export const runParity = async mainWindow => { + // 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(mainWindow); + if (isRunning) { + return; + } + + const parityPath = await getParityPath(); + + // Some users somehow had no +x on the parity binary after downloading + // it. We try to set it here (no guarantee it will work, we might not + // have rights to do it). + try { + await fsChmod(parityPath, '755'); + } catch (e) {} + + let logLastLine; // Always contains last line of the Parity logs + + // Run an instance of parity with the correct args + const args = [...parityArgv(), '--light']; + parity = spawn(parityPath, args); + logger(logCommand(parityPath, args)); + + // Save in memory the last line of the log file, for handling error + const callback = data => { + if (data && data.length) { + logLastLine = data.toString(); + } + debug(`${name}:parity`)(data.toString()); + }; + parity.stdout.on('data', callback); + parity.stderr.on('data', callback); + + parity.on('error', err => { + throw err; + }); + parity.on('close', (exitCode, signal) => { + if (exitCode === 0) { + return; + } + + // When there's already an instance of parity running, then the log + // is logging a particular line, see below. In this case, we just + // silently ignore our local instance, and let the 1st parity + // instance be the main one. + if ( + logLastLine && + catchableErrors.some(error => logLastLine.includes(error)) + ) { + logger('Another instance of parity is running, closing local instance.'); + return; + } + + // If the exit code is not 0, then we show some error message + if (Object.keys(parityArgv()).length > 0) { + app.exit(1); + } else { + throw new Error(`Exit code ${exitCode}, with signal ${signal}.`); + } + }); + + // Notify the renderers + mainWindow.webContents.send('parity-running', true); + global.isParityRunning = true; // Send this variable to renderes via IPC + + return Promise.resolve(true); +}; + +export const killParity = () => { + if (parity) { + debug('Stopping parity.'); + parity.kill(); + parity = null; + } + return Promise.resolve(true); +}; diff --git a/packages/parity-electron/src/signerNewToken.js b/packages/parity-electron/src/signerNewToken.js new file mode 100644 index 00000000..f867eaf6 --- /dev/null +++ b/packages/parity-electron/src/signerNewToken.js @@ -0,0 +1,49 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + +import debug from 'debug'; +import { spawn } from 'child_process'; + +import { getParityPath } from './getParityPath'; +import logCommand from './utils/logCommand'; +import { name } from '../package.json'; + +const logger = debug(`${name}:main`); + +/** + * Launch a parity instance to get a secure token. + */ +export const signerNewToken = () => + new Promise(async (resolve, reject) => { + logger('Requesting new token.'); + + const parityPath = await getParityPath(); + + // Generate a new token + const paritySigner = spawn(parityPath, ['signer', 'new-token']); + logger(logCommand(parityPath, ['signer', 'new-token'])); + + // Listen to the output of the previous command + paritySigner.stdout.on('data', data => { + // If the output line is xxxx-xxxx-xxxx-xxxx, then it's our token + const match = data + .toString() + .match( + /[a-zA-Z0-9]{4}(-)?[a-zA-Z0-9]{4}(-)?[a-zA-Z0-9]{4}(-)?[a-zA-Z0-9]{4}/ + ); + + if (match) { + const token = match[0]; + paritySigner.kill(); // We don't need the signer anymore + logger('Successfully extracted token.'); + resolve(token); + } + }); + + // If after 2s we still didn't find the token, consider it failed. + setTimeout(() => { + reject(new Error('Error extracting token.')); + }, 2000); + }); diff --git a/packages/parity-electron/src/utils/cli.js b/packages/parity-electron/src/utils/cli.js new file mode 100644 index 00000000..f2ca25d6 --- /dev/null +++ b/packages/parity-electron/src/utils/cli.js @@ -0,0 +1,65 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + +let cli = {}; + +/** + * Set custom cli options. + * + * @param {*} cliOptions + */ +export const setCli = cliObject => { + cli = cliObject; +}; + +/** + * Camel-case the given `flag` + * + * @param {String} flag + * @return {String} + * @see https://github.com/tj/commander.js/blob/dcddf698c5463795401ad3d6382f5ec5ec060478/index.js#L1160-L1172 + */ +const camelcase = flag => + 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/packages/fether-electron/src/main/utils/logCommand.js b/packages/parity-electron/src/utils/logCommand.js similarity index 100% rename from packages/fether-electron/src/main/utils/logCommand.js rename to packages/parity-electron/src/utils/logCommand.js diff --git a/packages/parity-electron/src/utils/parityChannel.js b/packages/parity-electron/src/utils/parityChannel.js new file mode 100644 index 00000000..c8f50c4d --- /dev/null +++ b/packages/parity-electron/src/utils/parityChannel.js @@ -0,0 +1,17 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + +let parityChannel = 'beta'; // Fetch beta by default + +/** + * Set custom parity channel. + * + * @param {*} channel + */ +export const setParityChannel = channel => { + parityChannel = channel; +}; + +export default parityChannel; diff --git a/yarn.lock b/yarn.lock index 79dcd680..c4f78b18 100644 --- a/yarn.lock +++ b/yarn.lock @@ -706,6 +706,13 @@ dependencies: regenerator-transform "^0.12.4" +"@babel/plugin-transform-runtime@^7.0.0-beta.51": + version "7.0.0-beta.51" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.0.0-beta.51.tgz#0c9cab174f4e3e131659fd65c5ce8e3d73376820" + dependencies: + "@babel/helper-module-imports" "7.0.0-beta.51" + "@babel/helper-plugin-utils" "7.0.0-beta.51" + "@babel/plugin-transform-shorthand-properties@7.0.0-beta.51": version "7.0.0-beta.51" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.0.0-beta.51.tgz#ddbc0b1ae1ddb3bcfe6969f2c968103f11e32bd9" @@ -8819,6 +8826,13 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" +pino-debug@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pino-debug/-/pino-debug-1.1.1.tgz#7986a1b3de232ea4cee488a0ebcea7073772a622" + dependencies: + debug "^3.1.0" + pino "^4.0.2" + pino-multi-stream@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/pino-multi-stream/-/pino-multi-stream-3.1.2.tgz#261af1b66caf208abe9c97f1bad1990d5c25d303" @@ -8829,7 +8843,7 @@ pino-std-serializers@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-2.1.0.tgz#01953dcaecd5f43b331ecf2e312a49c9fd64851c" -pino@^4.16.1, pino@^4.7.1: +pino@^4.0.2, pino@^4.16.1, pino@^4.7.1: version "4.17.3" resolved "https://registry.yarnpkg.com/pino/-/pino-4.17.3.tgz#3536ea36f6258356ba8891d18dbf1afed41fcf09" dependencies: -- GitLab From aa866a9d55065d64e88ffde6ab58303f0d5c6de5 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Thu, 28 Jun 2018 17:23:03 +0200 Subject: [PATCH 3/7] Fix bugs in @parity/electron --- packages/fether-electron/src/main/index.js | 19 +++++++++++++++++-- .../src/main/utils/handleError.js | 10 +++++++--- .../fether-react/src/stores/parityStore.js | 6 ++++-- packages/parity-electron/src/fetchParity.js | 9 ++------- .../parity-electron/src/isParityRunning.js | 6 +----- packages/parity-electron/src/runParity.js | 16 +++++----------- 6 files changed, 36 insertions(+), 30 deletions(-) diff --git a/packages/fether-electron/src/main/index.js b/packages/fether-electron/src/main/index.js index 3aaae717..00cb556d 100644 --- a/packages/fether-electron/src/main/index.js +++ b/packages/fether-electron/src/main/index.js @@ -39,9 +39,24 @@ function createWindow () { parityChannel: parity.channel }); + // Look if Parity is installed getParityPath() - .catch(() => fetchParity(mainWindow)) // Install parity if not present - .then(() => runParity(mainWindow)) + .catch(() => + // Install parity if not present + fetchParity(mainWindow, progress => + // Notify the renderers on download progress + mainWindow.webContents.send('parity-download-progress', progress) + ) + ) + .then(() => + // Run parity when installed + runParity(err => handleError(err, 'An error occured with Parity.')) + ) + .then(() => { + // Notify the renderers + mainWindow.webContents.send('parity-running', true); + global.isParityRunning = true; // Send this variable to renderes via IPC + }) .catch(handleError); // Opens file:///path/to/build/index.html in prod mode, or whatever is diff --git a/packages/fether-electron/src/main/utils/handleError.js b/packages/fether-electron/src/main/utils/handleError.js index 8fbd608e..f452bb43 100644 --- a/packages/fether-electron/src/main/utils/handleError.js +++ b/packages/fether-electron/src/main/utils/handleError.js @@ -16,15 +16,19 @@ export default (err, message = 'An error occurred.') => { { buttons: ['OK', 'Open logs'], defaultId: 0, // Default button id - detail: `Please attach the following debugging info: + detail: `Please file an issue at ${ + bugs.url + }. Please attach the following debugging info: + OS: ${process.platform} Arch: ${process.arch} Channel: ${parity.channel} Error: ${err.message} Please also attach the contents of the following file: -${logFile}`, - message: `${message} Please file an issue at ${bugs.url}.`, +${logFile}. +Click on "Open logs" to open this file.`, + message: `${message}`, title: 'Parity Error', type: 'error' }, diff --git a/packages/fether-react/src/stores/parityStore.js b/packages/fether-react/src/stores/parityStore.js index 45305196..d8f4ac8f 100644 --- a/packages/fether-react/src/stores/parityStore.js +++ b/packages/fether-react/src/stores/parityStore.js @@ -3,14 +3,16 @@ // // SPDX-License-Identifier: BSD-3-Clause -import Api from '@parity/api'; import { action, observable } from 'mobx'; +import Api from '@parity/api'; import isElectron from 'is-electron'; import light from '@parity/light.js'; import store from 'store'; +import Debug from '../utils/debug'; import LS_PREFIX from './utils/lsPrefix'; +const debug = Debug('sendStore'); const electron = isElectron() ? window.require('electron') : null; const LS_KEY = `${LS_PREFIX}::secureToken`; @@ -29,7 +31,7 @@ class ParityStore { } if (!electron) { - console.log( + debug( 'Not in Electron, ParityStore will only have limited capabilities.' ); return; diff --git a/packages/parity-electron/src/fetchParity.js b/packages/parity-electron/src/fetchParity.js index 01296305..b4056e85 100644 --- a/packages/parity-electron/src/fetchParity.js +++ b/packages/parity-electron/src/fetchParity.js @@ -67,7 +67,7 @@ export const deleteParity = async () => { }; // Fetch parity from https://vanity-service.parity.io/parity-binaries -export const fetchParity = mainWindow => { +export const fetchParity = (mainWindow, onProgress) => { try { return retry( async (_, attempt) => { @@ -88,9 +88,7 @@ export const fetchParity = mainWindow => { // Start downloading. This will install parity into defaultParityPath. const downloadItem = await download(mainWindow, downloadUrl, { directory: app.getPath('userData'), - onProgress: progress => - // Notify the renderers - mainWindow.webContents.send('parity-download-progress', progress) + onProgress }); const downloadPath = downloadItem.getSavePath(); // Equal to defaultParityPath @@ -110,13 +108,11 @@ export const fetchParity = mainWindow => { await fsChmod(downloadPath, '755'); // Double-check that Parity exists now. - console.log('fetchParity'); const parityPath = await getParityPath(); return parityPath; }, { onRetry: async err => { - console.log('onRetry'); debug(err); // Everytime we retry, we remove the parity file we just downloaded. @@ -128,7 +124,6 @@ export const fetchParity = mainWindow => { } ); } catch (err) { - console.log('error in fetchParity'); return deleteParity().then(() => { Promise.reject(err); }); diff --git a/packages/parity-electron/src/isParityRunning.js b/packages/parity-electron/src/isParityRunning.js index 0e5e3d33..2592981e 100644 --- a/packages/parity-electron/src/isParityRunning.js +++ b/packages/parity-electron/src/isParityRunning.js @@ -27,7 +27,7 @@ if (cli.wsInterface || cli.wsPort) { * * @return [Promise] - Promise that resolves to true or false. */ -export const isParityRunning = async mainWindow => { +export const isParityRunning = async () => { try { // Retry to ping as many times as there are hosts in `hostsToPing` await retry( @@ -37,10 +37,6 @@ export const isParityRunning = async mainWindow => { logger( `Another instance of parity is already running on ${host}, skip running local instance.` ); - - // Notify the renderers - mainWindow.webContents.send('parity-running', true); - global.isParityRunning = true; // Send this variable to renderes via IPC }, { retries: hostsToPing.length } ); diff --git a/packages/parity-electron/src/runParity.js b/packages/parity-electron/src/runParity.js index 8648137a..7e3d1df7 100644 --- a/packages/parity-electron/src/runParity.js +++ b/packages/parity-electron/src/runParity.js @@ -29,14 +29,14 @@ const catchableErrors = [ 'IO error: While lock file:' ]; -export const runParity = async mainWindow => { +export const runParity = async onParityError => { // 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(mainWindow); + const isRunning = await isParityRunning(); if (isRunning) { return; } @@ -68,7 +68,7 @@ export const runParity = async mainWindow => { parity.stderr.on('data', callback); parity.on('error', err => { - throw err; + onParityError(err); }); parity.on('close', (exitCode, signal) => { if (exitCode === 0) { @@ -91,15 +91,9 @@ export const runParity = async mainWindow => { if (Object.keys(parityArgv()).length > 0) { app.exit(1); } else { - throw new Error(`Exit code ${exitCode}, with signal ${signal}.`); + onParityError(new Error(`Exit code ${exitCode}, with signal ${signal}.`)); } }); - - // Notify the renderers - mainWindow.webContents.send('parity-running', true); - global.isParityRunning = true; // Send this variable to renderes via IPC - - return Promise.resolve(true); }; export const killParity = () => { @@ -108,5 +102,5 @@ export const killParity = () => { parity.kill(); parity = null; } - return Promise.resolve(true); + return Promise.resolve(); }; -- GitLab From 4ee1b3087c2ab947066b424b99d205320357111c Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Thu, 28 Jun 2018 18:39:27 +0200 Subject: [PATCH 4/7] Fix small bugs --- packages/fether-electron/package.json | 8 ------- packages/fether-electron/src/main/index.js | 3 ++- .../src/main/utils/handleError.js | 3 ++- .../fether-electron/src/main/utils/pino.js | 15 +------------ .../src/Accounts/AccountsList/AccountsList.js | 3 ++- packages/parity-electron/package.json | 19 ++++------------- packages/parity-electron/src/fetchParity.js | 21 +++++++------------ packages/parity-electron/src/getParityPath.js | 8 +++---- .../parity-electron/src/isParityRunning.js | 7 ++----- packages/parity-electron/src/runParity.js | 13 ++++++------ .../parity-electron/src/signerNewToken.js | 11 ++++------ packages/parity-electron/src/utils/debug.js | 10 +++++++++ .../src/utils/parityChannel.js | 2 +- yarn.lock | 9 +------- 14 files changed, 45 insertions(+), 87 deletions(-) create mode 100644 packages/parity-electron/src/utils/debug.js diff --git a/packages/fether-electron/package.json b/packages/fether-electron/package.json index 3ddcc51b..aad76015 100644 --- a/packages/fether-electron/package.json +++ b/packages/fether-electron/package.json @@ -41,19 +41,11 @@ }, "dependencies": { "@parity/electron": "^0.1.0", - "async-retry": "^1.2.1", - "axios": "^0.18.0", - "checksum": "^0.1.1", - "command-exists": "^1.2.6", "commander": "^2.15.1", - "debug": "^3.1.0", - "electron-dl": "^1.11.0", "fether-react": "^0.1.0", "menubar": "^5.2.3", "pino": "^4.16.1", - "pino-debug": "^1.1.1", "pino-multi-stream": "^3.1.2", - "promise-any": "^0.2.0", "source-map-support": "^0.5.6" }, "devDependencies": { diff --git a/packages/fether-electron/src/main/index.js b/packages/fether-electron/src/main/index.js index 00cb556d..2c048688 100644 --- a/packages/fether-electron/src/main/index.js +++ b/packages/fether-electron/src/main/index.js @@ -18,12 +18,13 @@ import cli from './cli'; import handleError from './utils/handleError'; import messages from './messages'; import { parity } from '../../package.json'; -import pino from './utils/pino'; +import Pino from './utils/pino'; import { productName } from '../../electron-builder.json'; import staticPath from './utils/staticPath'; const { app, BrowserWindow, ipcMain, session } = electron; let mainWindow; +const pino = Pino(); function createWindow () { pino.info(`Starting ${productName}...`); diff --git a/packages/fether-electron/src/main/utils/handleError.js b/packages/fether-electron/src/main/utils/handleError.js index f452bb43..8159dfed 100644 --- a/packages/fether-electron/src/main/utils/handleError.js +++ b/packages/fether-electron/src/main/utils/handleError.js @@ -6,9 +6,10 @@ import { app, dialog, shell } from 'electron'; import { bugs, name, parity } from '../../../package.json'; -import pino from './pino'; +import Pino from './pino'; const logFile = `${app.getPath('userData')}/${name}.log`; +const pino = Pino(); export default (err, message = 'An error occurred.') => { pino.error(err); diff --git a/packages/fether-electron/src/main/utils/pino.js b/packages/fether-electron/src/main/utils/pino.js index fff8af9d..62084491 100644 --- a/packages/fether-electron/src/main/utils/pino.js +++ b/packages/fether-electron/src/main/utils/pino.js @@ -4,11 +4,9 @@ // SPDX-License-Identifier: BSD-3-Clause import { app } from 'electron'; -import debug from 'debug'; import fs from 'fs'; import { multistream } from 'pino-multi-stream'; import Pino from 'pino'; -import pinoDebug from 'pino-debug'; import { name } from '../../../package.json'; @@ -34,15 +32,4 @@ const streams = [ { level: 'info', stream: pretty } ]; -const pino = Pino({ name }, multistream(streams)); - -// @parity/electron's debug logs are in namespace "@parity/log", we enable -// them by default. -debug.enable('@parity/electron'); -pinoDebug(pino, { - map: { - '@parity/electron': 'debug' - } -}); - -export default pino; +export default opts => Pino({ name, ...opts }, multistream(streams)); diff --git a/packages/fether-react/src/Accounts/AccountsList/AccountsList.js b/packages/fether-react/src/Accounts/AccountsList/AccountsList.js index 331618be..3f1cc479 100644 --- a/packages/fether-react/src/Accounts/AccountsList/AccountsList.js +++ b/packages/fether-react/src/Accounts/AccountsList/AccountsList.js @@ -9,6 +9,7 @@ import { accountsInfo$, defaultAccount$ } from '@parity/light.js'; import { inject, observer } from 'mobx-react'; import light from 'light-hoc'; +import debug from '../../utils/debug'; import Health from '../../Health'; @light({ @@ -52,7 +53,7 @@ class AccountsList extends Component { history.push('/tokens', { address }); }) .catch(err => - console.error(`Error while selecting account, ${err.message}.`) + debug('AccountsList')(`Error while selecting account, ${err.message}.`) ); }; diff --git a/packages/parity-electron/package.json b/packages/parity-electron/package.json index b36a483e..32f6371d 100644 --- a/packages/parity-electron/package.json +++ b/packages/parity-electron/package.json @@ -36,25 +36,14 @@ "axios": "^0.18.0", "checksum": "^0.1.1", "command-exists": "^1.2.6", - "commander": "^2.15.1", "debug": "^3.1.0", "electron-dl": "^1.11.0", - "fether-react": "^0.1.0", - "menubar": "^5.2.3", - "pino": "^4.16.1", - "pino-multi-stream": "^3.1.2", - "promise-any": "^0.2.0", - "source-map-support": "^0.5.6" + "promise-any": "^0.2.0" }, "devDependencies": { "@babel/plugin-transform-runtime": "^7.0.0-beta.51", - "babel-eslint": "^8.2.3", - "copyfiles": "^2.0.0", - "cross-env": "^5.2.0", "electron": "^2.0.2", - "electron-builder": "^20.15.1", - "electron-webpack": "^2.1.2", - "semistandard": "^12.0.1", - "webpack": "^4.7.0" - } + "semistandard": "^12.0.1" + }, + "peerDependencies": {} } diff --git a/packages/parity-electron/src/fetchParity.js b/packages/parity-electron/src/fetchParity.js index b4056e85..e4c2a4d7 100644 --- a/packages/parity-electron/src/fetchParity.js +++ b/packages/parity-electron/src/fetchParity.js @@ -6,18 +6,16 @@ import { app } from 'electron'; import axios from 'axios'; import cs from 'checksum'; -import debug from 'debug'; import { download } from 'electron-dl'; import fs from 'fs'; import { promisify } from 'util'; import retry from 'async-retry'; +import debug from './utils/debug'; import { defaultParityPath, getParityPath } from './getParityPath'; -import { name } from '../package.json'; import parityChannel from './utils/parityChannel'; const checksum = promisify(cs.file); -const logger = debug(`${name}:main`); const fsChmod = promisify(fs.chmod); const fsStat = promisify(fs.stat); const fsUnlink = promisify(fs.unlink); @@ -72,12 +70,15 @@ export const fetchParity = (mainWindow, onProgress) => { return retry( async (_, attempt) => { if (attempt > 1) { - logger('Retrying.'); + debug('main')('Retrying.'); } + // Delete any old Parity if it exists + await deleteParity(); + // Fetch the metadata of the correct version of parity - const metadataUrl = `${VANITY_URL}?version=${parityChannel}&os=${getOs()}&architecture=${getArch()}`; - logger(`Downloading from ${metadataUrl}.`); + const metadataUrl = `${VANITY_URL}?version=${parityChannel()}&os=${getOs()}&architecture=${getArch()}`; + debug('main')(`Downloading from ${metadataUrl}.`); const { data } = await axios.get(metadataUrl); // Get the binary's url @@ -112,14 +113,6 @@ export const fetchParity = (mainWindow, onProgress) => { return parityPath; }, { - onRetry: async err => { - debug(err); - - // Everytime we retry, we remove the parity file we just downloaded. - // This needs to be done syncly normally, since onRetry is sync - // https://github.com/zeit/async-retry/issues/43 - return deleteParity(); - }, retries: 3 } ); diff --git a/packages/parity-electron/src/getParityPath.js b/packages/parity-electron/src/getParityPath.js index f4ad5530..7789e445 100644 --- a/packages/parity-electron/src/getParityPath.js +++ b/packages/parity-electron/src/getParityPath.js @@ -5,14 +5,12 @@ import { app } from 'electron'; import commandExists from 'command-exists'; -import debug from 'debug'; import fs from 'fs'; import promiseAny from 'promise-any'; import { promisify } from 'util'; -import { name } from '../package.json'; +import debug from './utils/debug'; -const logger = debug(`${name}:main`); const fsStat = promisify(fs.stat); // The default path to install parity, in case there's no other instance found @@ -98,10 +96,10 @@ export const getParityPath = async () => { try { const path = await doesParityExist(); parityPath = path; // Save the final result in module variable - logger(`Parity found on machine, can be run with "${path}".`); + debug('main')(`Parity found on machine, can be run with "${path}".`); return path; } catch (err) { - logger(`Parity not found on machine.`); + debug('main')(`Parity not found on machine.`); throw err; } }; diff --git a/packages/parity-electron/src/isParityRunning.js b/packages/parity-electron/src/isParityRunning.js index 2592981e..5b8b284a 100644 --- a/packages/parity-electron/src/isParityRunning.js +++ b/packages/parity-electron/src/isParityRunning.js @@ -4,13 +4,10 @@ // SPDX-License-Identifier: BSD-3-Clause import axios from 'axios'; -import debug from 'debug'; import retry from 'async-retry'; import { cli } from './utils/cli'; -import { name } from '../package.json'; - -const logger = debug(`${name}:main`); +import debug from './utils/debug'; // Try to ping these hosts const hostsToPing = ['http://127.0.0.1:8545', 'http://127.0.0.1:8546']; @@ -34,7 +31,7 @@ export const isParityRunning = async () => { async (_, attempt) => { const host = hostsToPing[attempt - 1]; // Attempt starts with 1 await axios.get(host); - logger( + debug('main')( `Another instance of parity is already running on ${host}, skip running local instance.` ); }, diff --git a/packages/parity-electron/src/runParity.js b/packages/parity-electron/src/runParity.js index 7e3d1df7..a234cb01 100644 --- a/packages/parity-electron/src/runParity.js +++ b/packages/parity-electron/src/runParity.js @@ -4,18 +4,15 @@ // SPDX-License-Identifier: BSD-3-Clause import { app } from 'electron'; -import debug from 'debug'; import fs from 'fs'; import { spawn } from 'child_process'; import { promisify } from 'util'; import { cli, parityArgv } from './utils/cli'; +import debug from './utils/debug'; import { getParityPath } from './getParityPath'; import { isParityRunning } from './isParityRunning'; import logCommand from './utils/logCommand'; -import { name } from '../package.json'; - -const logger = debug(`${name}:main`); const fsChmod = promisify(fs.chmod); @@ -55,14 +52,14 @@ export const runParity = async onParityError => { // Run an instance of parity with the correct args const args = [...parityArgv(), '--light']; parity = spawn(parityPath, args); - logger(logCommand(parityPath, args)); + debug('main')(logCommand(parityPath, args)); // Save in memory the last line of the log file, for handling error const callback = data => { if (data && data.length) { logLastLine = data.toString(); } - debug(`${name}:parity`)(data.toString()); + debug('parity')(data.toString()); }; parity.stdout.on('data', callback); parity.stderr.on('data', callback); @@ -83,7 +80,9 @@ export const runParity = async onParityError => { logLastLine && catchableErrors.some(error => logLastLine.includes(error)) ) { - logger('Another instance of parity is running, closing local instance.'); + debug('main')( + 'Another instance of parity is running, closing local instance.' + ); return; } diff --git a/packages/parity-electron/src/signerNewToken.js b/packages/parity-electron/src/signerNewToken.js index f867eaf6..f33c9972 100644 --- a/packages/parity-electron/src/signerNewToken.js +++ b/packages/parity-electron/src/signerNewToken.js @@ -3,27 +3,24 @@ // // SPDX-License-Identifier: BSD-3-Clause -import debug from 'debug'; import { spawn } from 'child_process'; +import debug from './utils/debug'; import { getParityPath } from './getParityPath'; import logCommand from './utils/logCommand'; -import { name } from '../package.json'; - -const logger = debug(`${name}:main`); /** * Launch a parity instance to get a secure token. */ export const signerNewToken = () => new Promise(async (resolve, reject) => { - logger('Requesting new token.'); + debug('main')('Requesting new token.'); const parityPath = await getParityPath(); // Generate a new token const paritySigner = spawn(parityPath, ['signer', 'new-token']); - logger(logCommand(parityPath, ['signer', 'new-token'])); + debug('main')(logCommand(parityPath, ['signer', 'new-token'])); // Listen to the output of the previous command paritySigner.stdout.on('data', data => { @@ -37,7 +34,7 @@ export const signerNewToken = () => if (match) { const token = match[0]; paritySigner.kill(); // We don't need the signer anymore - logger('Successfully extracted token.'); + debug('main')('Successfully extracted token.'); resolve(token); } }); diff --git a/packages/parity-electron/src/utils/debug.js b/packages/parity-electron/src/utils/debug.js new file mode 100644 index 00000000..3f6011b9 --- /dev/null +++ b/packages/parity-electron/src/utils/debug.js @@ -0,0 +1,10 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. +// +// SPDX-License-Identifier: BSD-3-Clause + +import debug from 'debug'; + +import { name } from '../../package.json'; + +export default namespace => debug(`${name}:${namespace}`); diff --git a/packages/parity-electron/src/utils/parityChannel.js b/packages/parity-electron/src/utils/parityChannel.js index c8f50c4d..44ad1827 100644 --- a/packages/parity-electron/src/utils/parityChannel.js +++ b/packages/parity-electron/src/utils/parityChannel.js @@ -14,4 +14,4 @@ export const setParityChannel = channel => { parityChannel = channel; }; -export default parityChannel; +export default () => parityChannel; diff --git a/yarn.lock b/yarn.lock index c4f78b18..41cf72b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8826,13 +8826,6 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" -pino-debug@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/pino-debug/-/pino-debug-1.1.1.tgz#7986a1b3de232ea4cee488a0ebcea7073772a622" - dependencies: - debug "^3.1.0" - pino "^4.0.2" - pino-multi-stream@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/pino-multi-stream/-/pino-multi-stream-3.1.2.tgz#261af1b66caf208abe9c97f1bad1990d5c25d303" @@ -8843,7 +8836,7 @@ pino-std-serializers@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-2.1.0.tgz#01953dcaecd5f43b331ecf2e312a49c9fd64851c" -pino@^4.0.2, pino@^4.16.1, pino@^4.7.1: +pino@^4.16.1, pino@^4.7.1: version "4.17.3" resolved "https://registry.yarnpkg.com/pino/-/pino-4.17.3.tgz#3536ea36f6258356ba8891d18dbf1afed41fcf09" dependencies: -- GitLab From ae0e6f9d81df7c4f41e7ed58c8b097bafe259601 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Fri, 29 Jun 2018 13:56:49 +0200 Subject: [PATCH 5/7] Make debug work across packages --- package.json | 1 + packages/fether-electron/src/main/index.js | 1 + .../src/Accounts/AccountsList/AccountsList.js | 2 +- packages/fether-react/src/stores/parityStore.js | 4 ++-- packages/fether-react/src/stores/sendStore.js | 6 +++--- packages/parity-electron/src/fetchParity.js | 6 +++--- packages/parity-electron/src/getParityPath.js | 8 +++++--- packages/parity-electron/src/index.js | 5 +++++ packages/parity-electron/src/isParityRunning.js | 4 ++-- packages/parity-electron/src/runParity.js | 10 +++++----- packages/parity-electron/src/signerNewToken.js | 10 ++++++---- .../parity-electron/src/utils/{debug.js => logger.js} | 8 ++++++-- 12 files changed, 40 insertions(+), 25 deletions(-) rename packages/parity-electron/src/utils/{debug.js => logger.js} (58%) diff --git a/package.json b/package.json index 6fe3080e..2c83689a 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "start": "npm-run-all -l -p start-*", "start-electron": "cd packages/fether-electron && yarn start", "start-hoc": "cd packages/light-hoc && yarn start", + "start-parity-electron": "cd packages/parity-electron && yarn start", "start-react": "cd packages/fether-react && yarn start", "start-ui": "cd packages/fether-ui && yarn start", "test": "lerna run test --parallel" diff --git a/packages/fether-electron/src/main/index.js b/packages/fether-electron/src/main/index.js index 2c048688..81403fc8 100644 --- a/packages/fether-electron/src/main/index.js +++ b/packages/fether-electron/src/main/index.js @@ -37,6 +37,7 @@ function createWindow () { // Set options for @parity/electron parityElectron({ cli, + logger: namespace => log => Pino({ name: namespace }).info(log), parityChannel: parity.channel }); diff --git a/packages/fether-react/src/Accounts/AccountsList/AccountsList.js b/packages/fether-react/src/Accounts/AccountsList/AccountsList.js index 3f1cc479..f29798e2 100644 --- a/packages/fether-react/src/Accounts/AccountsList/AccountsList.js +++ b/packages/fether-react/src/Accounts/AccountsList/AccountsList.js @@ -53,7 +53,7 @@ class AccountsList extends Component { history.push('/tokens', { address }); }) .catch(err => - debug('AccountsList')(`Error while selecting account, ${err.message}.`) + logger()('AccountsList')(`Error while selecting account, ${err.message}.`) ); }; diff --git a/packages/fether-react/src/stores/parityStore.js b/packages/fether-react/src/stores/parityStore.js index d8f4ac8f..5e477a4f 100644 --- a/packages/fether-react/src/stores/parityStore.js +++ b/packages/fether-react/src/stores/parityStore.js @@ -12,7 +12,7 @@ import store from 'store'; import Debug from '../utils/debug'; import LS_PREFIX from './utils/lsPrefix'; -const debug = Debug('sendStore'); +const debug = logger()('sendStore'); const electron = isElectron() ? window.require('electron') : null; const LS_KEY = `${LS_PREFIX}::secureToken`; @@ -31,7 +31,7 @@ class ParityStore { } if (!electron) { - debug( + logger()( 'Not in Electron, ParityStore will only have limited capabilities.' ); return; diff --git a/packages/fether-react/src/stores/sendStore.js b/packages/fether-react/src/stores/sendStore.js index 76a0d48e..9430b516 100644 --- a/packages/fether-react/src/stores/sendStore.js +++ b/packages/fether-react/src/stores/sendStore.js @@ -15,7 +15,7 @@ import Debug from '../utils/debug'; import parityStore from './parityStore'; import tokensStore from './tokensStore'; -const debug = Debug('sendStore'); +const debug = logger()('sendStore'); const DEFAULT_GAS = new BigNumber(21000); // Default gas amount to show const GAS_MULT_FACTOR = 1.33; // Since estimateGas is not always accurate, we add a 33% factor for buffer. @@ -111,7 +111,7 @@ class SendStore { this.txForErc20.options ); - debug( + logger()( 'Sending tx.', this.tokenAddress === 'ETH' ? this.txForEth : this.txForErc20 ); @@ -125,7 +125,7 @@ class SendStore { .catch(reject); } this.setTxStatus(txStatus); - debug('Tx status updated.', txStatus); + logger()('Tx status updated.', txStatus); }); }); }; diff --git a/packages/parity-electron/src/fetchParity.js b/packages/parity-electron/src/fetchParity.js index e4c2a4d7..84eb2348 100644 --- a/packages/parity-electron/src/fetchParity.js +++ b/packages/parity-electron/src/fetchParity.js @@ -11,8 +11,8 @@ import fs from 'fs'; import { promisify } from 'util'; import retry from 'async-retry'; -import debug from './utils/debug'; import { defaultParityPath, getParityPath } from './getParityPath'; +import logger from './utils/logger'; import parityChannel from './utils/parityChannel'; const checksum = promisify(cs.file); @@ -70,7 +70,7 @@ export const fetchParity = (mainWindow, onProgress) => { return retry( async (_, attempt) => { if (attempt > 1) { - debug('main')('Retrying.'); + logger()('@parity/electron:main')('Retrying.'); } // Delete any old Parity if it exists @@ -78,7 +78,7 @@ export const fetchParity = (mainWindow, onProgress) => { // Fetch the metadata of the correct version of parity const metadataUrl = `${VANITY_URL}?version=${parityChannel()}&os=${getOs()}&architecture=${getArch()}`; - debug('main')(`Downloading from ${metadataUrl}.`); + logger()('@parity/electron:main')(`Downloading from ${metadataUrl}.`); const { data } = await axios.get(metadataUrl); // Get the binary's url diff --git a/packages/parity-electron/src/getParityPath.js b/packages/parity-electron/src/getParityPath.js index 7789e445..82be6a3d 100644 --- a/packages/parity-electron/src/getParityPath.js +++ b/packages/parity-electron/src/getParityPath.js @@ -9,7 +9,7 @@ import fs from 'fs'; import promiseAny from 'promise-any'; import { promisify } from 'util'; -import debug from './utils/debug'; +import logger from './utils/logger'; const fsStat = promisify(fs.stat); @@ -96,10 +96,12 @@ export const getParityPath = async () => { try { const path = await doesParityExist(); parityPath = path; // Save the final result in module variable - debug('main')(`Parity found on machine, can be run with "${path}".`); + logger()('@parity/electron:main')( + `Parity found on machine, can be run with "${path}".` + ); return path; } catch (err) { - debug('main')(`Parity not found on machine.`); + logger()('@parity/electron:main')(`Parity not found on machine.`); throw err; } }; diff --git a/packages/parity-electron/src/index.js b/packages/parity-electron/src/index.js index 00ac3bd6..66398d94 100644 --- a/packages/parity-electron/src/index.js +++ b/packages/parity-electron/src/index.js @@ -4,6 +4,7 @@ // SPDX-License-Identifier: BSD-3-Clause import { setCli } from './utils/cli'; +import { setLogger } from './utils/logger'; import { setParityChannel } from './utils/parityChannel'; export * from './getParityPath'; @@ -18,6 +19,10 @@ export default opts => { setCli(opts.cli); } + if (opts.logger) { + setLogger(opts.logger); + } + if (opts.parityChannel) { setParityChannel(opts.parityChannel); } diff --git a/packages/parity-electron/src/isParityRunning.js b/packages/parity-electron/src/isParityRunning.js index 5b8b284a..9deb4cdb 100644 --- a/packages/parity-electron/src/isParityRunning.js +++ b/packages/parity-electron/src/isParityRunning.js @@ -7,7 +7,7 @@ import axios from 'axios'; import retry from 'async-retry'; import { cli } from './utils/cli'; -import debug from './utils/debug'; +import logger from './utils/logger'; // Try to ping these hosts const hostsToPing = ['http://127.0.0.1:8545', 'http://127.0.0.1:8546']; @@ -31,7 +31,7 @@ export const isParityRunning = async () => { async (_, attempt) => { const host = hostsToPing[attempt - 1]; // Attempt starts with 1 await axios.get(host); - debug('main')( + logger()('@parity/electron:main')( `Another instance of parity is already running on ${host}, skip running local instance.` ); }, diff --git a/packages/parity-electron/src/runParity.js b/packages/parity-electron/src/runParity.js index a234cb01..38c2528c 100644 --- a/packages/parity-electron/src/runParity.js +++ b/packages/parity-electron/src/runParity.js @@ -9,10 +9,10 @@ import { spawn } from 'child_process'; import { promisify } from 'util'; import { cli, parityArgv } from './utils/cli'; -import debug from './utils/debug'; import { getParityPath } from './getParityPath'; import { isParityRunning } from './isParityRunning'; import logCommand from './utils/logCommand'; +import logger from './utils/logger'; const fsChmod = promisify(fs.chmod); @@ -52,14 +52,14 @@ export const runParity = async onParityError => { // Run an instance of parity with the correct args const args = [...parityArgv(), '--light']; parity = spawn(parityPath, args); - debug('main')(logCommand(parityPath, args)); + logger()('@parity/electron:main')(logCommand(parityPath, args)); // Save in memory the last line of the log file, for handling error const callback = data => { if (data && data.length) { logLastLine = data.toString(); } - debug('parity')(data.toString()); + logger()('@parity/parity')(data.toString()); }; parity.stdout.on('data', callback); parity.stderr.on('data', callback); @@ -80,7 +80,7 @@ export const runParity = async onParityError => { logLastLine && catchableErrors.some(error => logLastLine.includes(error)) ) { - debug('main')( + logger()('@parity/electron:main')( 'Another instance of parity is running, closing local instance.' ); return; @@ -97,7 +97,7 @@ export const runParity = async onParityError => { export const killParity = () => { if (parity) { - debug('Stopping parity.'); + logger()('Stopping parity.'); parity.kill(); parity = null; } diff --git a/packages/parity-electron/src/signerNewToken.js b/packages/parity-electron/src/signerNewToken.js index f33c9972..fdbc4a0e 100644 --- a/packages/parity-electron/src/signerNewToken.js +++ b/packages/parity-electron/src/signerNewToken.js @@ -5,22 +5,24 @@ import { spawn } from 'child_process'; -import debug from './utils/debug'; import { getParityPath } from './getParityPath'; import logCommand from './utils/logCommand'; +import logger from './utils/logger'; /** * Launch a parity instance to get a secure token. */ export const signerNewToken = () => new Promise(async (resolve, reject) => { - debug('main')('Requesting new token.'); + logger()('@parity/electron:main')('Requesting new token.'); const parityPath = await getParityPath(); // Generate a new token const paritySigner = spawn(parityPath, ['signer', 'new-token']); - debug('main')(logCommand(parityPath, ['signer', 'new-token'])); + logger()('@parity/electron:main')( + logCommand(parityPath, ['signer', 'new-token']) + ); // Listen to the output of the previous command paritySigner.stdout.on('data', data => { @@ -34,7 +36,7 @@ export const signerNewToken = () => if (match) { const token = match[0]; paritySigner.kill(); // We don't need the signer anymore - debug('main')('Successfully extracted token.'); + logger()('@parity/electron:main')('Successfully extracted token.'); resolve(token); } }); diff --git a/packages/parity-electron/src/utils/debug.js b/packages/parity-electron/src/utils/logger.js similarity index 58% rename from packages/parity-electron/src/utils/debug.js rename to packages/parity-electron/src/utils/logger.js index 3f6011b9..acdb5666 100644 --- a/packages/parity-electron/src/utils/debug.js +++ b/packages/parity-electron/src/utils/logger.js @@ -5,6 +5,10 @@ import debug from 'debug'; -import { name } from '../../package.json'; +let logger = debug; -export default namespace => debug(`${name}:${namespace}`); +export const setLogger = _logger => { + logger = _logger; +}; + +export default () => logger; -- GitLab From c1d2e5ae2ead278589b12a2d7da799adb68c0d93 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Fri, 29 Jun 2018 13:59:08 +0200 Subject: [PATCH 6/7] Fix bug debug in react --- .../fether-react/src/Accounts/AccountsList/AccountsList.js | 2 +- packages/fether-react/src/stores/parityStore.js | 4 ++-- packages/fether-react/src/stores/sendStore.js | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/fether-react/src/Accounts/AccountsList/AccountsList.js b/packages/fether-react/src/Accounts/AccountsList/AccountsList.js index f29798e2..3f1cc479 100644 --- a/packages/fether-react/src/Accounts/AccountsList/AccountsList.js +++ b/packages/fether-react/src/Accounts/AccountsList/AccountsList.js @@ -53,7 +53,7 @@ class AccountsList extends Component { history.push('/tokens', { address }); }) .catch(err => - logger()('AccountsList')(`Error while selecting account, ${err.message}.`) + debug('AccountsList')(`Error while selecting account, ${err.message}.`) ); }; diff --git a/packages/fether-react/src/stores/parityStore.js b/packages/fether-react/src/stores/parityStore.js index 5e477a4f..d8f4ac8f 100644 --- a/packages/fether-react/src/stores/parityStore.js +++ b/packages/fether-react/src/stores/parityStore.js @@ -12,7 +12,7 @@ import store from 'store'; import Debug from '../utils/debug'; import LS_PREFIX from './utils/lsPrefix'; -const debug = logger()('sendStore'); +const debug = Debug('sendStore'); const electron = isElectron() ? window.require('electron') : null; const LS_KEY = `${LS_PREFIX}::secureToken`; @@ -31,7 +31,7 @@ class ParityStore { } if (!electron) { - logger()( + debug( 'Not in Electron, ParityStore will only have limited capabilities.' ); return; diff --git a/packages/fether-react/src/stores/sendStore.js b/packages/fether-react/src/stores/sendStore.js index 9430b516..76a0d48e 100644 --- a/packages/fether-react/src/stores/sendStore.js +++ b/packages/fether-react/src/stores/sendStore.js @@ -15,7 +15,7 @@ import Debug from '../utils/debug'; import parityStore from './parityStore'; import tokensStore from './tokensStore'; -const debug = logger()('sendStore'); +const debug = Debug('sendStore'); const DEFAULT_GAS = new BigNumber(21000); // Default gas amount to show const GAS_MULT_FACTOR = 1.33; // Since estimateGas is not always accurate, we add a 33% factor for buffer. @@ -111,7 +111,7 @@ class SendStore { this.txForErc20.options ); - logger()( + debug( 'Sending tx.', this.tokenAddress === 'ETH' ? this.txForEth : this.txForErc20 ); @@ -125,7 +125,7 @@ class SendStore { .catch(reject); } this.setTxStatus(txStatus); - logger()('Tx status updated.', txStatus); + debug('Tx status updated.', txStatus); }); }); }; -- GitLab From 55d6a16fb006395b6e7b7a6a2441bf60d6d09150 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Fri, 29 Jun 2018 15:07:02 +0200 Subject: [PATCH 7/7] Add readme for parity-electron --- packages/fether-electron/src/main/index.js | 13 ++-- packages/parity-electron/README.md | 75 +++++++++++++++++++ packages/parity-electron/src/fetchParity.js | 10 ++- packages/parity-electron/src/index.js | 5 -- .../src/utils/parityChannel.js | 17 ----- 5 files changed, 89 insertions(+), 31 deletions(-) delete mode 100644 packages/parity-electron/src/utils/parityChannel.js diff --git a/packages/fether-electron/src/main/index.js b/packages/fether-electron/src/main/index.js index 81403fc8..63e6024c 100644 --- a/packages/fether-electron/src/main/index.js +++ b/packages/fether-electron/src/main/index.js @@ -37,18 +37,19 @@ function createWindow () { // Set options for @parity/electron parityElectron({ cli, - logger: namespace => log => Pino({ name: namespace }).info(log), - parityChannel: parity.channel + logger: namespace => log => Pino({ name: namespace }).info(log) }); // Look if Parity is installed getParityPath() .catch(() => // Install parity if not present - fetchParity(mainWindow, progress => - // Notify the renderers on download progress - mainWindow.webContents.send('parity-download-progress', progress) - ) + fetchParity(mainWindow, { + onProgress: progress => + // Notify the renderers on download progress + mainWindow.webContents.send('parity-download-progress', progress), + parityChannel: parity.channel + }) ) .then(() => // Run parity when installed diff --git a/packages/parity-electron/README.md b/packages/parity-electron/README.md index 5ef12eb6..3f6e24e9 100644 --- a/packages/parity-electron/README.md +++ b/packages/parity-electron/README.md @@ -1,3 +1,78 @@ # @parity/electron Control the Parity client from electron. + +## Getting Started + +```bash +yarn add @parity/electron +``` + +## Usage + +```javascript +import parityElectron, { isParityRunning } from '@parity/electron'; + +// Optional: override default options +parityElectron({ + cli: myOwnCliObject, + logger: myCustomLoggerFunction, + parityChannel: 'nightly' +}) + +isParityRunning() + .then(() => ...); +``` + +## API + +#### `parityElectron(options: Object)` + +If you don't want to override the default options, there's no need to call this function. Here `options` can have the following fields: + +| 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` + +Downloads Parity, saves it to Electron's `userData` folder, and returns the path to the downloaded binary once finished. + +| Option | Type | Description | +| ----------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| `mainWindow` | [BrowserWindow](https://github.com/electron/electron/blob/master/docs/api/browser-window.md) | The Electron BrowserWindow in which to download the binary. | +| `options.onProgress` | `Function` | Optional callback that receives a number between 0 and 1 representing the progress of the current download. | +| `options.parityChannel` | `String` | Can be `stable`, `beta` or `nightly`. If downloading Parity is needed, determines which version of Parity to download. Defaults to `beta`. | + +#### `getParityPath(): Promise` + +Returns the path to Parity. It checks (in this order) if Parity is in `$PATH`, in a standard OS location or in Electron's `userData` folder, and returns the first instance of Parity found. The Promise rejects if no Parity instance is found. + +#### `defaultParityPath(): Promise` + +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` + +Resolves to `true` if Parity is currently running, or to `false` if not. + +#### `runParity(onParityError: Function): 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. + +| Option | Type | Description | +| --------------- | ---------- | ------------------------------------------------------------------ | +| `onParityError` | `Function` | Callback with `error` as argument when Parity encounters an error. | + +#### `killParity(): Promise` + +If a Parity process has been spawned with [`runParity`](#runParityonParityError-Function-PromiseltNullgt), then it kills this process. The Promise resolves instantly, there's no guarantee that Parity has been cleanly killed. + +#### `deleteParity(): Promise` + +If Parity has been downloaded to Electron's `userData` folder, then it deletes the Parity binary file from that folder. + +#### `signerNewToken(): Promise` + +Runs `parity signer new-token` and resolves with a new secure token to be used in a dapp. Rejects if no token could be extracted. diff --git a/packages/parity-electron/src/fetchParity.js b/packages/parity-electron/src/fetchParity.js index 84eb2348..fbc3da02 100644 --- a/packages/parity-electron/src/fetchParity.js +++ b/packages/parity-electron/src/fetchParity.js @@ -13,7 +13,6 @@ import retry from 'async-retry'; import { defaultParityPath, getParityPath } from './getParityPath'; import logger from './utils/logger'; -import parityChannel from './utils/parityChannel'; const checksum = promisify(cs.file); const fsChmod = promisify(fs.chmod); @@ -65,7 +64,12 @@ export const deleteParity = async () => { }; // Fetch parity from https://vanity-service.parity.io/parity-binaries -export const fetchParity = (mainWindow, onProgress) => { +export const fetchParity = ( + mainWindow, + { onProgress, parityChannel } = { + parityChannel: 'beta' + } +) => { try { return retry( async (_, attempt) => { @@ -77,7 +81,7 @@ export const fetchParity = (mainWindow, onProgress) => { await deleteParity(); // Fetch the metadata of the correct version of parity - const metadataUrl = `${VANITY_URL}?version=${parityChannel()}&os=${getOs()}&architecture=${getArch()}`; + const metadataUrl = `${VANITY_URL}?version=${parityChannel}&os=${getOs()}&architecture=${getArch()}`; logger()('@parity/electron:main')(`Downloading from ${metadataUrl}.`); const { data } = await axios.get(metadataUrl); diff --git a/packages/parity-electron/src/index.js b/packages/parity-electron/src/index.js index 66398d94..bd384f3e 100644 --- a/packages/parity-electron/src/index.js +++ b/packages/parity-electron/src/index.js @@ -5,7 +5,6 @@ import { setCli } from './utils/cli'; import { setLogger } from './utils/logger'; -import { setParityChannel } from './utils/parityChannel'; export * from './getParityPath'; export * from './fetchParity'; @@ -22,8 +21,4 @@ export default opts => { if (opts.logger) { setLogger(opts.logger); } - - if (opts.parityChannel) { - setParityChannel(opts.parityChannel); - } }; diff --git a/packages/parity-electron/src/utils/parityChannel.js b/packages/parity-electron/src/utils/parityChannel.js deleted file mode 100644 index 44ad1827..00000000 --- a/packages/parity-electron/src/utils/parityChannel.js +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. -// This file is part of Parity. -// -// SPDX-License-Identifier: BSD-3-Clause - -let parityChannel = 'beta'; // Fetch beta by default - -/** - * Set custom parity channel. - * - * @param {*} channel - */ -export const setParityChannel = channel => { - parityChannel = channel; -}; - -export default () => parityChannel; -- GitLab