@onboardbase/cli
Version:
[](https://www.npmjs.com/package/@onboardbase/cli) [](https://www.npmjs.com/package/@onboardbase/cli) [ • 12.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConfigManager = exports.PROJECT_CONFIG_FILENAME = exports.REFPATH_VALUE = void 0;
const fs_1 = require("fs");
const os_1 = require("os");
const path_1 = require("path");
const YAML = require("yaml");
const safelyGetFileContent_1 = require("../common/utils/safelyGetFileContent");
const errors_1 = require("../common/errors");
const chalk = require("chalk");
const fs = require("fs");
const util = require("util");
const defaults_1 = require("../common/defaults");
exports.REFPATH_VALUE = `__REFPATH__`;
const OLD_GLOBAL_CONFIG_YAML = "/.onboardbase/.onboardbase.yaml";
exports.PROJECT_CONFIG_FILENAME = ".onboardbase.yaml";
const GLOBAL_CONFIG_FILENAME = "config.json";
const DEFAULT_GLOBAL_CONFIGURATION = { "version-check": {}, scoped: {} };
const HOME_DIR = (0, os_1.homedir)();
class ConfigManager {
constructor() {
this.OLD_GLOBAL_CONFIG_DIR = `${HOME_DIR}/.onboardbase/`;
this._CURRENT_DIR_SCOPE = process.cwd();
}
destroy() {
(0, fs_1.unlinkSync)(this._getGlobalConfigPath());
}
getConfigDir() {
return this.GLOBAL_CONFIG_DIR;
}
getGlobalConfig() {
return this.globalConfig;
}
getCacheDir() {
return this.GLOBAL_CACHE_DIR;
}
getOldConfigDir() {
return this.OLD_GLOBAL_CONFIG_DIR;
}
_getCurrentProjectConfigFilePath() {
return (0, path_1.join)(process.cwd(), exports.PROJECT_CONFIG_FILENAME);
}
_getCurrentProjectConfigFileContent() {
const filePath = this._getCurrentProjectConfigFilePath();
return (0, safelyGetFileContent_1.safelyGetFileContent)(filePath);
}
/**
* Use this function to pull out the details from the .onboardbase.yaml file
* into memory
*
* The file is expected to be in the current directory where the command is
* executed e.g:
* $ ~/work/path: onboardbase run env
*
* A `.onboardbase.yaml` file is expected to be found at
* ~/work/path/.onboardbase.yaml
*
*/
_loadCurrentProjectConfig() {
const configContent = this._getCurrentProjectConfigFileContent();
return configContent ? YAML.parse(configContent) : {};
}
_getOldGlobalConfigPath() {
return (0, path_1.join)(HOME_DIR, OLD_GLOBAL_CONFIG_YAML);
}
/**
*
* @returns
*/
_readOldGlobalConfigFromDisk() {
try {
const filePath = this._getOldGlobalConfigPath();
const fileContent = (0, fs_1.readFileSync)(filePath);
(0, fs_1.writeFileSync)(`${filePath}.bak`, fileContent.toString());
return fileContent.toString("utf-8");
}
catch (error) {
if (error.code !== "ENOENT") {
throw errors_1.BadConfigError.from(error.message);
}
}
}
_getGlobalConfigPath() {
(0, fs_1.mkdirSync)(this.GLOBAL_CONFIG_DIR, { recursive: true });
return (0, path_1.join)(this.GLOBAL_CONFIG_DIR, GLOBAL_CONFIG_FILENAME);
}
_writeGlobalConfigToDisk(content) {
try {
const filePath = this._getGlobalConfigPath();
(0, fs_1.writeFileSync)(filePath, content);
}
catch (error) {
return errors_1.BadConfigError.from("unable to syncronise config to disk");
}
}
_migrateDeprecatedConfigFormat() {
if ((0, fs_1.existsSync)(this._getGlobalConfigPath()))
return;
const oldConfigContent = this._readOldGlobalConfigFromDisk();
if (!oldConfigContent)
return;
const parsedConent = YAML.parse(oldConfigContent);
this._writeGlobalConfigToDisk(JSON.stringify(parsedConent));
// unlinkSync(this._getOldGlobalConfigPath()); // TODO: enable after complete migration
}
_readGlobalConfigFromDisk(haltOnErr = true) {
try {
const filePath = this._getGlobalConfigPath();
const content = (0, fs_1.readFileSync)(filePath);
return content.toString();
}
catch (error) {
const instr = `You might need to run ${chalk.bold(chalk.red(`onboardbase login`))} to start setup process`;
console.warn(`Could not find global config`);
console.info(haltOnErr ? instr : "");
if (haltOnErr)
throw errors_1.BadConfigError.from("unable to read global config file from disk");
return "";
}
}
_checkIfGlobalConfigExists() {
return (0, fs_1.existsSync)(this._getGlobalConfigPath());
}
_loadGlobalConfig(haltOnErr = true) {
this._migrateDeprecatedConfigFormat();
const content = this._readGlobalConfigFromDisk(haltOnErr);
if (!content)
return DEFAULT_GLOBAL_CONFIGURATION;
const parseConfig = JSON.parse(content);
return parseConfig;
}
/**
* Merges the current working directory (cwd) configuration with the root configuration.
* If a key exists in the root configuration but not in the cwd configuration, it is set to REFPATH_VALUE.
* @param cwdConfig The current working directory configuration.
* @returns The merged configuration.
*/
_mergeCWDConfigWithRootConfig(cwdConfig) {
// default to empty object if the config is falsy(null | undefined)
const rootConfig = this.getFromGlobal(`scoped./`) || {};
// get the key differences
const keyNeedle = ["token", "api-host", "dashboard-host", "auth-result"];
keyNeedle.forEach((needle) => {
// set REFPATH on keys that exist on rootConfig but not in cwdConfig
if (!cwdConfig[needle] && rootConfig[needle]) {
cwdConfig[needle] = exports.REFPATH_VALUE;
}
});
cwdConfig.merged = true;
return cwdConfig;
}
_bootstrapGlobalConfig() {
// assumes that global config is already in memory.
const cwdConfigPath = `scoped.${process.cwd()}`;
// get the cwd config
const cwdConfig = this.getFromGlobal(cwdConfigPath) || {};
// return if the cwd config is already merged
if (cwdConfig.merged)
return cwdConfig;
// merge the root config with the global config
this._mergeCWDConfigWithRootConfig(cwdConfig);
// set the cwd config as the merged config
this.setGlobal(cwdConfigPath, cwdConfig);
}
setScopeConfig(key, value) {
const _key = `scoped.${this._CURRENT_DIR_SCOPE}.${key}`;
return this.setGlobal(_key, value);
}
/**
* Sets a configuration for the current scope.
* @param subPath - Optional sub-path within the current scope.
* @returns The configuration for the current scope.
*/
getScopeConfig(subPath) {
return this.getFromGlobal(`scoped.${this._CURRENT_DIR_SCOPE}${subPath ? `.${subPath}` : ""}`);
}
getProjectConfigStat() {
return util.promisify(fs.stat)(`${process.cwd()}/${exports.PROJECT_CONFIG_FILENAME}`);
}
loadAllConfig(cmdConfig, haltOnErr = true) {
this.loadPartialConfig(cmdConfig, haltOnErr);
this.projectConfig = this._loadCurrentProjectConfig();
this._bootstrapGlobalConfig();
}
// TODO: this method might need to be merged with `loadAllConfig` - but it's currently needed for the `login` command
loadPartialConfig(cmdConfig, haltOnErr = true) {
this.GLOBAL_CONFIG_DIR = cmdConfig.configDir;
this.GLOBAL_CACHE_DIR = cmdConfig.cacheDir;
this.globalConfig = this._loadGlobalConfig(haltOnErr);
this._setHostURLs();
}
_setHostURLs() {
ConfigManager.CURRENT_SCOPE_API_HOST =
this.getScopeConfig("api-host") || defaults_1.DEFAULT_API_HOST;
ConfigManager.CURRENT_SCOPE_DASHBOARD_HOST =
this.getScopeConfig("api-host") || defaults_1.DEFAULT_DASHBOARD_HOST;
}
_stringifyCurrentProjectConfig() {
return YAML.stringify(this.projectConfig);
}
_writeCurrentProjectToDisk(content) {
try {
const filePath = this._getCurrentProjectConfigFilePath();
(0, fs_1.writeFileSync)(filePath, content);
}
catch (error) {
return "";
}
}
/**
* Involves reading local and global config objects then storing them to disk
* Both config objects have their different storage paths
* See their implementation for more details
*/
async persistConfigs() {
this._persistCurrentProjectConfig();
const stat = await this.getProjectConfigStat();
this.setScopeConfig("projectConfigLastModified", stat.mtimeMs);
this._persistGlobalConfig();
}
_persistCurrentProjectConfig() {
const content = this._stringifyCurrentProjectConfig();
content.trim() && this._writeCurrentProjectToDisk(content);
}
_persistGlobalConfig() {
const globalConfigStr = JSON.stringify(this.globalConfig);
this._writeGlobalConfigToDisk(globalConfigStr);
}
_get(path, obj) {
return path.split(".").reduce((acc, part) => acc && acc[part], obj);
}
_generateRootConfigKeyFromKey(key) {
const splitKey = key.split(".");
const baseKey = splitKey[splitKey.length - 1];
return `scoped./.${baseKey}`;
}
/**
* Get a value from the global config
* @param scopedKey: the format is `scoped.${scope_path}.${key}`
* @example`scoped./.token` - get the token from the root config
* @example `scoped./path/to/project/.token` - get the token from scoped config to a project path
* @returns
*/
getFromGlobal(scopedKey) {
const value = this._get(scopedKey, this.globalConfig);
if (value === exports.REFPATH_VALUE) {
// convert to root config scopedKey
const rootConfigKey = this._generateRootConfigKeyFromKey(scopedKey);
return this.getFromGlobal(rootConfigKey);
}
return value;
}
/**
* Get a value from the root config. The methood is a wrapper around `getFromGlobal`
* @param key the key to get from the root config
* @returns
*/
getFromRootConfig(key) {
return this.getFromGlobal(`scoped./.${key}`);
}
getFromProject(key) {
return this._get(key, this.projectConfig);
}
getFromProcessEnv(key) {
return process.env[key];
}
setGlobal(key, val) {
return this._set(key, val, this.globalConfig);
}
setDefaultGlobalConfig(data) {
const { scope, content } = data;
const config = this.globalConfig;
if (config === null || config === void 0 ? void 0 : config.scoped) {
this.globalConfig = Object.assign(config, {
scoped: Object.assign(config.scoped, {
[scope]: content,
}),
});
}
else {
this.globalConfig = {
scoped: {
[scope]: content,
},
"version-check": {},
};
}
this._persistGlobalConfig();
}
setProject(key, val) {
return this._set(key, val, this.projectConfig);
}
/**
* Sets the value at the specified path in the given source object.
* If the path does not exist, it creates the necessary nested objects along the way.
*
* @param path - The path to set the value at, using dot notation.
* @param data - The value to set at the specified path.
* @param source - The source object to set the value in.
* @returns The modified source object.
*/
_set(path, data, source) {
const splitPath = path.split("."); // key.key1.ney.asdfasdf => 1
return splitPath.reduce((acc, part, ind) => {
var _a;
if (ind === splitPath.length - 1) {
acc[part] = data;
}
else {
acc[part] = (_a = acc[part]) !== null && _a !== void 0 ? _a : {};
}
return acc[part];
}, source);
}
setRSAKeys(data) {
this.RSA_KEYS = data;
}
getRSAKeys() {
return this.RSA_KEYS;
}
setAndPersistGlobalConfig(key, val) {
this.setGlobal(key, val);
this.persistConfigs();
}
getEnvPrefix(userPrefix) {
const prefix = userPrefix !== null && userPrefix !== void 0 ? userPrefix : this.getFromProject("setup.prefix");
return prefix;
}
}
exports.ConfigManager = ConfigManager;
ConfigManager.CURRENT_SCOPE_API_HOST = defaults_1.DEFAULT_API_HOST;
ConfigManager.CURRENT_SCOPE_DASHBOARD_HOST = defaults_1.DEFAULT_DASHBOARD_HOST;