Skip to content

Commit

Permalink
Migrate config to json
Browse files Browse the repository at this point in the history
  • Loading branch information
LabhanshAgrawal committed Dec 26, 2022
1 parent de9dd14 commit bafa448
Show file tree
Hide file tree
Showing 9 changed files with 723 additions and 131 deletions.
69 changes: 69 additions & 0 deletions app/config/config-default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"$schema": "./schema.json",
"config": {
"updateChannel": "stable",
"fontSize": 12,
"fontFamily": "Menlo, \"DejaVu Sans Mono\", Consolas, \"Lucida Console\", monospace",
"fontWeight": "normal",
"fontWeightBold": "bold",
"lineHeight": 1,
"letterSpacing": 0,
"scrollback": 1000,
"cursorColor": "rgba(248,28,229,0.8)",
"cursorAccentColor": "#000",
"cursorShape": "BLOCK",
"cursorBlink": false,
"foregroundColor": "#fff",
"backgroundColor": "#000",
"selectionColor": "rgba(248,28,229,0.3)",
"borderColor": "#333",
"css": "",
"termCSS": "",
"workingDirectory": "",
"showHamburgerMenu": "",
"showWindowControls": "",
"padding": "12px 14px",
"colors": {
"black": "#000000",
"red": "#C51E14",
"green": "#1DC121",
"yellow": "#C7C329",
"blue": "#0A2FC4",
"magenta": "#C839C5",
"cyan": "#20C5C6",
"white": "#C7C7C7",
"lightBlack": "#686868",
"lightRed": "#FD6F6B",
"lightGreen": "#67F86F",
"lightYellow": "#FFFA72",
"lightBlue": "#6A76FB",
"lightMagenta": "#FD7CFC",
"lightCyan": "#68FDFE",
"lightWhite": "#FFFFFF",
"limeGreen": "#32CD32",
"lightCoral": "#F08080"
},
"shell": "",
"shellArgs": [
"--login"
],
"env": {},
"bell": "SOUND",
"bellSound": null,
"bellSoundURL": null,
"copyOnSelect": false,
"defaultSSHApp": true,
"quickEdit": false,
"macOptionSelectionMode": "vertical",
"webGLRenderer": false,
"webLinksActivationKey": "",
"disableLigatures": true,
"disableAutoUpdates": false,
"autoUpdatePlugins": true,
"preserveCWD": true,
"screenReaderMode": false
},
"plugins": [],
"localPlugins": [],
"keymaps": {}
}
90 changes: 36 additions & 54 deletions app/config/import.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import {moveSync, copySync, existsSync, writeFileSync, readFileSync, lstatSync} from 'fs-extra';
import {copySync, existsSync, writeFileSync, readFileSync, copy} from 'fs-extra';
import {sync as mkdirpSync} from 'mkdirp';
import {defaultCfg, cfgPath, legacyCfgPath, plugs, defaultPlatformKeyPath} from './paths';
import {
defaultCfg,
cfgPath,
legacyCfgPath,
plugs,
defaultPlatformKeyPath,
schemaPath,
cfgDir,
schemaFile
} from './paths';
import {_init, _extractDefault} from './init';
import notify from '../notify';
import {rawConfig} from '../../lib/config';
import _ from 'lodash';
import {resolve} from 'path';

let defaultConfig: rawConfig;

Expand All @@ -18,62 +29,33 @@ const _write = (path: string, data: string) => {
writeFileSync(path, format, 'utf8');
};

