@contentstack/cli-cm-clone
Version:
Contentstack stack clone plugin
764 lines (763 loc) • 42.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CloneHandler = void 0;
const tslib_1 = require("tslib");
const ora_1 = tslib_1.__importDefault(require("ora"));
const path = tslib_1.__importStar(require("path"));
const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
const chalk_1 = tslib_1.__importDefault(require("chalk"));
const fs = tslib_1.__importStar(require("fs"));
const rimraf_1 = require("rimraf");
const cli_cm_export_1 = tslib_1.__importDefault(require("@contentstack/cli-cm-export"));
const cli_cm_import_1 = tslib_1.__importDefault(require("@contentstack/cli-cm-import"));
// eslint-disable-next-line @typescript-eslint/no-var-requires
const prompt = require('prompt');
const safe_1 = tslib_1.__importDefault(require("@colors/colors/safe"));
const cloneDeep_1 = tslib_1.__importDefault(require("lodash/cloneDeep"));
const cli_utilities_1 = require("@contentstack/cli-utilities");
const command_helpers_1 = require("../helpers/command-helpers");
const constants_1 = require("../../utils/constants");
// Override prompt's stop method
prompt.stop = function () {
if (prompt.stopped) {
return;
}
prompt.emit('stop');
prompt.stopped = true;
return prompt;
};
class CloneHandler {
constructor(opt) {
this.orgUidList = {};
this.stackUidList = {};
this.masterLocaleList = {};
this.config = opt;
this.cloneCommand = new command_helpers_1.Clone();
this.pathDir = opt.pathDir || '';
// Create mutable copy of stack name prompt for dynamic default updates
this.stackNamePrompt = {
type: constants_1.STACK_NAME_PROMPT.type,
name: constants_1.STACK_NAME_PROMPT.name,
default: constants_1.STACK_NAME_PROMPT.default,
message: constants_1.STACK_NAME_PROMPT.message,
};
process.stdin.setMaxListeners(50);
cli_utilities_1.log.debug('Initializing CloneHandler', Object.assign(Object.assign({}, this.config.cloneContext), { pathDir: opt.pathDir, cloneType: opt.cloneType }));
}
setClient(managementSDKClient) {
this.client = managementSDKClient;
}
async getOrganizationChoices(orgMessage) {
const orgChoice = {
type: 'list',
name: 'Organization',
message: orgMessage !== undefined ? orgMessage : 'Choose an organization',
choices: [],
};
return new Promise(async (resolve, reject) => {
cli_utilities_1.log.debug('Fetching organization choices', this.config.cloneContext);
const spinner = (0, ora_1.default)('Fetching Organization').start();
try {
let organizations;
const configOrgUid = cli_utilities_1.configHandler.get('oauthOrgUid');
cli_utilities_1.log.debug('Getting organizations', Object.assign(Object.assign({}, this.config.cloneContext), { hasConfigOrgUid: !!configOrgUid }));
if (configOrgUid) {
organizations = await this.client.organization(configOrgUid).fetch();
}
else {
organizations = await this.client.organization().fetchAll({ limit: 100 });
}
spinner.succeed('Fetched Organization');
cli_utilities_1.log.debug('Fetched organizations', this.config.cloneContext);
for (const element of organizations.items || [organizations]) {
this.orgUidList[element.name] = element.uid;
orgChoice.choices.push(element.name);
}
return resolve(orgChoice);
}
catch (e) {
spinner.fail();
return reject(e);
}
});
}
async handleOrgSelection(options = {}) {
return new Promise(async (resolve, reject) => {
var _a;
const { msg = '', isSource = true } = options || {};
cli_utilities_1.log.debug('Handling organization selection', this.config.cloneContext);
const orgList = await this.getOrganizationChoices(msg).catch(reject);
if (orgList) {
cli_utilities_1.log.debug(`Found ${((_a = orgList.choices) === null || _a === void 0 ? void 0 : _a.length) || 0} organization(s) to choose from`, this.config.cloneContext);
const orgSelected = await inquirer_1.default.prompt(orgList);
cli_utilities_1.log.debug(`Organization selected: ${orgSelected.Organization}`, this.config.cloneContext);
if (isSource) {
this.config.sourceOrg = this.orgUidList[orgSelected.Organization];
cli_utilities_1.log.debug(`Source organization UID: ${this.config.sourceOrg}`, this.config.cloneContext);
}
else {
this.config.targetOrg = this.orgUidList[orgSelected.Organization];
cli_utilities_1.log.debug(`Target organization UID: ${this.config.targetOrg}`, this.config.cloneContext);
}
resolve(orgSelected);
}
});
}
async getStack(answer, stkMessage, isSource = true) {
const stackChoice = {
type: 'list',
name: 'stack',
message: stkMessage !== undefined ? stkMessage : 'Select the stack',
choices: [],
};
return new Promise(async (resolve, reject) => {
cli_utilities_1.log.debug('Fetching stacks', this.config.cloneContext);
const spinner = (0, ora_1.default)('Fetching stacks').start();
try {
const organization_uid = this.orgUidList[answer.Organization];
cli_utilities_1.log.debug('Querying stacks for organization', Object.assign(Object.assign({}, this.config.cloneContext), { organizationUid: organization_uid }));
const stackList = this.client.stack().query({ organization_uid }).find();
stackList
.then((stacklist) => {
cli_utilities_1.log.debug('Fetched stacks', Object.assign(Object.assign({}, this.config.cloneContext), { count: stacklist.items ? stacklist.items.length : 0 }));
for (const element of stacklist.items) {
this.stackUidList[element.name] = element.api_key;
this.masterLocaleList[element.name] = element.master_locale;
stackChoice.choices.push(element.name);
}
spinner.succeed('Fetched stack');
return resolve(stackChoice);
})
.catch((error) => {
spinner.fail();
return reject(error);
});
}
catch (e) {
spinner.fail();
return reject(e);
}
});
}
displayBackOptionMessage() {
const ui = new inquirer_1.default.ui.BottomBar();
ui.updateBottomBar(chalk_1.default.cyan('\nPress shift & left arrow together to undo the operation\n'));
}
setBackKeyPressHandler(backKeyPressHandler) {
this.backKeyPressHandler = backKeyPressHandler;
}
removeBackKeyPressHandler() {
if (this.backKeyPressHandler) {
process.stdin.removeListener('keypress', this.backKeyPressHandler);
}
}
setExectingCommand(command) {
// 0 for org, 1 for stack, 1 for branch, 3 stack cancelled, 4 branch cancelled
this.executingCommand = command;
}
async handleStackSelection(options = {}) {
return new Promise(async (resolve, reject) => {
var _a;
try {
const { org = {}, msg = '', isSource = true } = options || {};
cli_utilities_1.log.debug('Handling stack selection', Object.assign(Object.assign({}, this.config.cloneContext), { isSource, orgName: org.Organization, msg }));
const stackList = await this.getStack(org, msg, isSource).catch(reject);
if (stackList) {
this.displayBackOptionMessage();
cli_utilities_1.log.debug(`Found ${((_a = stackList.choices) === null || _a === void 0 ? void 0 : _a.length) || 0} stack(s) to choose from`, this.config.cloneContext);
const selectedStack = await inquirer_1.default.prompt(stackList);
cli_utilities_1.log.debug(`Stack selected: ${selectedStack.stack}`, this.config.cloneContext);
if (this.executingCommand != 1) {
return reject();
}
if (isSource) {
this.config.sourceStackName = selectedStack.stack;
this.master_locale = this.masterLocaleList[selectedStack.stack];
this.config.source_stack = this.stackUidList[selectedStack.stack];
cli_utilities_1.log.debug(`Source stack configured`, this.config.cloneContext);
}
else {
this.config.target_stack = this.stackUidList[selectedStack.stack];
this.config.destinationStackName = selectedStack.stack;
cli_utilities_1.log.debug(`Target stack configured`, this.config.cloneContext);
}
resolve(selectedStack);
}
}
catch (error) {
return reject(error);
}
});
}
async validateIfBranchExist(stackAPIClient, isSource) {
let spinner;
const completeSpinner = (msg, method = 'succeed') => {
spinner[method](msg);
spinner.stop();
};
try {
const branch = isSource ? this.config.sourceStackBranch : this.config.targetStackBranch;
cli_utilities_1.log.debug('Validating branch existence', this.config.cloneContext);
spinner = (0, ora_1.default)(`Validation if ${isSource ? 'source' : 'target'} branch exist.!`).start();
const isBranchExist = await stackAPIClient
.branch(branch)
.fetch()
.then((data) => data);
if (isBranchExist && typeof isBranchExist === 'object') {
cli_utilities_1.log.debug('Branch validation successful', this.config.cloneContext);
completeSpinner(`${isSource ? 'Source' : 'Target'} branch verified.!`);
}
else {
cli_utilities_1.log.error('Branch not found', this.config.cloneContext);
completeSpinner(`${isSource ? 'Source' : 'Target'} branch not found.!`, 'fail');
process.exit();
}
}
catch (e) {
completeSpinner(`${isSource ? 'Source' : 'Target'} branch not found.!`, 'fail');
throw e;
}
}
async resolveBranchAliases(isSource = false) {
try {
cli_utilities_1.log.debug('Resolving branch aliases', Object.assign(Object.assign({}, this.config.cloneContext), { isSource, alias: isSource ? this.config.sourceStackBranchAlias : this.config.targetStackBranchAlias }));
if (isSource) {
const sourceStack = this.client.stack({ api_key: this.config.source_stack });
this.config.sourceStackBranch = await (0, cli_utilities_1.getBranchFromAlias)(sourceStack, this.config.sourceStackBranchAlias);
cli_utilities_1.log.debug('Source branch alias resolved', Object.assign(Object.assign({}, this.config.cloneContext), { alias: this.config.sourceStackBranchAlias, branch: this.config.sourceStackBranch }));
}
else {
const targetStack = this.client.stack({ api_key: this.config.target_stack });
this.config.targetStackBranch = await (0, cli_utilities_1.getBranchFromAlias)(targetStack, this.config.targetStackBranchAlias);
cli_utilities_1.log.debug('Target branch alias resolved', Object.assign(Object.assign({}, this.config.cloneContext), { alias: this.config.targetStackBranchAlias, branch: this.config.targetStackBranch }));
}
}
catch (error) {
throw error;
}
}
async handleBranchSelection(options = {}) {
const { api_key, isSource = true, returnBranch = false } = options;
return new Promise(async (resolve, reject) => {
let spinner;
try {
cli_utilities_1.log.debug('Handling branch selection', Object.assign(Object.assign({}, this.config.cloneContext), { isSource, returnBranch, stackApiKey: isSource ? this.config.source_stack : this.config.target_stack }));
const stackAPIClient = this.client.stack({
api_key: isSource ? this.config.source_stack : this.config.target_stack,
management_token: this.config.management_token,
});
// NOTE validate if source branch is exist
if (isSource && this.config.sourceStackBranch) {
cli_utilities_1.log.debug('Validating source branch exists', Object.assign(Object.assign({}, this.config.cloneContext), { branch: this.config.sourceStackBranch }));
await this.validateIfBranchExist(stackAPIClient, true);
return resolve(undefined);
}
else if (isSource && this.config.sourceStackBranchAlias) {
cli_utilities_1.log.debug('Resolving source branch alias', Object.assign(Object.assign({}, this.config.cloneContext), { alias: this.config.sourceStackBranchAlias }));
await this.resolveBranchAliases(true);
return resolve(undefined);
}
// NOTE Validate target branch is exist
if (!isSource && this.config.targetStackBranch) {
cli_utilities_1.log.debug('Validating target branch exists', Object.assign(Object.assign({}, this.config.cloneContext), { branch: this.config.targetStackBranch }));
await this.validateIfBranchExist(stackAPIClient, false);
return resolve(undefined);
}
else if (!isSource && this.config.targetStackBranchAlias) {
cli_utilities_1.log.debug('Resolving target branch alias', Object.assign(Object.assign({}, this.config.cloneContext), { alias: this.config.targetStackBranchAlias }));
await this.resolveBranchAliases();
return resolve(undefined);
}
spinner = (0, ora_1.default)('Fetching Branches').start();
cli_utilities_1.log.debug(`Querying branches for stack: ${isSource ? this.config.source_stack : this.config.target_stack}`, this.config.cloneContext);
const result = await stackAPIClient
.branch()
.query()
.find()
.then(({ items }) => items)
.catch((_err) => { });
const condition = result && Array.isArray(result) && result.length > 0;
cli_utilities_1.log.debug(`Found ${(result === null || result === void 0 ? void 0 : result.length) || 0} branch(es)`, this.config.cloneContext);
// NOTE if want to get only list of branches (Pass param -> returnBranch = true )
if (returnBranch) {
resolve(condition ? result : []);
}
else {
if (condition) {
spinner.succeed('Fetched Branches');
const { branch } = await inquirer_1.default.prompt({
type: 'list',
name: 'branch',
message: 'Choose a branch',
choices: result.map((row) => row.uid),
});
if (this.executingCommand != 2) {
return reject();
}
if (isSource) {
this.config.sourceStackBranch = branch;
cli_utilities_1.log.debug(`Source branch selected: ${branch}`, this.config.cloneContext);
}
else {
this.config.targetStackBranch = branch;
cli_utilities_1.log.debug(`Target branch selected: ${branch}`, this.config.cloneContext);
}
}
else {
spinner.succeed('No branches found.!');
}
resolve(undefined);
}
}
catch (e) {
if (spinner)
spinner.fail();
return reject(e);
}
});
}
async executeStackPrompt(params = {}) {
try {
this.setExectingCommand(1);
const sourceStack = await this.cloneCommand.execute((0, command_helpers_1.HandleStackCommand)(params, this));
if (this.config.source_stack) {
await this.executeBranchPrompt(params);
}
// Update stackName default dynamically
this.stackNamePrompt.default = this.config.stackName || `Copy of ${sourceStack.stack || this.config.source_alias || 'ABC'}`;
}
catch (error) {
throw error;
}
}
async executeBranchPrompt(parentParams) {
try {
this.setExectingCommand(2);
await this.cloneCommand.execute((0, command_helpers_1.HandleBranchCommand)({ api_key: this.config.source_stack }, this, this.executeStackPrompt.bind(this, parentParams)));
await this.executeExport();
}
catch (error) {
throw error;
}
}
async executeExport() {
try {
cli_utilities_1.log.debug('Executing export operation', this.config.cloneContext);
const exportRes = await this.cloneCommand.execute((0, command_helpers_1.HandleExportCommand)(null, this));
await this.cloneCommand.execute((0, command_helpers_1.SetBranchCommand)(null, this));
if (exportRes) {
cli_utilities_1.log.debug('Export operation completed, proceeding with destination', this.config.cloneContext);
this.executeDestination().catch(() => {
throw '';
});
}
}
catch (error) {
throw error;
}
finally {
this.removeBackKeyPressHandler();
}
}
async execute() {
return new Promise(async (resolve, reject) => {
let keyPressHandler;
try {
cli_utilities_1.log.debug('Starting clone execution', Object.assign(Object.assign({}, this.config.cloneContext), { sourceStack: this.config.source_stack, targetStack: this.config.target_stack }));
if (!this.config.source_stack) {
const orgMsg = 'Choose an organization where your source stack exists:';
cli_utilities_1.log.debug('Source stack not provided, prompting for organization', this.config.cloneContext);
this.setExectingCommand(0);
this.removeBackKeyPressHandler();
const org = await this.cloneCommand.execute((0, command_helpers_1.HandleOrgCommand)({ msg: orgMsg, isSource: true }, this));
const self = this;
if (org) {
keyPressHandler = async function (_ch, key) {
// executingCommand is a tracking property to determine which method invoked this key press.
if (key.name === 'left' && key.shift) {
if (self.executingCommand === 1) {
self.setExectingCommand(3);
}
else if (self.executingCommand === 2) {
self.setExectingCommand(4);
}
self.config.source_stack = undefined;
self.config.sourceStackBranch = undefined;
if (self.executingCommand != 0) {
console.clear();
await self.cloneCommand.undo();
}
}
};
process.stdin.addListener('keypress', keyPressHandler);
this.setBackKeyPressHandler(keyPressHandler);
await this.executeStackPrompt({ org, isSource: true, msg: 'Select the source stack' });
}
else {
return reject('Org not found.');
}
}
else {
cli_utilities_1.log.debug('Source stack provided, proceeding with branch selection and export', this.config.cloneContext);
this.setExectingCommand(2);
await this.handleBranchSelection({ api_key: this.config.source_stack });
cli_utilities_1.log.debug('Starting export operation', this.config.cloneContext);
const exportRes = await this.cloneCommand.execute((0, command_helpers_1.HandleExportCommand)(null, this));
await this.cloneCommand.execute((0, command_helpers_1.SetBranchCommand)(null, this));
if (exportRes) {
cli_utilities_1.log.debug('Export completed, proceeding with destination setup', this.config.cloneContext);
this.executeDestination().catch((error) => {
return reject(error);
});
}
}
cli_utilities_1.log.debug('Clone execution completed successfully', this.config.cloneContext);
return resolve();
}
catch (error) {
return reject(error);
}
});
}
async executeDestination() {
return new Promise(async (resolve, reject) => {
let keyPressHandler;
try {
cli_utilities_1.log.debug('Executing destination setup', this.config.cloneContext);
let canCreateStack = false;
if (!this.config.target_stack) {
cli_utilities_1.log.debug('Target stack not provided, prompting for stack creation', this.config.cloneContext);
canCreateStack = await inquirer_1.default.prompt(constants_1.STACK_CREATION_CONFIRMATION);
}
this.setExectingCommand(0);
this.removeBackKeyPressHandler();
const orgMsgExistingStack = 'Choose an organization where the destination stack exists: ';
const orgMsgNewStack = 'Choose an organization where you want to create a stack: ';
let org;
if (!this.config.target_stack) {
org = await this.cloneCommand.execute((0, command_helpers_1.HandleOrgCommand)({
msg: !canCreateStack.stackCreate ? orgMsgExistingStack : orgMsgNewStack,
isSource: false,
}, this));
}
const params = { org, canCreateStack };
if (!this.config.target_stack) {
const self = this;
keyPressHandler = async function (_ch, key) {
if (key.name === 'left' && key.shift) {
if (self.executingCommand === 1) {
self.setExectingCommand(3);
}
else if (self.executingCommand === 2) {
self.setExectingCommand(4);
}
if (self.createNewStackPrompt) {
self.createNewStackPrompt.stop();
}
self.config.target_stack = undefined;
self.config.targetStackBranch = undefined;
if (self.executingCommand != 0) {
console.clear();
await self.cloneCommand.undo();
}
}
};
process.stdin.addListener('keypress', keyPressHandler);
this.setBackKeyPressHandler(keyPressHandler);
await this.executeStackDestinationPrompt(params);
}
else {
await this.executeBranchDestinationPrompt(params);
}
cli_utilities_1.log.debug('Destination setup completed successfully', this.config.cloneContext);
return resolve();
}
catch (error) {
reject(error);
}
});
}
async executeStackDestinationPrompt(params) {
try {
this.setExectingCommand(1);
const { org, canCreateStack } = params;
if (!canCreateStack.stackCreate) {
const stackMsg = 'Choose the destination stack:';
await this.cloneCommand.execute((0, command_helpers_1.HandleDestinationStackCommand)({ org, msg: stackMsg, isSource: false }, this));
await this.executeBranchDestinationPrompt(params);
}
else {
const orgUid = this.orgUidList[org.Organization];
await this.cloneCommand.execute((0, command_helpers_1.CreateNewStackCommand)({ orgUid }, this));
this.removeBackKeyPressHandler();
await this.cloneCommand.execute((0, command_helpers_1.CloneTypeSelectionCommand)(null, this));
}
}
catch (error) {
throw error;
}
}
async executeBranchDestinationPrompt(parentParams) {
try {
this.setExectingCommand(2);
await this.cloneCommand.execute((0, command_helpers_1.HandleBranchCommand)({ isSource: false, api_key: this.config.target_stack }, this, this.executeStackDestinationPrompt.bind(this, parentParams)));
this.removeBackKeyPressHandler();
await this.cloneCommand.execute((0, command_helpers_1.CloneTypeSelectionCommand)(null, this));
}
catch (error) {
throw error;
}
}
async cmdExport() {
return new Promise((resolve, reject) => {
var _a;
cli_utilities_1.log.debug('Preparing export command', Object.assign(Object.assign({}, this.config.cloneContext), { sourceStack: this.config.source_stack, cloneType: this.config.cloneType }));
// Creating export specific config by merging external configurations
let exportConfig = Object.assign({}, (0, cloneDeep_1.default)(this.config), Object.assign({}, (_a = this.config) === null || _a === void 0 ? void 0 : _a.export));
delete exportConfig.import;
delete exportConfig.export;
// Resolve path to package root - go up 3 levels from __dirname (core/util -> package root)
const packageRoot = path.resolve(__dirname, '../../..');
const exportDir = path.join(packageRoot, 'contents');
cli_utilities_1.log.debug(`Export directory: ${exportDir}`, this.config.cloneContext);
const cmd = ['-k', exportConfig.source_stack, '-d', exportDir];
if (exportConfig.cloneType === 'a') {
exportConfig.filteredModules = ['stack'].concat(constants_1.STRUCTURE_LIST);
cli_utilities_1.log.debug(`Filtered modules for structure-only export: ${exportConfig.filteredModules.join(', ')}`, this.config.cloneContext);
}
if (exportConfig.source_alias) {
cmd.push('-a', exportConfig.source_alias);
cli_utilities_1.log.debug(`Using source alias: ${exportConfig.source_alias}`, this.config.cloneContext);
}
if (exportConfig.sourceStackBranch) {
cmd.push('--branch', exportConfig.sourceStackBranch);
cli_utilities_1.log.debug(`Using source branch: ${exportConfig.sourceStackBranch}`, this.config.cloneContext);
}
if (exportConfig.forceStopMarketplaceAppsPrompt) {
cmd.push('-y');
cli_utilities_1.log.debug('Force stop marketplace apps prompt enabled', this.config.cloneContext);
}
// dummyConfig.json is in the same directory as this file
const configFilePath = path.join(__dirname, 'dummyConfig.json');
cmd.push('-c');
cmd.push(configFilePath);
cli_utilities_1.log.debug(`Writing export config to: ${configFilePath}`, this.config.cloneContext);
fs.writeFileSync(configFilePath, JSON.stringify(exportConfig));
cli_utilities_1.log.debug('Export command prepared', Object.assign(Object.assign({}, this.config.cloneContext), { cmd: cmd.join(' '), exportDir, sourceStack: exportConfig.source_stack, branch: exportConfig.sourceStackBranch }));
cli_utilities_1.log.debug('Running export command', Object.assign(Object.assign({}, this.config.cloneContext), { cmd }));
const exportData = cli_cm_export_1.default.run(cmd);
exportData.then(() => {
cli_utilities_1.log.debug('Export command completed successfully', this.config.cloneContext);
resolve(true);
}).catch((error) => {
reject(error);
});
});
}
async cmdImport() {
return new Promise(async (resolve, _reject) => {
var _a;
cli_utilities_1.log.debug('Preparing import command', Object.assign(Object.assign({}, this.config.cloneContext), { targetStack: this.config.target_stack, targetBranch: this.config.targetStackBranch }));
// Creating export specific config by merging external configurations
let importConfig = Object.assign({}, (0, cloneDeep_1.default)(this.config), Object.assign({}, (_a = this.config) === null || _a === void 0 ? void 0 : _a.import));
delete importConfig.import;
delete importConfig.export;
// dummyConfig.json is in the same directory as this file
const configFilePath = path.join(__dirname, 'dummyConfig.json');
const cmd = ['-c', configFilePath];
if (importConfig.destination_alias) {
cmd.push('-a', importConfig.destination_alias);
cli_utilities_1.log.debug(`Using destination alias: ${importConfig.destination_alias}`, this.config.cloneContext);
}
if (!importConfig.data && importConfig.sourceStackBranch && importConfig.pathDir) {
const dataPath = path.join(importConfig.pathDir, importConfig.sourceStackBranch);
cmd.push('-d', dataPath);
cli_utilities_1.log.debug(`Import data path: ${dataPath}`, this.config.cloneContext);
}
if (importConfig.targetStackBranch) {
cmd.push('--branch', importConfig.targetStackBranch);
cli_utilities_1.log.debug(`Using target branch: ${importConfig.targetStackBranch}`, this.config.cloneContext);
}
if (importConfig.importWebhookStatus) {
cmd.push('--import-webhook-status', importConfig.importWebhookStatus);
cli_utilities_1.log.debug(`Import webhook status: ${importConfig.importWebhookStatus}`, this.config.cloneContext);
}
if (importConfig.skipAudit) {
cmd.push('--skip-audit');
cli_utilities_1.log.debug('Skip audit flag enabled', this.config.cloneContext);
}
if (importConfig.forceStopMarketplaceAppsPrompt) {
cmd.push('-y');
cli_utilities_1.log.debug('Force stop marketplace apps prompt enabled', this.config.cloneContext);
}
cli_utilities_1.log.debug(`Writing import config to: ${configFilePath}`, this.config.cloneContext);
fs.writeFileSync(configFilePath, JSON.stringify(importConfig));
cli_utilities_1.log.debug('Import command prepared', Object.assign(Object.assign({}, this.config.cloneContext), { cmd: cmd.join(' '), targetStack: importConfig.target_stack, targetBranch: importConfig.targetStackBranch, dataPath: importConfig.data || (importConfig.pathDir && importConfig.sourceStackBranch ? path.join(importConfig.pathDir, importConfig.sourceStackBranch) : undefined) }));
cli_utilities_1.log.debug('Running import command', Object.assign(Object.assign({}, this.config.cloneContext), { cmd }));
const importData = cli_cm_import_1.default.run(cmd);
importData.then(() => {
cli_utilities_1.log.debug('Import command completed successfully', this.config.cloneContext);
cli_utilities_1.log.debug('Clearing import config file', this.config.cloneContext);
fs.writeFileSync(configFilePath, JSON.stringify({}));
resolve();
}).catch((error) => {
cli_utilities_1.log.error('Import command failed', Object.assign(Object.assign({}, this.config.cloneContext), { error }));
throw error;
});
});
}
setCreateNewStackPrompt(createNewStackPrompt) {
this.createNewStackPrompt = createNewStackPrompt;
}
async setBranch() {
if (!this.config.sourceStackBranch) {
try {
const branches = await this.client
.stack({ api_key: this.config.source_stack })
.branch()
.query()
.find()
.catch((_err) => { });
if (branches && branches.items && branches.items.length) {
this.config.sourceStackBranch = 'main';
}
}
catch (_error) {
// Ignore error
}
}
}
getNewStackPromptResult() {
return new Promise((resolve) => {
prompt.get({
properties: {
name: { description: safe_1.default.white(this.stackNamePrompt.message), default: safe_1.default.grey(this.stackNamePrompt.default) },
},
}, function (_, result) {
if (prompt.stopped) {
prompt.stopped = false;
resolve(undefined);
}
else {
let _name = result.name.replace(/\[\d+m/g, '');
_name = _name.replace(//g, '');
resolve({ stack: _name });
}
});
});
}
async createNewStack(options) {
return new Promise(async (resolve, reject) => {
try {
const { orgUid } = options;
cli_utilities_1.log.debug('Creating new stack', Object.assign(Object.assign({}, this.config.cloneContext), { orgUid, masterLocale: this.master_locale, stackName: this.config.stackName }));
this.displayBackOptionMessage();
let inputvalue;
if (!this.config.stackName) {
cli_utilities_1.log.debug('Stack name not provided, prompting user', this.config.cloneContext);
prompt.start();
prompt.message = '';
this.setCreateNewStackPrompt(prompt);
inputvalue = await this.getNewStackPromptResult();
this.setCreateNewStackPrompt(null);
}
else {
inputvalue = { stack: this.config.stackName };
}
if (this.executingCommand === 0 || !inputvalue) {
cli_utilities_1.log.debug('Stack creation cancelled or invalid input', this.config.cloneContext);
return reject();
}
let stack = { name: inputvalue.stack, master_locale: this.master_locale };
cli_utilities_1.log.debug('Creating stack with configuration', this.config.cloneContext);
const spinner = (0, ora_1.default)('Creating New stack').start();
cli_utilities_1.log.debug('Sending stack creation API request', this.config.cloneContext);
const newStack = this.client.stack().create({ stack }, { organization_uid: orgUid });
newStack
.then((result) => {
cli_utilities_1.log.debug('Stack created successfully', Object.assign(Object.assign({}, this.config.cloneContext), { stackName: result.name }));
spinner.succeed('New Stack created Successfully name as ' + result.name);
this.config.target_stack = result.api_key;
this.config.destinationStackName = result.name;
cli_utilities_1.log.debug('Target stack configuration updated', this.config.cloneContext);
return resolve(result);
})
.catch((error) => {
spinner.fail();
return reject(error.errorMessage + ' Contact the Organization owner for Stack Creation access.');
});
}
catch (error) {
return reject(error);
}
});
}
async cloneTypeSelection() {
console.clear();
return new Promise(async (resolve, reject) => {
try {
cli_utilities_1.log.debug('Starting clone type selection', this.config.cloneContext);
const choices = [
'Structure (all modules except entries & assets)',
'Structure with content (all modules including entries & assets)',
];
const cloneTypeSelection = [
{
choices,
type: 'list',
name: 'type',
message: 'Choose the type of data to clone:',
},
];
let successMsg;
let selectedValue = {};
// Resolve path to package root - go up 3 levels from __dirname (core/util -> package root)
const cloneTypePackageRoot = path.resolve(__dirname, '../../..');
this.config.data = path.join(cloneTypePackageRoot, 'contents', this.config.sourceStackBranch || '');
cli_utilities_1.log.debug(`Clone data directory: ${this.config.data}`, this.config.cloneContext);
if (!this.config.cloneType) {
cli_utilities_1.log.debug('Clone type not specified, prompting user for selection', this.config.cloneContext);
selectedValue = await inquirer_1.default.prompt(cloneTypeSelection);
}
else {
cli_utilities_1.log.debug(`Using pre-configured clone type: ${this.config.cloneType}`, this.config.cloneContext);
}
if (this.config.cloneType === 'a' || selectedValue.type === 'Structure (all modules except entries & assets)') {
this.config.modules = constants_1.STRUCTURE_LIST;
successMsg = 'Stack clone Structure completed';
cli_utilities_1.log.debug(`Clone type: Structure only. Modules to clone: ${constants_1.STRUCTURE_LIST.join(', ')}`, this.config.cloneContext);
}
else {
successMsg = 'Stack clone completed with structure and content';
cli_utilities_1.log.debug('Clone type: Structure with content (all modules)', this.config.cloneContext);
}
this.cmdImport()
.then(async () => {
cli_utilities_1.log.debug('Clone type selection and import completed successfully', this.config.cloneContext);
// Clean up contents directory after import completes
if (this.config.pathDir) {
const resolvedPath = path.resolve(this.config.pathDir);
cli_utilities_1.log.debug('Cleaning up contents directory after import', Object.assign(Object.assign({}, this.config.cloneContext), { pathDir: this.config.pathDir, resolvedPath }));
try {
await (0, rimraf_1.rimraf)(resolvedPath);
cli_utilities_1.log.debug('Contents directory cleaned up successfully', Object.assign(Object.assign({}, this.config.cloneContext), { pathDir: this.config.pathDir, resolvedPath }));
}
catch (cleanupError) {
cli_utilities_1.log.debug('Cleanup error (non-fatal)', Object.assign(Object.assign({}, this.config.cloneContext), { pathDir: this.config.pathDir, resolvedPath, error: cleanupError === null || cleanupError === void 0 ? void 0 : cleanupError.message, code: cleanupError === null || cleanupError === void 0 ? void 0 : cleanupError.code }));
// Don't fail the clone if cleanup fails
}
}
else {
cli_utilities_1.log.debug('No pathDir configured, skipping cleanup', this.config.cloneContext);
}
resolve(successMsg);
})
.catch(reject);
}
catch (error) {
reject(error);
}
});
}
}
exports.CloneHandler = CloneHandler;