UNPKG

flexmonster-cli

Version:

CLI for Flexmonster Pivot Table & Charts installation

466 lines (449 loc) 18.7 kB
import chalk from 'chalk'; import escExit from 'esc-exit'; import fs from 'fs'; import inquirer from 'inquirer'; import Listr from 'listr'; import { sync } from 'rimraf'; import decompress from 'decompress'; import { basename } from 'path'; import { commandInAction, COMMANDS, program, tempDirURL } from '../cli.js'; import { listChoices, copyFolderSync, getArchiveNameFromURL, getOSString, isWindows, isMacOS, isLinux } from '../utils.js'; import { deleteDownloadedArchive, downloadArchive, getServerSideToolFolder, installAccelerator, isFA, isFDS, isForOS, isClientSideTool, installClientSideTool, isServerSideTool, getTool, isValidTool, getServerToolByOS, handleServiceUpdateProcess, updateAndRunGUITool, updateAndRunServiceTool, uninstallServiceTool, checkIfServiceInstalled, stopService, closeGUITool } from './base.js'; import { promisify } from 'util'; // import { exec as execNonPromise } from 'child_process'; // const exec = promisify(execNonPromise); const TOOLS_TO_UPDATE = [{ name: 'fds', value: 'fds', title: 'Flexmonster Data Server', description: 'Flexmonster Data Server (a server-side tool)', guiToolName: 'Flexmonster Admin Panel', fileToBestGuess: 'flexmonster-config.json', os: [{ name: 'Linux (x64)', url: 'https://dist.flexmonster.com/flexmonster-data-server/2.9/latest/FlexmonsterDataServer-linux-x64.tar.gz', executable: 'flexmonster-data-server', serviceExecutable: 'service-install', serviceUninstaller: 'service-uninstall', serviceStatusCommand: 'systemctl status flexmonster-data-server.service', serviceStopCommand: 'sudo systemctl stop flexmonster-data-server.service', guiToolStopCommand: 'killall flexmonster-admin-panel', guiExecutable: 'Flexmonster-Admin-Panel.AppImage' }, { name: 'Linux (ARM64)', url: 'https://dist.flexmonster.com/flexmonster-data-server/2.9/latest/FlexmonsterDataServer-linux-arm64.tar.gz', executable: 'flexmonster-data-server', serviceExecutable: 'service-install', serviceUninstaller: 'service-uninstall', serviceStatusCommand: 'systemctl status flexmonster-data-server.service', serviceStopCommand: 'sudo systemctl stop flexmonster-data-server.service', guiToolStopCommand: 'killall flexmonster-admin-panel', guiExecutable: 'Flexmonster-Admin-Panel.AppImage' }, { name: 'macOS (x64)', url: 'https://dist.flexmonster.com/flexmonster-data-server/2.9/latest/FlexmonsterDataServer-osx-x64.tar.gz', executable: 'flexmonster-data-server', serviceExecutable: 'service-install.sh', serviceUninstaller: 'service-uninstall.sh', serviceStatusCommand: 'ls ~/Library/LaunchAgents/com.flexmonster.DataServer.plist', serviceStopCommand: 'launchctl unload -w ~/Library/LaunchAgents/com.flexmonster.DataServer.plist', guiToolStopCommand: 'killall "Flexmonster Admin Panel"', guiExecutable: 'Flexmonster-Admin-Panel.dmg' }, { name: 'macOS (ARM64)', url: 'https://dist.flexmonster.com/flexmonster-data-server/2.9/latest/FlexmonsterDataServer-osx-arm64.tar.gz', executable: 'flexmonster-data-server', serviceExecutable: 'service-install.sh', serviceUninstaller: 'service-uninstall.sh', serviceStatusCommand: 'ls ~/Library/LaunchAgents/com.flexmonster.DataServer.plist', serviceStopCommand: 'launchctl unload -w ~/Library/LaunchAgents/com.flexmonster.DataServer.plist', guiToolStopCommand: 'killall "Flexmonster Admin Panel"', guiExecutable: 'Flexmonster-Admin-Panel.dmg' }, { name: 'Windows (x64)', url: 'https://dist.flexmonster.com/flexmonster-data-server/2.9/latest/FlexmonsterDataServer-win-x64.zip', executable: 'flexmonster-data-server', serviceExecutable: 'service-install.bat', serviceUninstaller: 'service-uninstall.bat', serviceStatusCommand: 'sc query FlexmonsterDataServer', serviceStopCommand: 'sc stop FlexmonsterDataServer', guiToolStopCommand: 'taskkill /F /IM "Flexmonster Admin Panel.exe"', guiExecutable: 'Flexmonster-Admin-Panel.exe' }, { name: 'Windows (x86)', url: 'https://dist.flexmonster.com/flexmonster-data-server/2.9/latest/FlexmonsterDataServer-win-x86.zip', executable: 'flexmonster-data-server', serviceExecutable: 'service-install.bat', serviceUninstaller: 'service-uninstall.bat', serviceStatusCommand: 'sc query FlexmonsterDataServer', serviceStopCommand: 'sc stop FlexmonsterDataServer', guiToolStopCommand: 'taskkill /F /IM "Flexmonster Admin Panel.exe"', guiExecutable: 'Flexmonster-Admin-Panel.exe', } ], }, { name: 'accelerator', value: 'accelerator', title: 'Flexmonster Accelerator for SSAS', description: 'Flexmonster Accelerator for SSAS (a server-side tool)', os: [{ name: 'Windows (x64)', url: 'https://dist.flexmonster.com/flexmonster-accelerator/2.9/latest/FlexmonsterAccelerator.zip', dir: '', executable: 'Flexmonster Accelerator.msi', }, { name: 'Windows (x86)', url: 'https://dist.flexmonster.com/flexmonster-accelerator/2.9/latest/FlexmonsterAccelerator.zip', dir: '', executable: 'Flexmonster Accelerator.msi', } ], }, { name: 'ng-flexmonster', value: 'ng-flexmonster', title: 'Flexmonster Pivot wrapper for Angular 15 and older projects (legacy)', description: 'Flexmonster Pivot wrapper for Angular 15 and older projects (a legacy node module)', fileToBestGuess: 'package.json', module: 'ng-flexmonster', }, { name: 'ngx-flexmonster', value: 'ngx-flexmonster', title: 'Flexmonster Pivot wrapper for Angular 14+ projects', description: 'Flexmonster Pivot wrapper for Angular 14+ projects (a node module)', fileToBestGuess: 'package.json', module: 'ngx-flexmonster', }, { name: 'react-flexmonster', value: 'react-flexmonster', title: 'Flexmonster Pivot wrapper for React projects', description: 'Flexmonster Pivot wrapper for React projects (a node module)', fileToBestGuess: 'package.json', module: 'react-flexmonster', }, { name: 'vue-flexmonster', value: 'vue-flexmonster', title: 'Flexmonster Pivot for Vue projects', description: 'Flexmonster Pivot for Vue projects (a node module)', fileToBestGuess: 'package.json', module: 'vue-flexmonster', }, { name: 'js-flexmonster', value: 'flexmonster', title: 'Flexmonster Pivot', description: 'Flexmonster Pivot (a node module)', fileToBestGuess: 'package.json', module: 'flexmonster', }, ]; function toolBestGuess() { for (let i = 0; i < TOOLS_TO_UPDATE.length; i++) { const toolObj = TOOLS_TO_UPDATE[i]; const fileToBestGuess = toolObj.fileToBestGuess; // console.log(">>>>", fileToBestGuess, fs.existsSync(fileToBestGuess)); // console.log(">>>>", "flexmonster-data-server", fs.existsSync("flexmonster-data-server")); if (fs.existsSync(fileToBestGuess)) { if (toolObj.module !== undefined) { var content = fs.readFileSync(fileToBestGuess, 'utf8'); const dIdx = content.indexOf("dependencies"); if (dIdx > -1 && content.indexOf('"' + toolObj.module + '":') > dIdx) { //console.log(">>!!!", toolObj.name, toolObj.value); return toolObj.value; } } else { return toolObj.value; } } } return undefined; } const Q_COMMAND_UPDATE = [{ type: 'list', name: 'tool', message: 'Choose what should be updated:', choices: listChoices(TOOLS_TO_UPDATE), when: function (answers) { const tool = toolBestGuess(); if (tool !== undefined) { answers.tool = tool; return false; } return true; } }]; function composeCommandUpdateQuestions(argAnswers) { return Q_COMMAND_UPDATE; } export function initUpdateCommand() { program.command('update [tool|module]') // sub-command name .alias('u') // alternative sub-command .description(COMMANDS[2].description) // command description .action(function (tool, args) { // function to execute when command is used commandInAction(); let argAnswers = {}; const toolGuess = toolBestGuess(); if (isValidTool(TOOLS_TO_UPDATE, tool)) { executeUpdate(tool, args); } else if (isValidTool(TOOLS_TO_UPDATE, toolGuess)) { tool = toolGuess; executeUpdate(tool, args); } else { if (tool !== undefined) { console.log(''); console.log('~ update command has an invalid tool name: %s', tool); console.log(''); } helpUpdate(argAnswers); } }) .addHelpText('after', ` Examples: flexmonster update accelerator flexmonster update fds flexmonster update flexmonster flexmonster update js-flexmonster flexmonster update ng-flexmonster flexmonster update react-flexmonster flexmonster update vue-flexmonster `); } export function helpUpdate(argAnswers) { escExit(); inquirer.prompt(composeCommandUpdateQuestions(argAnswers)) .then(function (answers) { executeUpdate(answers["tool"], answers); }); } async function executeUpdate(tool, args) { tool = tool.toLowerCase(); const toolObj = getTool(TOOLS_TO_UPDATE, tool); let wasServiceInstalled; const osObj = getServerToolByOS(TOOLS_TO_UPDATE, tool, getOSString()); if (!isForOS(TOOLS_TO_UPDATE, tool)) { console.log(''); console.log('~ there is no %s for %s operating system', toolObj.title, chalk.blueBright(getOSString())); // if (isMacOS(process.platform)) console.log(' try %s as a possible solution or', chalk.underline('https://www.flexmonster.com/doc/troubleshooting-cli/#no-fds-for-macos')); console.log(' contact us at %s to address any questions', chalk.underline('https://www.flexmonster.com/technical-support/')); console.log(''); } else { console.log(''); if (isFDS(tool)) { console.log(chalk.italic(`Preparing to update ${toolObj.title}`)); // Check if flexmonster-data-server directory exists in cwd if (fs.existsSync(osObj.executable) && fs.lstatSync(osObj.executable).isDirectory()) { try { // Try switching to flexmonster-data-server directory process.chdir(osObj.executable); } catch (err) { } } wasServiceInstalled = await checkIfServiceInstalled(osObj); if (wasServiceInstalled === true) { // console.log("need to stop service"); const stoppedService = await stopService(toolObj, osObj); const closedAdminPanel = await closeGUITool(osObj); // console.log("service and admin panel were closed ", stoppedService, closedAdminPanel); if (stoppedService) console.log(chalk.green(`${toolObj.title} service is stopped`)); if (closedAdminPanel) console.log(chalk.green(`${toolObj.guiToolName} is stopped`)); } // else { // console.log("service was not previously installed"); // } } console.log(''); console.log('--- UPDATE ---'); console.log(''); const tasks = new Listr([ { title: 'Download ' + toolObj.title, task: (ctx, task) => downloadServerSideTool(osObj, task) .catch(() => { const url = osObj.url; throw new Error(chalk.redBright.bold("Error:") + " download has failed. \n" + chalk.yellowBright.bold("Recommendation:") + " Try to download it from here: " + chalk.yellowBright.bold.underline(url)); }), enabled: () => isServerSideTool(tool), }, { title: 'Unpack files to ' + (isFDS(tool) ? 'this folder' : chalk.green.bold(getServerSideToolFolder(tool))), task: () => unpackServerSideTool(tool, osObj) .then(() => { const archiveName = getArchiveNameFromURL(osObj.url); deleteDownloadedArchive(archiveName); }) .catch((error) => { const archiveName = getArchiveNameFromURL(osObj.url); throw new Error(error + chalk.redBright.bold("Error:") + " unpacking has failed. \n" + chalk.yellowBright.bold("Recommendation:") + " Try to unpack it manually: " + chalk.yellowBright.bold(archiveName)); }), enabled: () => isServerSideTool(tool), }, { title: 'Update ' + toolObj.title, task: () => installServerSideTool(tool, osObj), enabled: () => isFA(tool), }, { title: 'Update ' + toolObj.title, task: () => installClientSideTool(toolObj), enabled: () => isClientSideTool(tool), }, { title: `Uninstall previous ${toolObj.title} service version`, task: () => uninstallServiceTool(osObj, toolObj) .catch(error => { throw error; }), enabled: () => !isLinux(process.platform) && wasServiceInstalled && isFDS(tool) }, { title: `Update ${toolObj.title} service`, task: () => updateAndRunServiceTool(osObj, toolObj) .catch(error => { throw error; }), enabled: () => !isLinux(process.platform) && wasServiceInstalled && isFDS(tool) }, { title: `Update ${toolObj.guiToolName} (the service GUI tool)` + (isMacOS(process.platform) ? `\n${chalk.blueBright(`Hint: please use Finder to drag the app to the "Applications" folder`)}` : ""), task: () => updateAndRunGUITool(osObj, toolObj) .catch(error => { throw error; }), enabled: () => !isLinux(process.platform) && wasServiceInstalled && isFDS(tool) } ]); tasks.run() .then(() => { console.log(chalk.green.bold('DONE')); console.log(''); //reinstall service and start Admin Panel if (wasServiceInstalled && isServerSideTool(tool) && !isFA(tool) && !(isWindows(process.platform) || isMacOS(process.platform))) { console.log(chalk.italic(`Updating ${toolObj.title} service (requires sudo privileges)`)); console.log(''); uninstallServiceTool(osObj, toolObj) .then(() => { console.log(chalk.green(`Uninstalled previous ${toolObj.title} service version`)) console.log(''); handleServiceUpdateProcess(tool, osObj, toolObj) }) .catch((error) => { console.log(chalk.red("Error: Failed to uninstall previous version")) console.log(chalk.red(`Details: ${error.message}`)) console.log(''); handleServiceUpdateProcess(tool, osObj, toolObj) }); } }) .catch(error => { console.error(error.message); console.log(''); }); } } async function downloadServerSideTool(osObj, task) { const url = osObj.url; const archiveName = getArchiveNameFromURL(osObj.url); return await downloadArchive(url, archiveName, task); } function unpackServerSideTool(tool, osObj) { //create temp directory for unpacking if (!fs.existsSync(tempDirURL)) { fs.mkdirSync(tempDirURL); } try { const archiveName = getArchiveNameFromURL(osObj.url); const toolObj = getTool(TOOLS_TO_UPDATE, tool); let file = decompress(archiveName, tempDirURL, { filter: file => basename(file.path) !== toolObj.fileToBestGuess }); return new Promise((resolve, reject) => { file.then((files) => { let projectFolderURL; if (isFDS(tool)) { projectFolderURL = "."; try { if (osObj.dir !== undefined) { copyFolderSync(tempDirURL + osObj.dir, projectFolderURL); } else { copyFolderSync(tempDirURL, projectFolderURL); } sync(tempDirURL); resolve(); } catch (error) { sync(tempDirURL); reject(error); } } else if (isFA(tool)) { projectFolderURL = "." + getServerSideToolFolder(tool); if (fs.existsSync(projectFolderURL)) { sync(projectFolderURL); } fs.mkdirSync(projectFolderURL); if (osObj.dir !== undefined) { copyFolderSync(tempDirURL + osObj.dir, projectFolderURL); } else { copyFolderSync(tempDirURL, projectFolderURL); } sync(tempDirURL); resolve(); } }); file.catch((error) => { sync(tempDirURL); reject(error); }); }); } catch (error) { sync(tempDirURL); return Promise.reject(new Error('Failed to unpack')); } } // for the Accelerator only function installServerSideTool(tool, osObj) { const url = "." + getServerSideToolFolder(tool); return installAccelerator(url, osObj.executable); }