From 3e1784cdbc7a5557f8e5c6bc21987b746a6a2298 Mon Sep 17 00:00:00 2001 From: CHaBou Date: Sun, 22 Apr 2018 22:13:23 +0200 Subject: [PATCH] Prevent /usr/local/bin/hyper overwriting (#2885) Silently fail installation at startup. Add a menu item to manually install it with a notification feedback. Fix plugin update menu item. Fixes #2884 --- app/commands.js | 4 ++ app/config/paths.js | 4 +- app/index.js | 35 +++++++--------- app/menus/menus/plugins.js | 13 +++++- app/utils/cli-install.js | 81 ++++++++++++++++++++++++++------------ 5 files changed, 88 insertions(+), 49 deletions(-) diff --git a/app/commands.js b/app/commands.js index ad848260..498573cb 100644 --- a/app/commands.js +++ b/app/commands.js @@ -1,6 +1,7 @@ const {app} = require('electron'); const {openConfig} = require('./config'); const {updatePlugins} = require('./plugins'); +const {installCLI} = require('./utils/cli-install'); const commands = { 'window:new': () => { @@ -99,6 +100,9 @@ const commands = { }, 'editor:break': focusedWindow => { focusedWindow && focusedWindow.rpc.emit('session break req'); + }, + 'cli:install': () => { + installCLI(true); } }; diff --git a/app/config/paths.js b/app/config/paths.js index d302791a..5afb3d64 100644 --- a/app/config/paths.js +++ b/app/config/paths.js @@ -36,6 +36,7 @@ const plugs = { }; const yarn = resolve(__dirname, '../../bin/yarn-standalone.js'); const cliScriptPath = resolve(__dirname, '../../bin/hyper'); +const cliLinkPath = '/usr/local/bin/hyper'; const icon = resolve(__dirname, '../static/icon96x96.png'); @@ -66,5 +67,6 @@ module.exports = { defaultPlatformKeyPath, plugs, yarn, - cliScriptPath + cliScriptPath, + cliLinkPath }; diff --git a/app/index.js b/app/index.js index 47785025..a0eb6fe5 100644 --- a/app/index.js +++ b/app/index.js @@ -62,7 +62,7 @@ const config = require('./config'); config.setup(); const plugins = require('./plugins'); -const {addSymlink, addBinToUserPath} = require('./utils/cli-install'); +const {installCLI} = require('./utils/cli-install'); const AppMenu = require('./menus/menu'); const Window = require('./ui/window'); const windowUtils = require('./utils/window-utils'); @@ -98,13 +98,6 @@ if (isDev) { } else { //eslint-disable-next-line no-console console.log('running in prod mode'); - if (process.platform === 'win32') { - //eslint-disable-next-line no-console - addBinToUserPath().catch(err => console.error('Failed to add Hyper CLI path to user PATH', err)); - } else { - //eslint-disable-next-line no-console - addSymlink().catch(err => console.error('Failed to symlink Hyper CLI', err)); - } } const url = 'file://' + resolve(isDev ? __dirname : app.getAppPath(), 'index.html'); @@ -183,19 +176,6 @@ app.on('ready', () => // expose to plugins app.createWindow = createWindow; - if (!isDev) { - // check if should be set/removed as default ssh protocol client - if (config.getConfig().defaultSSHApp && !app.isDefaultProtocolClient('ssh')) { - //eslint-disable-next-line no-console - console.log('Setting Hyper as default client for ssh:// protocol'); - app.setAsDefaultProtocolClient('ssh'); - } else if (!config.getConfig().defaultSSHApp && app.isDefaultProtocolClient('ssh')) { - //eslint-disable-next-line no-console - console.log('Removing Hyper from default client for ssh:// protocl'); - app.removeAsDefaultProtocolClient('ssh'); - } - } - // mac only. when the dock icon is clicked // and we don't have any active windows open, // we open one @@ -228,6 +208,19 @@ app.on('ready', () => makeMenu(); plugins.subscribe(plugins.onApp.bind(undefined, app)); config.subscribe(makeMenu); + if (!isDev) { + // check if should be set/removed as default ssh protocol client + if (config.getConfig().defaultSSHApp && !app.isDefaultProtocolClient('ssh')) { + //eslint-disable-next-line no-console + console.log('Setting Hyper as default client for ssh:// protocol'); + app.setAsDefaultProtocolClient('ssh'); + } else if (!config.getConfig().defaultSSHApp && app.isDefaultProtocolClient('ssh')) { + //eslint-disable-next-line no-console + console.log('Removing Hyper from default client for ssh:// protocl'); + app.removeAsDefaultProtocolClient('ssh'); + } + installCLI(false); + } }) .catch(err => { //eslint-disable-next-line no-console diff --git a/app/menus/menus/plugins.js b/app/menus/menus/plugins.js index 4c08f93f..e252b2d2 100644 --- a/app/menus/menus/plugins.js +++ b/app/menus/menus/plugins.js @@ -1,4 +1,4 @@ -module.exports = (commands, update) => { +module.exports = (commands, execCommand) => { return { label: 'Plugins', submenu: [ @@ -6,8 +6,17 @@ module.exports = (commands, update) => { label: 'Update', accelerator: commands['plugins:update'], click() { - update(); + execCommand('plugins:update'); } + }, + { + label: 'Install Hyper CLI command in PATH', + click() { + execCommand('cli:install'); + } + }, + { + type: 'separator' } ] }; diff --git a/app/utils/cli-install.js b/app/utils/cli-install.js index 438b5588..b6094ba1 100644 --- a/app/utils/cli-install.js +++ b/app/utils/cli-install.js @@ -3,21 +3,16 @@ const fs = require('fs'); const path = require('path'); const Registry = require('winreg'); -const {cliScriptPath} = require('../config/paths'); +const notify = require('../notify'); + +const {cliScriptPath, cliLinkPath} = require('../config/paths'); -const lstat = pify(fs.lstat); const readlink = pify(fs.readlink); -const unlink = pify(fs.unlink); const symlink = pify(fs.symlink); -const target = '/usr/local/bin/hyper'; -const source = cliScriptPath; - const checkInstall = () => { - return lstat(target) - .then(stat => stat.isSymbolicLink()) - .then(() => readlink(target)) - .then(link => link === source) + return readlink(cliLinkPath) + .then(link => link === cliScriptPath) .catch(err => { if (err.code === 'ENOENT') { return false; @@ -26,27 +21,20 @@ const checkInstall = () => { }); }; -const createSymlink = () => { - return unlink(target) - .catch(err => { - if (err.code === 'ENOENT') { - return; - } - throw err; - }) - .then(() => symlink(source, target)); -}; - -exports.addSymlink = () => { +const addSymlink = () => { return checkInstall().then(isInstalled => { if (isInstalled) { + //eslint-disable-next-line no-console + console.log('Hyper CLI already in PATH'); return Promise.resolve(); } - return createSymlink(); + //eslint-disable-next-line no-console + console.log('Linking HyperCLI'); + return symlink(cliScriptPath, cliLinkPath); }); }; -exports.addBinToUserPath = () => { +const addBinToUserPath = () => { // Can't use pify because of param order of Registry.values callback return new Promise((resolve, reject) => { const envKey = new Registry({hive: 'HKCU', key: '\\Environment'}); @@ -68,6 +56,8 @@ exports.addBinToUserPath = () => { const pathParts = pathItem.value.split(';'); const existingPath = pathParts.find(pathPart => pathPart === binPath); if (existingPath) { + //eslint-disable-next-line no-console + console.log('Hyper CLI already in PATH'); resolve(); return; } @@ -78,7 +68,8 @@ exports.addBinToUserPath = () => { .concat([binPath]) .join(';'); } - + //eslint-disable-next-line no-console + console.log('Adding HyperCLI path (registry)'); envKey.set(pathItemName, Registry.REG_SZ, newPathValue, error => { if (error) { reject(error); @@ -89,3 +80,43 @@ exports.addBinToUserPath = () => { }); }); }; + +const logNotify = (withNotification, ...args) => { + //eslint-disable-next-line no-console + console.log(...args); + withNotification && notify(...args); +}; + +exports.installCLI = withNotification => { + if (process.platform === 'win32') { + addBinToUserPath() + .then(() => + logNotify( + withNotification, + 'Hyper CLI installed', + 'You may need to restart your computer to complete this installation process.' + ) + ) + .catch(err => + logNotify(withNotification, 'Hyper CLI installation failed', `Failed to add Hyper CLI path to user PATH ${err}`) + ); + } else if (process.platform === 'darwin') { + addSymlink() + .then(() => logNotify(withNotification, 'Hyper CLI installed', `Symlink created at ${cliLinkPath}`)) + .catch(err => { + // 'EINVAL' is returned by readlink, + // 'EEXIST' is returned by symlink + const error = + err.code === 'EEXIST' || err.code === 'EINVAL' + ? `File already exists: ${cliLinkPath}` + : `Symlink creation failed: ${err.code}`; + + //eslint-disable-next-line no-console + console.error(err); + logNotify(withNotification, 'Hyper CLI installation failed', error); + }); + } else { + withNotification && + notify('Hyper CLI instalation', 'Command is added in PATH only at package installation. Please reinstall.'); + } +};