Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Hyper-cli] embed hpm #2375

Merged
merged 39 commits into from
Jan 9, 2018
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f41d7e8
Symlink Hyper CLI on macOS
chabou Oct 9, 2017
4916285
Add cli building
chabou Oct 10, 2017
3a38884
Replace util.promisify by pify
chabou Oct 10, 2017
a441d62
Remove util.promisify from app dep
chabou Oct 10, 2017
618a8cb
Use implicit return in promise chains
chabou Oct 10, 2017
1f670c8
Explicit returns in promise chaining
chabou Oct 10, 2017
5ac8dfd
Fix path for linux
chabou Oct 16, 2017
02df9d4
Working cli on macOS
chabou Oct 10, 2017
ba5687c
Merge branch 'add_cli_cmd' into embed_hpm
chabou Oct 18, 2017
6147109
Delay config reloading to ensure file consistency
chabou Oct 18, 2017
eea9631
Disable lsr/search command and fix cli name in help
chabou Oct 18, 2017
1545d8e
Add Windows compatibility
chabou Oct 18, 2017
1c2b6b0
Merge branch 'add_cli_cmd' into embed_hpm
chabou Oct 18, 2017
3913d3a
Fix WSL laucnh script
chabou Oct 19, 2017
f9a3346
Merge branch 'add_cli_cmd' into embed_hpm
chabou Oct 19, 2017
be7e3d3
Merge branch 'canary' into add_cli_cmd
chabou Nov 2, 2017
9baabe1
Add postinstall scripts for Linux
chabou Nov 2, 2017
c06202c
Merge branch 'add_cli_cmd' into embed_hpm
chabou Nov 2, 2017
4309196
Prevent launching multiple Hyper process on macOS
chabou Nov 2, 2017
87ccaa0
Fix Hyper path from cli.js on GitBash
chabou Nov 3, 2017
180f169
Add warning for WSL
chabou Nov 3, 2017
e2943b9
Convert relative path arguments to absolute paths
chabou Nov 3, 2017
28eb7fe
Update yarn.lock
chabou Nov 3, 2017
321b509
Merge branch 'canary' into embed_hpm
chabou Nov 3, 2017
3225d4e
Update yarn.lock
chabou Nov 3, 2017
82db3a7
Wake up AppVeyor
chabou Nov 3, 2017
e9bbb3e
Remove logs
chabou Nov 3, 2017
2d5fd3e
Fix app path in Linux bootstrap
chabou Nov 3, 2017
249812f
Merge branch 'canary' into embed_hpm
chabou Nov 3, 2017
cf99fb9
Merge branch 'canary' into embed_hpm
chabou Nov 3, 2017
6d00ce8
Check if config exists only when needed
chabou Nov 4, 2017
c2212d8
Fix verbose mode with path
chabou Nov 4, 2017
6d15318
Check if directory/file argument exists
chabou Nov 4, 2017
4822898
Use commandPromise and wait for command termination
chabou Nov 5, 2017
fc45cb8
Enable package checking on npm
chabou Jan 9, 2018
0fb085b
Fix comment
chabou Jan 9, 2018
3e5bc0d
Add plugin list and search
chabou Jan 9, 2018
2699d6e
Enforce hyper- prefix
chabou Jan 9, 2018
77892cf
Merge branch 'canary' into embed_hpm
chabou Jan 9, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# build output
dist
app/renderer
bin/cli.*

# dependencies
node_modules
Expand Down
11 changes: 7 additions & 4 deletions app/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ const _watch = function() {
}

const onChange = () => {
cfg = _import();
notify('Configuration updated', 'Hyper configuration reloaded!');
watchers.forEach(fn => fn());
checkDeprecatedConfig();
// Need to wait 100ms to ensure that write is complete
setTimeout(() => {
cfg = _import();
notify('Configuration updated', 'Hyper configuration reloaded!');
watchers.forEach(fn => fn());
checkDeprecatedConfig();
}, 100);
};

if (process.platform === 'win32') {
Expand Down
4 changes: 3 additions & 1 deletion app/config/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const plugs = {
cache: resolve(plugins, 'cache')
};
const yarn = resolve(__dirname, '../../bin/yarn-standalone.js');
const cliScriptPath = resolve(__dirname, '../../bin/hyper');

const icon = resolve(__dirname, '../static/icon96x96.png');

Expand Down Expand Up @@ -64,5 +65,6 @@ module.exports = {
icon,
defaultPlatformKeyPath,
plugs,
yarn
yarn,
cliScriptPath
};
8 changes: 8 additions & 0 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const config = require('./config');
config.setup();

const plugins = require('./plugins');
const {addSymlink, addBinToUserPath} = require('./utils/cli-install');

const AppMenu = require('./menus/menu');

Expand Down Expand Up @@ -98,6 +99,13 @@ 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');
Expand Down
86 changes: 86 additions & 0 deletions app/utils/cli-install.js
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-2.0.4\resources\bin
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// C:\Users\<user>\AppData\Local\hyper\app-<version>\resources\bin

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

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();
});
});
});
};
4 changes: 4 additions & 0 deletions build/linux/after-install.tpl
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}'
34 changes: 34 additions & 0 deletions build/linux/hyper
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 $?
9 changes: 9 additions & 0 deletions build/mac/hyper
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 $?
25 changes: 25 additions & 0 deletions build/win/hyper
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."

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this mean? Do you mean "you cannot use Hyper CLI here"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah just missing the 't

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 $?

5 changes: 5 additions & 0 deletions build/win/hyper.cmd
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
112 changes: 112 additions & 0 deletions cli/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
const fs = require('fs');
const os = require('os');
const npmName = () => Promise.resolve(); //require('npm-name');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hoped that this PR would have been merged sooner: dominictarr/rc#94

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

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;
Loading