UNPKG

@contentstack/cli-cm-clone

Version:
267 lines (243 loc) 9.64 kB
const { Command } = require('@contentstack/cli-command'); const { configHandler, flags, isAuthenticated, managementSDKClient } = require('@contentstack/cli-utilities'); const { CloneHandler } = require('../../../lib/util/clone-handler'); const path = require('path'); const { rimraf } = require('rimraf'); const merge = require('merge'); let pathdir = path.join(__dirname.split('src')[0], 'contents'); const { readdirSync, readFileSync } = require('fs'); let config = {}; class StackCloneCommand extends Command { async run() { try { let self = this; const { flags: cloneCommandFlags } = await self.parse(StackCloneCommand); const { yes, type: cloneType, 'stack-name': stackName, 'source-branch': sourceStackBranch, 'target-branch': targetStackBranch, 'source-stack-api-key': sourceStackApiKey, 'destination-stack-api-key': destinationStackApiKey, 'source-management-token-alias': sourceManagementTokenAlias, 'destination-management-token-alias': destinationManagementTokenAlias, 'import-webhook-status': importWebhookStatus, config: externalConfigPath, } = cloneCommandFlags; const handleClone = async () => { const listOfTokens = configHandler.get('tokens'); if (externalConfigPath) { let externalConfig = readFileSync(externalConfigPath, 'utf-8'); externalConfig = JSON.parse(externalConfig); config = merge.recursive(config, externalConfig); } config.forceStopMarketplaceAppsPrompt = yes; config.skipAudit = cloneCommandFlags['skip-audit']; if (cloneType) { config.cloneType = cloneType; } if (stackName) { config.stackName = stackName; } if (sourceStackBranch) { config.sourceStackBranch = sourceStackBranch; } if (targetStackBranch) { config.targetStackBranch = targetStackBranch; } if (sourceStackApiKey) { config.source_stack = sourceStackApiKey; } if (destinationStackApiKey) { config.target_stack = destinationStackApiKey; } if (sourceManagementTokenAlias && listOfTokens[sourceManagementTokenAlias]) { config.source_alias = sourceManagementTokenAlias; config.source_stack = listOfTokens[sourceManagementTokenAlias].apiKey; } else if (sourceManagementTokenAlias) { console.log(`Provided source token alias (${sourceManagementTokenAlias}) not found in your config.!`); } if (destinationManagementTokenAlias && listOfTokens[destinationManagementTokenAlias]) { config.destination_alias = destinationManagementTokenAlias; config.target_stack = listOfTokens[destinationManagementTokenAlias].apiKey; } else if (destinationManagementTokenAlias) { console.log( `Provided destination token alias (${destinationManagementTokenAlias}) not found in your config.!`, ); } if (importWebhookStatus) { config.importWebhookStatus = importWebhookStatus; } await this.removeContentDirIfNotEmptyBeforeClone(pathdir); // NOTE remove if folder not empty before clone this.registerCleanupOnInterrupt(pathdir); config.auth_token = configHandler.get('authtoken'); config.host = this.cmaHost; config.cdn = this.cdaHost; config.pathDir = pathdir; const cloneHandler = new CloneHandler(config); const managementAPIClient = await managementSDKClient(config); cloneHandler.setClient(managementAPIClient); cloneHandler.execute().catch((error) => { console.log(error); }); }; if (sourceManagementTokenAlias && destinationManagementTokenAlias) { if (sourceStackBranch || targetStackBranch) { if (isAuthenticated()) { handleClone(); } else { console.log('Please login to execute this command, csdx auth:login'); this.exit(1); } } else { handleClone(); } } else if (isAuthenticated()) { handleClone(); } else { console.log('Please login to execute this command, csdx auth:login'); this.exit(1); } } catch (error) { if (error) { await this.cleanUp(pathdir); // eslint-disable-next-line no-console console.log(error.message || error); } } } async removeContentDirIfNotEmptyBeforeClone(dir) { try { const dirNotEmpty = readdirSync(dir).length; if (dirNotEmpty) { await this.cleanUp(dir); } } catch (error) { const omit = ['ENOENT']; // NOTE add emittable error codes in the array if (!omit.includes(error.code)) { console.log(error.message); } } } async cleanUp(pathDir, message) { try { await rimraf(pathDir); if (message) { // eslint-disable-next-line no-console console.log(message); } } catch (err) { if (err) { console.log('\nCleaning up'); const skipCodeArr = ['ENOENT', 'EBUSY', 'EPERM', 'EMFILE', 'ENOTEMPTY']; if (skipCodeArr.includes(err.code)) { process.exit(); } } } } registerCleanupOnInterrupt(pathDir) { const interrupt = ['SIGINT', 'SIGQUIT', 'SIGTERM']; const exceptions = ['unhandledRejection', 'uncaughtException']; const cleanUp = async (exitOrError) => { if (exitOrError) { // eslint-disable-next-line no-console console.log('\nCleaning up'); await this.cleanUp(pathDir); // eslint-disable-next-line no-console console.log('done'); // eslint-disable-next-line no-process-exit if (exitOrError instanceof Promise) { exitOrError.catch((error) => { console.log((error && error.message) || ''); }); } else if (exitOrError.message) { console.log(exitOrError.message); } else if (exitOrError.errorMessage) { console.log(exitOrError.message); } if (exitOrError === true) process.exit(); } }; exceptions.forEach((event) => process.on(event, cleanUp)); interrupt.forEach((signal) => process.on(signal, () => cleanUp(true))); } } StackCloneCommand.description = `Clone data (structure/content or both) of a stack into another stack Use this plugin to automate the process of cloning a stack in few steps. `; StackCloneCommand.examples = [ 'csdx cm:stacks:clone', 'csdx cm:stacks:clone --source-branch <source-branch-name> --target-branch <target-branch-name> --yes', 'csdx cm:stacks:clone --source-stack-api-key <apiKey> --destination-stack-api-key <apiKey>', 'csdx cm:stacks:clone --source-management-token-alias <management token alias> --destination-management-token-alias <management token alias>', 'csdx cm:stacks:clone --source-branch --target-branch --source-management-token-alias <management token alias> --destination-management-token-alias <management token alias>', 'csdx cm:stacks:clone --source-branch --target-branch --source-management-token-alias <management token alias> --destination-management-token-alias <management token alias> --type <value a or b>', ]; StackCloneCommand.aliases = ['cm:stack-clone']; StackCloneCommand.flags = { 'source-branch': flags.string({ required: false, multiple: false, description: 'Branch of the source stack.', }), 'target-branch': flags.string({ required: false, multiple: false, description: 'Branch of the target stack.', }), 'source-management-token-alias': flags.string({ required: false, multiple: false, description: 'Source management token alias.', }), 'destination-management-token-alias': flags.string({ required: false, multiple: false, description: 'Destination management token alias.', }), 'stack-name': flags.string({ char: 'n', required: false, multiple: false, description: 'Provide a name for the new stack to store the cloned content.', }), type: flags.string({ required: false, multiple: false, options: ['a', 'b'], description: ` Type of data to clone. You can select option a or b. a) Structure (all modules except entries & assets). b) Structure with content (all modules including entries & assets). `, }), 'source-stack-api-key': flags.string({ description: 'Source stack API key', }), 'destination-stack-api-key': flags.string({ description: 'Destination stack API key', }), 'import-webhook-status': flags.string({ description: '[default: disable] (optional) The status of the import webhook. <options: disable|current>', options: ['disable', 'current'], required: false, default: 'disable', }), yes: flags.boolean({ char: 'y', required: false, description: 'Force override all Marketplace prompts.', }), 'skip-audit': flags.boolean({ description: ' (optional) Skips the audit fix that occurs during an import operation.', }), config: flags.string({ char: 'c', required: false, description: 'Path for the external configuration', }), }; StackCloneCommand.usage = 'cm:stacks:clone [--source-branch <value>] [--target-branch <value>] [--source-management-token-alias <value>] [--destination-management-token-alias <value>] [-n <value>] [--type a|b] [--source-stack-api-key <value>] [--destination-stack-api-key <value>] [--import-webhook-status disable|current]'; module.exports = StackCloneCommand;