@squiz/dxp-cli
Version:
The command line interface for dxp
205 lines (180 loc) • 6.41 kB
JavaScript
/*!
* @license
* Copyright Squiz Australia Pty Ltd. All Rights Reserved.
*/
/* eslint-disable unicorn/no-abusive-eslint-disable */
/* eslint-disable */
const readline = require('readline');
const readlineInterface = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const { spawnSync } = require('child_process');
const { readdirSync, existsSync } = require('fs');
const homedir = require('os').homedir();
/**
* The OS specific path to the config store folder.
* @constant {string}
*/
const CONFIG_FOLDER_PATH = `${homedir}/.config`;
/**
* The OS specific path to the dxp config store folder.
* @constant {string}
*/
const DXP_CONFIG_FOLDER_PATH = `${CONFIG_FOLDER_PATH}/dxp`;
/**
* The OS specific path to the dxp plugins config store folder.
* @constant {string}
*/
const DXP_PLUGINS_FOLDER_PATH = `${DXP_CONFIG_FOLDER_PATH}/plugins`;
/**
* Asynchronously run forEach over array.
* @param {array} - The array to iterate and call async function on it.
* @param {callback} - Async callback to run on each element.
* @return {void}
*/
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
/**
* Prompt the question text to the command line and get the input.
* @param {string} questionText - The question text to prompt.
* @return {Promise}
*/
function ask(questionText) {
return new Promise((resolve, reject) => {
readlineInterface.question(questionText, (input) => resolve(input));
});
}
/**
* Helper function to print text message in boxed format.
* @param {string} message - The message to print in boxed format.
* @return {void}
*/
function printHeader(message) {
console.log(``);
console.log(`-`.repeat(message.length));
console.log(message);
console.log(`-`.repeat(message.length));
}
/**
* Executes CLI command.
* @param {string} cmd - The raw command string to run.
* @param {string} description - The optional description to to describe the command.
* @return {object}
* @throws {Error} - Thrown for non-zero exit code.
*/
async function runCmd(cmd, description = null) {
printHeader(`${description === null ? 'Running' : description}: $ ${cmd}`);
const options = { stdio: [process.stdin.fd, process.stdout.fd, process.stderr.fd] };
const cmdParts = cmd.split(` `);
const command = cmdParts[0];
const args = cmdParts.length > 1 ? cmdParts.slice(1) : [];
const result = await spawnSync(command, args, options);
result.stdout = Buffer.isBuffer(result.stdout) ? result.stdout.toString() : ``;
result.stderr = Buffer.isBuffer(result.stderr) ? result.stderr.toString() : ``;
if (result.status !== 0) {
throw new Error(result.stderr);
}
return result;
}
/**
* Get the list of installed dxp plugins.
* @returns {array}
*/
async function getDxpPlugins() {
if (existsSync(DXP_PLUGINS_FOLDER_PATH) === false) {
return [];
}
return (await readdirSync(`${DXP_PLUGINS_FOLDER_PATH}/node_modules/@squiz`)).filter(
(pluginFolderName) => pluginFolderName !== `dxp-plugin-core` && pluginFolderName.indexOf(`dxp-plugin`) === 0,
);
}
/**
* Perform DXP CLI upgrade.
* @param {array} pluginNames - The list of plugin names to upgrade.
* @param {string} version - Optional package version number.
* @return {void}
*/
async function upgrade(pluginNames = [], version = null) {
await runCmd(`npm uninstall -g @squiz/dxp-cli`, `Uninstall @squiz/dxp-cli package`);
await runCmd(`rm -rf ${DXP_CONFIG_FOLDER_PATH}`, `Clean up DXP config folder`);
// List of plugins that have been removed entirely. If the user already had them before,
// exclude it from the list.
const retiredPlugins = [
'dxp-plugin-core-internal',
'dxp-plugin-datastore',
'dxp-plugin-datastore-internal',
'dxp-plugin-funnelback',
'dxp-plugin-funnelback-internal',
'dxp-plugin-ipaas',
'dxp-plugin-ipaas-internal',
'dxp-plugin-matrix',
'dxp-plugin-matrix-internal',
'dxp-plugin-sxp',
'dxp-plugin-sxp-internal',
'dxp-plugin-workplace',
'dxp-plugin-workplace-internal',
];
pluginNames = pluginNames.filter((pluginName) => retiredPlugins.includes(pluginName) === false);
await runCmd(
`npm install -g --silent @squiz/dxp-cli${version === null ? '' : '@' + version}`,
`Install @squiz/dxp-cli package`,
);
await asyncForEach(pluginNames, async (pluginName) => {
await runCmd(
`dxp plugin add @squiz/${pluginName}${version === null ? '' : '@' + version}`,
`Installing ${pluginName} plugin`,
);
});
await runCmd(`dxp --version`, `Show the updated DXP CLI version`);
}
async function main() {
try {
const plugins = await getDxpPlugins();
if (plugins.length > 0) {
printHeader(`${plugins.length} dxp plugins found to upgrade:`);
plugins.forEach((pluginName) => console.log(` - ${pluginName}`));
}
// If any internal plugin exist, then confirm the NPM login.s
if (plugins.filter((pluginName) => pluginName.indexOf(`-internal`) !== -1).length > 0) {
try {
await runCmd(`npm whoami`, `Checking NPM credential`);
console.log(``);
} catch (noNpmLoginErr) {
console.error(``);
console.error(`You must be login to NPM to install internal DXP plugins`);
process.exit(1);
}
}
let answer = await ask(
`Do you want to upgrade all plugins to ` +
(process.env.DXP_CLI_VERSION ? process.env.DXP_CLI_VERSION : `the latest stable`) +
` version? (y/N) `,
);
answer = answer.trim();
if (answer.toLowerCase() === `y`) {
await upgrade(plugins, process.env.DXP_CLI_VERSION ? process.env.DXP_CLI_VERSION : null);
} else if (answer === `` || answer.toLowerCase() === `n`) {
console.log(``);
console.log(`Bye~`);
process.exit(0);
} else {
console.error(`Unknown answer`);
process.exit(1);
}
process.exit(0);
} catch (err) {
const docLink = `https://docs.squiz.systems/experience-cloud/latest/customer-success-admin-guide/administering-with-the-cli.html#upgrading-the-cli-tools-for-administrators`;
console.error(
`Error occurred during the upgrade. Please follow the Upgrading the CLI tools for administrators guide from ${docLink}`,
);
process.exit(1);
}
}
if (require.main === module) {
main();
}