-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add CLI tool to install/search plugins or launch app (#2375)
- Loading branch information
Showing
15 changed files
with
790 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
# build output | ||
dist | ||
app/renderer | ||
bin/cli.* | ||
|
||
# dependencies | ||
node_modules | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
const pify = require('pify'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const Registry = require('winreg'); | ||
|
||
const {cliScriptPath} = 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) | ||
.catch(err => { | ||
if (err.code === 'ENOENT') { | ||
return false; | ||
} | ||
throw err; | ||
}); | ||
}; | ||
|
||
const createSymlink = () => { | ||
return unlink(target) | ||
.catch(err => { | ||
if (err.code === 'ENOENT') { | ||
return; | ||
} | ||
throw err; | ||
}) | ||
.then(() => symlink(source, target)); | ||
}; | ||
|
||
exports.addSymlink = () => { | ||
return checkInstall().then(isInstalled => { | ||
if (isInstalled) { | ||
return Promise.resolve(); | ||
} | ||
return createSymlink(); | ||
}); | ||
}; | ||
|
||
exports.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'}); | ||
envKey.values((err, items) => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
// C:\Users\<user>\AppData\Local\hyper\app-<version>\resources\bin | ||
const binPath = path.dirname(cliScriptPath); | ||
// C:\Users\<user>\AppData\Local\hyper | ||
const basePath = path.resolve(binPath, '../../..'); | ||
|
||
const pathItem = items.find(item => item.name === 'Path'); | ||
const pathParts = pathItem.value.split(';'); | ||
const existingPath = pathParts.find(pathPart => pathPart === binPath); | ||
if (existingPath) { | ||
resolve(); | ||
return; | ||
} | ||
|
||
// Because version is in path we need to remove old path if present and add current path | ||
const newPathValue = pathParts | ||
.filter(pathPart => !pathPart.startsWith(basePath)) | ||
.concat([binPath]) | ||
.join(';'); | ||
|
||
envKey.set(pathItem.name, Registry.REG_SZ, newPathValue, error => { | ||
if (error) { | ||
reject(error); | ||
return; | ||
} | ||
resolve(); | ||
}); | ||
}); | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#!/bin/bash | ||
|
||
# Link to the CLI bootstrap | ||
ln -sf '/opt/${productFilename}/resources/bin/${executable}' '/usr/local/bin/${executable}' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#!/usr/bin/env bash | ||
# Deeply inspired by https://github.com/Microsoft/vscode/blob/1.17.0/resources/linux/bin/code.sh | ||
|
||
# If root, ensure that --user-data-dir is specified | ||
if [ "$(id -u)" = "0" ]; then | ||
for i in $@ | ||
do | ||
if [[ $i == --user-data-dir=* ]]; then | ||
DATA_DIR_SET=1 | ||
fi | ||
done | ||
if [ -z $DATA_DIR_SET ]; then | ||
echo "It is recommended to start hyper as a normal user. To run as root, you must specify an alternate user data directory with the --user-data-dir argument." 1>&2 | ||
exit 1 | ||
fi | ||
fi | ||
|
||
if [ ! -L $0 ]; then | ||
# if path is not a symlink, find relatively | ||
HYPER_PATH="$(dirname $0)/../.." | ||
else | ||
if which readlink >/dev/null; then | ||
# if readlink exists, follow the symlink and find relatively | ||
HYPER_PATH="$(dirname $(readlink -f $0))/../.." | ||
else | ||
# else use the standard install location | ||
HYPER_PATH="/opt/Hyper" | ||
fi | ||
fi | ||
|
||
ELECTRON="$HYPER_PATH/hyper" | ||
CLI="$HYPER_PATH/resources/bin/cli.js" | ||
ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@" | ||
exit $? |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#!/usr/bin/env bash | ||
# Deeply inspired by https://github.com/Microsoft/vscode/blob/1.17.0/resources/darwin/bin/code.sh | ||
|
||
function realpath() { /usr/bin/python -c "import os,sys; print os.path.realpath(sys.argv[1])" "$0"; } | ||
CONTENTS="$(dirname "$(dirname "$(dirname "$(realpath "$0")")")")" | ||
ELECTRON="$CONTENTS/MacOS/Hyper" | ||
CLI="$CONTENTS/Resources/bin/cli.js" | ||
ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@" | ||
exit $? |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#!/usr/bin/env bash | ||
# Deeply inspired by https://github.com/Microsoft/vscode/blob/1.17.0/resources/win/bin/code.sh | ||
|
||
NAME="Hyper" | ||
HYPER_PATH="$(dirname "$(dirname "$(dirname "$(realpath "$0")")")")" | ||
ELECTRON="$HYPER_PATH/$NAME.exe" | ||
if grep -q Microsoft /proc/version; then | ||
echo "Warning! Due to WSL limitations, you can use CLI commands here. Please use Hyper CLI on cmd, PowerShell or GitBash/CygWin." | ||
echo "Please see: https://github.com/Microsoft/WSL/issues/1494" | ||
echo "" | ||
# If running under WSL don't pass cli.js to Electron as environment vars | ||
# cannot be transferred from WSL to Windows | ||
# See: https://github.com/Microsoft/BashOnWindows/issues/1363 | ||
# https://github.com/Microsoft/BashOnWindows/issues/1494 | ||
"$ELECTRON" "$@" | ||
exit $? | ||
fi | ||
if [ "$(expr substr $(uname -s) 1 9)" == "CYGWIN_NT" ]; then | ||
CLI=$(cygpath -m "$HYPER_PATH/resources/bin/cli.js") | ||
else | ||
CLI="$HYPER_PATH/resources/bin/cli.js" | ||
fi | ||
ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@" | ||
exit $? | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
@echo off | ||
setlocal | ||
set ELECTRON_RUN_AS_NODE=1 | ||
call "%~dp0..\..\Hyper.exe" "%~dp0..\..\resources\bin\cli.js" %* | ||
endlocal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
const fs = require('fs'); | ||
const os = require('os'); | ||
const npmName = require('npm-name'); | ||
const pify = require('pify'); | ||
const recast = require('recast'); | ||
|
||
const fileName = `${os.homedir()}/.hyper.js`; | ||
|
||
let fileContents; | ||
let parsedFile; | ||
let plugins; | ||
let localPlugins; | ||
|
||
try { | ||
fileContents = fs.readFileSync(fileName, 'utf8'); | ||
|
||
parsedFile = recast.parse(fileContents); | ||
|
||
const properties = parsedFile.program.body[0].expression.right.properties; | ||
plugins = properties.find(property => { | ||
return property.key.name === 'plugins'; | ||
}).value.elements; | ||
|
||
localPlugins = properties.find(property => { | ||
return property.key.name === 'localPlugins'; | ||
}).value.elements; | ||
} catch (err) { | ||
if (err.code !== 'ENOENT') { | ||
// ENOENT === !exists() | ||
throw err; | ||
} | ||
} | ||
|
||
function exists() { | ||
return fileContents !== undefined; | ||
} | ||
|
||
function isInstalled(plugin, locally) { | ||
const array = locally ? localPlugins : plugins; | ||
if (array && Array.isArray(array)) { | ||
return array.find(entry => entry.value === plugin) !== undefined; | ||
} | ||
return false; | ||
} | ||
|
||
function save() { | ||
return pify(fs.writeFile)(fileName, recast.print(parsedFile).code, 'utf8'); | ||
} | ||
|
||
function existsOnNpm(plugin) { | ||
plugin = plugin.split('#')[0].split('@')[0]; | ||
return npmName(plugin).then(unavailable => { | ||
if (unavailable) { | ||
const err = new Error(`${plugin} not found on npm`); | ||
err.code = 'NOT_FOUND_ON_NPM'; | ||
throw err; | ||
} | ||
}); | ||
} | ||
|
||
function install(plugin, locally) { | ||
const array = locally ? localPlugins : plugins; | ||
return new Promise((resolve, reject) => { | ||
existsOnNpm(plugin) | ||
.then(() => { | ||
if (isInstalled(plugin, locally)) { | ||
return reject(`${plugin} is already installed`); | ||
} | ||
|
||
array.push(recast.types.builders.literal(plugin)); | ||
save() | ||
.then(resolve) | ||
.catch(err => reject(err)); | ||
}) | ||
.catch(err => { | ||
if (err.code === 'NOT_FOUND_ON_NPM') { | ||
reject(err.message); | ||
} else { | ||
reject(err); | ||
} | ||
}); | ||
}); | ||
} | ||
|
||
function uninstall(plugin) { | ||
return new Promise((resolve, reject) => { | ||
if (!isInstalled(plugin)) { | ||
return reject(`${plugin} is not installed`); | ||
} | ||
|
||
const index = plugins.findIndex(entry => entry.value === plugin); | ||
plugins.splice(index, 1); | ||
save() | ||
.then(resolve) | ||
.catch(err => reject(err)); | ||
}); | ||
} | ||
|
||
function list() { | ||
if (Array.isArray(plugins)) { | ||
return plugins.map(plugin => plugin.value).join('\n'); | ||
} | ||
return false; | ||
} | ||
|
||
module.exports.configPath = fileName; | ||
module.exports.exists = exists; | ||
module.exports.existsOnNpm = existsOnNpm; | ||
module.exports.isInstalled = isInstalled; | ||
module.exports.install = install; | ||
module.exports.uninstall = uninstall; | ||
module.exports.list = list; |
Oops, something went wrong.