// Saves a file as backup by appending '.backup' or '.backup2', '.backup3', etc.
// so as to not override any existing files
const saveAsBackup = (src: string) => {
let attempt = 1;
while (attempt < 100) {
const backupPath = `${src}.backup${attempt === 1 ? '' : attempt}`;
if (!existsSync(backupPath)) {
moveSync(src, backupPath);
return backupPath;
// Migrate Hyper3 config to Hyper4 but only if the user hasn't manually
// touched the new config and if the old config is not a symlink
const migrateHyper3Config = () => {
copy(schemaPath, resolve(cfgDir, schemaFile), (err) => {
if (err) {
console.error(err);
}
attempt++;
}
throw new Error('Failed to create backup for config file. Too many backups');
};
});

// Migrate Hyper2 config to Hyper3 but only if the user hasn't manually
// touched the new config and if the old config is not a symlink
const migrateHyper2Config = () => {
if (cfgPath === legacyCfgPath) {
// No need to migrate
if (existsSync(cfgPath)) {
return;
}

if (!existsSync(legacyCfgPath)) {
// Already migrated or user never used Hyper 2
copySync(defaultCfg, cfgPath);
return;
}
const existsNew = existsSync(cfgPath);
if (lstatSync(legacyCfgPath).isSymbolicLink() || (existsNew && lstatSync(cfgPath).isSymbolicLink())) {
// One of the files is a symlink, there could be a number of complications
// in this case so let's avoid those and not do automatic migration
return;
}

if (existsNew) {
const cfg1 = readFileSync(defaultCfg, 'utf8').replace(/\r|\n/g, '');
const cfg2 = readFileSync(cfgPath, 'utf8').replace(/\r|\n/g, '');
const hasNewConfigBeenTouched = cfg1 !== cfg2;
if (hasNewConfigBeenTouched) {
// Assume the user has migrated manually but rename old config to .backup so
// we don't keep trying to migrate on every launch
const backupPath = saveAsBackup(legacyCfgPath);
notify(
'Hyper 3',
`Settings location has changed to ${cfgPath}.\nWe've backed up your old Hyper config to ${backupPath}`
);
return;
}
}

// Migrate
copySync(legacyCfgPath, cfgPath);
saveAsBackup(legacyCfgPath);
const defaultCfgData = JSON.parse(readFileSync(defaultCfg, 'utf8'));
const legacyCfgData = _extractDefault(readFileSync(legacyCfgPath, 'utf8'));
const newCfgData = _.merge(defaultCfgData, legacyCfgData);
_write(cfgPath, JSON.stringify(newCfgData, null, 2));

notify(
'Hyper 3',
`Settings location has changed to ${cfgPath}.\nWe've automatically migrated your existing config!\nPlease restart Hyper now`
'Hyper 4',
`Settings location and format has changed.\nWe've automatically migrated your existing config to ${cfgPath}`
);
};

Expand All @@ -83,18 +65,18 @@ const _importConf = () => {
mkdirpSync(plugs.local);

try {
migrateHyper2Config();
migrateHyper3Config();
} catch (err) {
console.error(err);
}

let defaultCfgRaw = '';
let defaultCfgRaw = '{}';
try {
defaultCfgRaw = readFileSync(defaultCfg, 'utf8');
} catch (err) {
console.log(err);
}
const _defaultCfg = _extractDefault(defaultCfgRaw) as rawConfig;
const _defaultCfg = JSON.parse(defaultCfgRaw) as rawConfig;

// Importing platform specific keymap
let content = '{}';
Expand All @@ -107,12 +89,12 @@ const _importConf = () => {
_defaultCfg.keymaps = mapping;

// Import user config
let userCfg: string;
let userCfg: rawConfig;
try {
userCfg = readFileSync(cfgPath, 'utf8');
userCfg = JSON.parse(readFileSync(cfgPath, 'utf8'));
} catch (err) {
_write(cfgPath, defaultCfgRaw);
userCfg = defaultCfgRaw;
userCfg = JSON.parse(defaultCfgRaw);
}

return {userCfg, defaultCfg: _defaultCfg};
Expand All @@ -121,7 +103,7 @@ const _importConf = () => {
export const _import = () => {
const imported = _importConf();
defaultConfig = imported.defaultCfg;
const result = _init(imported);
const result = _init(imported.userCfg, imported.defaultCfg);
return result;
};

Expand Down
17 changes: 8 additions & 9 deletions app/config/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import vm from 'vm';
import notify from '../notify';
import mapKeys from '../utils/map-keys';
import {parsedConfig, rawConfig, configOptions} from '../../lib/config';
import _ from 'lodash';

const _extract = (script?: vm.Script): Record<string, any> => {
const module: Record<string, any> = {};
Expand All @@ -27,23 +28,21 @@ const _extractDefault = (cfg: string) => {
};

// init config
const _init = (cfg: {userCfg: string; defaultCfg: rawConfig}): parsedConfig => {
const script = _syntaxValidation(cfg.userCfg);
const _cfg = script && (_extract(script) as rawConfig);
const _init = (userCfg: rawConfig, defaultCfg: rawConfig): parsedConfig => {
return {
config: (() => {
if (_cfg?.config) {
return _cfg.config;
if (userCfg?.config) {
return _.merge(defaultCfg.config, userCfg.config);
} else {
notify('Error reading configuration: `config` key is missing');
return cfg.defaultCfg.config || ({} as configOptions);
return defaultCfg.config || ({} as configOptions);
}
})(),
// Merging platform specific keymaps with user defined keymaps
keymaps: mapKeys({...cfg.defaultCfg.keymaps, ..._cfg?.keymaps}),
keymaps: mapKeys({...defaultCfg.keymaps, ...userCfg?.keymaps}),
// Ignore undefined values in plugin and localPlugins array Issue #1862
plugins: (_cfg?.plugins && _cfg.plugins.filter(Boolean)) || [],
localPlugins: (_cfg?.localPlugins && _cfg.localPlugins.filter(Boolean)) || []
plugins: (userCfg?.plugins && userCfg.plugins.filter(Boolean)) || [],
localPlugins: (userCfg?.localPlugins && userCfg.localPlugins.filter(Boolean)) || []
};
};

Expand Down
30 changes: 19 additions & 11 deletions app/config/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,30 @@ import {statSync} from 'fs';
import {resolve, join} from 'path';
import isDev from 'electron-is-dev';

const cfgFile = '.hyper.js';
const defaultCfgFile = 'config-default.js';
const cfgFile = 'hyper.json';
const defaultCfgFile = 'config-default.json';
const schemaFile = 'schema.json';
const homeDirectory = homedir();

// If the user defines XDG_CONFIG_HOME they definitely want their config there,
// otherwise use the home directory in linux/mac and userdata in windows
const applicationDirectory =
let cfgDir = process.env.XDG_CONFIG_HOME
? join(process.env.XDG_CONFIG_HOME, 'Hyper')
: process.platform === 'win32'
? app.getPath('userData')
: join(homeDirectory, '.config', 'Hyper');

const legacyCfgPath = join(
process.env.XDG_CONFIG_HOME !== undefined
? join(process.env.XDG_CONFIG_HOME, 'hyper')
: process.platform == 'win32'
? app.getPath('userData')
: homedir();
: homedir(),
'.hyper.js'
);

let cfgDir = applicationDirectory;
let cfgPath = join(applicationDirectory, cfgFile);
const legacyCfgPath = join(homeDirectory, cfgFile); // Hyper 2 config location
let cfgPath = join(cfgDir, cfgFile);
const schemaPath = resolve(__dirname, schemaFile);

const devDir = resolve(__dirname, '../..');
const devCfg = join(devDir, cfgFile);
Expand All @@ -38,10 +46,8 @@ if (isDev) {
}
}

const plugins = resolve(cfgDir, '.hyper_plugins');
const plugins = resolve(cfgDir, 'plugins');
const plugs = {
legacyBase: resolve(homeDirectory, '.hyper_plugins'),
legacyLocal: resolve(homeDirectory, '.hyper_plugins', 'local'),
base: plugins,
local: resolve(plugins, 'local'),
cache: resolve(plugins, 'cache')
Expand Down Expand Up @@ -82,5 +88,7 @@ export {
yarn,
cliScriptPath,
cliLinkPath,
homeDirectory
homeDirectory,
schemaFile,
schemaPath
};
Loading

0 comments on commit bafa448

Please sign in to comment.