mirror of
https://github.com/quine-global/hyper.git
synced 2026-01-12 20:18:41 -09:00
146 lines
5.1 KiB
TypeScript
146 lines
5.1 KiB
TypeScript
import pify from 'pify';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import notify from '../notify';
|
|
import {cliScriptPath, cliLinkPath} from '../config/paths';
|
|
import {Registry, loadRegistry} from './registry';
|
|
import type {ValueType} from 'native-reg';
|
|
import sudoPrompt from 'sudo-prompt';
|
|
import {clipboard, dialog} from 'electron';
|
|
|
|
const readlink = pify(fs.readlink);
|
|
const symlink = pify(fs.symlink);
|
|
const sudoExec = pify(sudoPrompt.exec, {multiArgs: true});
|
|
|
|
const checkInstall = () => {
|
|
return readlink(cliLinkPath)
|
|
.then((link) => link === cliScriptPath)
|
|
.catch((err) => {
|
|
if (err.code === 'ENOENT') {
|
|
return false;
|
|
}
|
|
throw err;
|
|
});
|
|
};
|
|
|
|
const addSymlink = async (silent: boolean) => {
|
|
try {
|
|
const isInstalled = await checkInstall();
|
|
if (isInstalled) {
|
|
console.log('Hyper CLI already in PATH');
|
|
return;
|
|
}
|
|
console.log('Linking HyperCLI');
|
|
await symlink(cliScriptPath, cliLinkPath);
|
|
} catch (err) {
|
|
// 'EINVAL' is returned by readlink,
|
|
// 'EEXIST' is returned by symlink
|
|
let error =
|
|
err.code === 'EEXIST' || err.code === 'EINVAL'
|
|
? `File already exists: ${cliLinkPath}`
|
|
: `Symlink creation failed: ${err.code}`;
|
|
// Need sudo access to create symlink
|
|
if (err.code === 'EACCES' && !silent) {
|
|
const result = await dialog.showMessageBox({
|
|
message: `You need to grant elevated privileges to add Hyper CLI to PATH
|
|
Or you can run
|
|
sudo ln -sf "${cliScriptPath}" "${cliLinkPath}"`,
|
|
type: 'info',
|
|
buttons: ['OK', 'Copy Command', 'Cancel']
|
|
});
|
|
if (result.response === 0) {
|
|
try {
|
|
await sudoExec(`ln -sf "${cliScriptPath}" "${cliLinkPath}"`, {name: 'Hyper'});
|
|
return;
|
|
} catch (_error) {
|
|
error = _error[0];
|
|
}
|
|
} else if (result.response === 1) {
|
|
clipboard.writeText(`sudo ln -sf "${cliScriptPath}" "${cliLinkPath}"`);
|
|
}
|
|
}
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const addBinToUserPath = () => {
|
|
return new Promise<void>((resolve, reject) => {
|
|
if (!loadRegistry()) {
|
|
reject('Failed to load Registry Module');
|
|
return;
|
|
}
|
|
try {
|
|
const envKey = Registry.openKey(Registry.HKCU, 'Environment', Registry.Access.ALL_ACCESS)!;
|
|
|
|
// C:\Users\<user>\AppData\Local\Programs\hyper\resources\bin
|
|
const binPath = path.dirname(cliScriptPath);
|
|
// C:\Users\<user>\AppData\Local\hyper
|
|
const oldPath = path.resolve(process.env.LOCALAPPDATA!, 'hyper');
|
|
|
|
const items = Registry.enumValueNames(envKey);
|
|
const pathItem = items.find((item) => item.toUpperCase() === 'PATH');
|
|
const pathItemName = pathItem || 'PATH';
|
|
|
|
let newPathValue = binPath;
|
|
let type: ValueType = Registry.ValueType.SZ;
|
|
if (pathItem) {
|
|
type = Registry.queryValueRaw(envKey, pathItem)!.type;
|
|
if (type !== Registry.ValueType.SZ && type !== Registry.ValueType.EXPAND_SZ) {
|
|
reject(`Registry key type is ${type}`);
|
|
return;
|
|
}
|
|
const value = Registry.queryValue(envKey, pathItem) as string;
|
|
let pathParts = value.split(';');
|
|
const existingPath = pathParts.includes(binPath);
|
|
const existingOldPath = pathParts.some((pathPart) => pathPart.startsWith(oldPath));
|
|
if (existingPath && !existingOldPath) {
|
|
console.log('Hyper CLI already in PATH');
|
|
Registry.closeKey(envKey);
|
|
resolve();
|
|
return;
|
|
}
|
|
|
|
// Because nsis install path is different from squirrel we need to remove old path if present
|
|
// and add current path if absent
|
|
if (existingOldPath) pathParts = pathParts.filter((pathPart) => !pathPart.startsWith(oldPath));
|
|
if (!pathParts.includes(binPath)) pathParts.push(binPath);
|
|
newPathValue = pathParts.join(';');
|
|
}
|
|
console.log('Adding HyperCLI path (registry)');
|
|
Registry.setValueRaw(envKey, pathItemName, type, Registry.formatString(newPathValue));
|
|
Registry.closeKey(envKey);
|
|
resolve();
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
};
|
|
|
|
const logNotify = (withNotification: boolean, title: string, body: string, details?: {error?: any}) => {
|
|
console.log(title, body, details);
|
|
withNotification && notify(title, body, details);
|
|
};
|
|
|
|
export const installCLI = async (withNotification: boolean) => {
|
|
if (process.platform === 'win32') {
|
|
try {
|
|
await addBinToUserPath();
|
|
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' || process.platform === 'linux') {
|
|
try {
|
|
await addSymlink(!withNotification);
|
|
logNotify(withNotification, 'Hyper CLI installed', `Symlink created at ${cliLinkPath}`);
|
|
} catch (error) {
|
|
logNotify(withNotification, 'Hyper CLI installation failed', `${error}`);
|
|
}
|
|
} else {
|
|
logNotify(withNotification, 'Hyper CLI installation failed', `Unsupported platform ${process.platform}`);
|
|
}
|
|
};
|