@contentstack/cli-cm-clone 1.18.1 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/bin/run.cmd +3 -0
- package/bin/run.js +7 -0
- package/package.json +28 -14
- package/src/commands/cm/stacks/clone.js +0 -357
- package/src/lib/helpers/command-helpers.js +0 -67
- package/src/lib/util/abort-controller.js +0 -49
- package/src/lib/util/clone-handler.js +0 -815
- package/src/lib/util/dummyConfig.json +0 -1
|
@@ -1,815 +0,0 @@
|
|
|
1
|
-
const ora = require('ora');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const inquirer = require('inquirer');
|
|
4
|
-
const chalk = require('chalk');
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
let { default: exportCmd } = require('@contentstack/cli-cm-export');
|
|
7
|
-
let { default: importCmd } = require('@contentstack/cli-cm-import');
|
|
8
|
-
const { CustomAbortController } = require('./abort-controller');
|
|
9
|
-
const prompt = require('prompt');
|
|
10
|
-
const colors = require('@colors/colors/safe');
|
|
11
|
-
const cloneDeep = require('lodash/cloneDeep');
|
|
12
|
-
|
|
13
|
-
const {
|
|
14
|
-
HandleOrgCommand,
|
|
15
|
-
HandleStackCommand,
|
|
16
|
-
HandleDestinationStackCommand,
|
|
17
|
-
HandleExportCommand,
|
|
18
|
-
SetBranchCommand,
|
|
19
|
-
CreateNewStackCommand,
|
|
20
|
-
CloneTypeSelectionCommand,
|
|
21
|
-
Clone,
|
|
22
|
-
HandleBranchCommand,
|
|
23
|
-
} = require('../helpers/command-helpers');
|
|
24
|
-
const { configHandler, getBranchFromAlias, log } = require('@contentstack/cli-utilities');
|
|
25
|
-
|
|
26
|
-
let client = {};
|
|
27
|
-
let config;
|
|
28
|
-
let cloneCommand;
|
|
29
|
-
|
|
30
|
-
let stackCreationConfirmation = [
|
|
31
|
-
{
|
|
32
|
-
type: 'confirm',
|
|
33
|
-
name: 'stackCreate',
|
|
34
|
-
message: 'Want to clone content into a new stack ?',
|
|
35
|
-
initial: true,
|
|
36
|
-
},
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
let stackName = {
|
|
40
|
-
type: 'input',
|
|
41
|
-
name: 'stack',
|
|
42
|
-
default: 'ABC',
|
|
43
|
-
message: 'Enter name for the new stack to store the cloned content ?',
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
let orgUidList = {};
|
|
47
|
-
let stackUidList = {};
|
|
48
|
-
let masterLocaleList = {};
|
|
49
|
-
|
|
50
|
-
let structureList = [
|
|
51
|
-
'locales',
|
|
52
|
-
'environments',
|
|
53
|
-
'extensions',
|
|
54
|
-
'marketplace-apps',
|
|
55
|
-
'webhooks',
|
|
56
|
-
'global-fields',
|
|
57
|
-
'content-types',
|
|
58
|
-
'workflows',
|
|
59
|
-
'labels',
|
|
60
|
-
];
|
|
61
|
-
let master_locale;
|
|
62
|
-
|
|
63
|
-
// Overrides prompt's stop method
|
|
64
|
-
prompt.stop = function () {
|
|
65
|
-
if (prompt.stopped) {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
prompt.emit('stop');
|
|
69
|
-
prompt.stopped = true;
|
|
70
|
-
return prompt;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
class CloneHandler {
|
|
74
|
-
constructor(opt) {
|
|
75
|
-
config = opt;
|
|
76
|
-
cloneCommand = new Clone();
|
|
77
|
-
this.pathDir = opt.pathDir;
|
|
78
|
-
process.stdin.setMaxListeners(50);
|
|
79
|
-
log.debug('Initializing CloneHandler', config.cloneContext, { pathDir: opt.pathDir, cloneType: opt.cloneType });
|
|
80
|
-
}
|
|
81
|
-
setClient(managementSDKClient) {
|
|
82
|
-
client = managementSDKClient;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
handleOrgSelection(options = {}) {
|
|
86
|
-
return new Promise(async (resolve, reject) => {
|
|
87
|
-
const { msg = '', isSource = true } = options || {};
|
|
88
|
-
log.debug('Handling organization selection', config.cloneContext);
|
|
89
|
-
const orgList = await this.getOrganizationChoices(msg).catch(reject);
|
|
90
|
-
|
|
91
|
-
if (orgList) {
|
|
92
|
-
log.debug(`Found ${orgList.choices?.length || 0} organization(s) to choose from`, config.cloneContext);
|
|
93
|
-
const orgSelected = await inquirer.prompt(orgList);
|
|
94
|
-
log.debug(`Organization selected: ${orgSelected.Organization}`, config.cloneContext);
|
|
95
|
-
|
|
96
|
-
if (isSource) {
|
|
97
|
-
config.sourceOrg = orgUidList[orgSelected.Organization];
|
|
98
|
-
log.debug(`Source organization UID: ${config.sourceOrg}`, config.cloneContext);
|
|
99
|
-
} else {
|
|
100
|
-
config.targetOrg = orgUidList[orgSelected.Organization];
|
|
101
|
-
log.debug(`Target organization UID: ${config.targetOrg}`, config.cloneContext);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
resolve(orgSelected);
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
handleStackSelection(options = {}) {
|
|
110
|
-
return new Promise(async (resolve, reject) => {
|
|
111
|
-
try {
|
|
112
|
-
const { org = {}, msg = '', isSource = true } = options || {};
|
|
113
|
-
log.debug('Handling stack selection', config.cloneContext, { isSource, orgName: org.Organization, msg });
|
|
114
|
-
|
|
115
|
-
const stackList = await this.getStack(org, msg, isSource).catch(reject);
|
|
116
|
-
|
|
117
|
-
if (stackList) {
|
|
118
|
-
this.displayBackOptionMessage();
|
|
119
|
-
|
|
120
|
-
log.debug(`Found ${stackList.choices?.length || 0} stack(s) to choose from`, config.cloneContext);
|
|
121
|
-
const selectedStack = await inquirer.prompt(stackList);
|
|
122
|
-
log.debug(`Stack selected: ${selectedStack.stack}`, config.cloneContext);
|
|
123
|
-
if (this.executingCommand != 1) {
|
|
124
|
-
return reject();
|
|
125
|
-
}
|
|
126
|
-
if (isSource) {
|
|
127
|
-
config.sourceStackName = selectedStack.stack;
|
|
128
|
-
master_locale = masterLocaleList[selectedStack.stack];
|
|
129
|
-
config.source_stack = stackUidList[selectedStack.stack];
|
|
130
|
-
log.debug(`Source stack configured`, config.cloneContext);
|
|
131
|
-
} else {
|
|
132
|
-
config.target_stack = stackUidList[selectedStack.stack];
|
|
133
|
-
config.destinationStackName = selectedStack.stack;
|
|
134
|
-
log.debug(`Target stack configured`, config.cloneContext);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
resolve(selectedStack);
|
|
138
|
-
}
|
|
139
|
-
} catch (error) {
|
|
140
|
-
return reject(error);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
handleBranchSelection = async (options) => {
|
|
146
|
-
const { api_key, isSource = true, returnBranch = false } = options;
|
|
147
|
-
return new Promise(async (resolve, reject) => {
|
|
148
|
-
let spinner;
|
|
149
|
-
try {
|
|
150
|
-
log.debug('Handling branch selection', config.cloneContext, { isSource, returnBranch, stackApiKey: isSource ? config.source_stack : config.target_stack });
|
|
151
|
-
const stackAPIClient = client.stack({
|
|
152
|
-
api_key: isSource ? config.source_stack : config.target_stack,
|
|
153
|
-
management_token: config.management_token,
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// NOTE validate if source branch is exist
|
|
157
|
-
if (isSource && config.sourceStackBranch) {
|
|
158
|
-
log.debug('Validating source branch exists', { ...config.cloneContext, branch: config.sourceStackBranch });
|
|
159
|
-
await this.validateIfBranchExist(stackAPIClient, true);
|
|
160
|
-
return resolve();
|
|
161
|
-
} else if(isSource && config.sourceStackBranchAlias) {
|
|
162
|
-
log.debug('Resolving source branch alias', { ...config.cloneContext, alias: config.sourceStackBranchAlias });
|
|
163
|
-
await this.resolveBranchAliases(true);
|
|
164
|
-
return resolve();
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// NOTE Validate target branch is exist
|
|
168
|
-
if (!isSource && config.targetStackBranch) {
|
|
169
|
-
log.debug('Validating target branch exists', { ...config.cloneContext, branch: config.targetStackBranch });
|
|
170
|
-
await this.validateIfBranchExist(stackAPIClient, false);
|
|
171
|
-
return resolve();
|
|
172
|
-
} else if (!isSource && config.targetStackBranchAlias) {
|
|
173
|
-
log.debug('Resolving target branch alias', { ...config.cloneContext, alias: config.targetStackBranchAlias });
|
|
174
|
-
await this.resolveBranchAliases();
|
|
175
|
-
return resolve();
|
|
176
|
-
}
|
|
177
|
-
spinner = ora('Fetching Branches').start();
|
|
178
|
-
log.debug(`Querying branches for stack: ${isSource ? config.source_stack : config.target_stack}`, config.cloneContext);
|
|
179
|
-
const result = await stackAPIClient
|
|
180
|
-
.branch()
|
|
181
|
-
.query()
|
|
182
|
-
.find()
|
|
183
|
-
.then(({ items }) => items)
|
|
184
|
-
.catch((_err) => {});
|
|
185
|
-
|
|
186
|
-
const condition = result && Array.isArray(result) && result.length > 0;
|
|
187
|
-
log.debug(`Found ${result?.length || 0} branch(es)`, config.cloneContext);
|
|
188
|
-
|
|
189
|
-
// NOTE if want to get only list of branches (Pass param -> returnBranch = true )
|
|
190
|
-
if (returnBranch) {
|
|
191
|
-
resolve(condition ? result : []);
|
|
192
|
-
} else {
|
|
193
|
-
if (condition) {
|
|
194
|
-
spinner.succeed('Fetched Branches');
|
|
195
|
-
const { branch } = await inquirer.prompt({
|
|
196
|
-
type: 'list',
|
|
197
|
-
name: 'branch',
|
|
198
|
-
message: 'Choose a branch',
|
|
199
|
-
choices: result.map((row) => row.uid),
|
|
200
|
-
});
|
|
201
|
-
if (this.executingCommand != 2) {
|
|
202
|
-
return reject();
|
|
203
|
-
}
|
|
204
|
-
if (isSource) {
|
|
205
|
-
config.sourceStackBranch = branch;
|
|
206
|
-
log.debug(`Source branch selected: ${branch}`, config.cloneContext);
|
|
207
|
-
} else {
|
|
208
|
-
config.targetStackBranch = branch;
|
|
209
|
-
log.debug(`Target branch selected: ${branch}`, config.cloneContext);
|
|
210
|
-
}
|
|
211
|
-
} else {
|
|
212
|
-
spinner.succeed('No branches found.!');
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
resolve();
|
|
216
|
-
}
|
|
217
|
-
} catch (e) {
|
|
218
|
-
if (spinner) spinner.fail();
|
|
219
|
-
return reject(e);
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
async validateIfBranchExist(stackAPIClient, isSource) {
|
|
225
|
-
let spinner;
|
|
226
|
-
const completeSpinner = (msg, method = 'succeed') => {
|
|
227
|
-
spinner[method](msg);
|
|
228
|
-
spinner.stop();
|
|
229
|
-
};
|
|
230
|
-
try {
|
|
231
|
-
const branch = isSource ? config.sourceStackBranch : config.targetStackBranch;
|
|
232
|
-
log.debug('Validating branch existence', config.cloneContext);
|
|
233
|
-
spinner = ora(`Validation if ${isSource ? 'source' : 'target'} branch exist.!`).start();
|
|
234
|
-
const isBranchExist = await stackAPIClient
|
|
235
|
-
.branch(branch)
|
|
236
|
-
.fetch()
|
|
237
|
-
.then((data) => data);
|
|
238
|
-
|
|
239
|
-
if (isBranchExist && typeof isBranchExist === 'object') {
|
|
240
|
-
log.debug('Branch validation successful', config.cloneContext);
|
|
241
|
-
completeSpinner(`${isSource ? 'Source' : 'Target'} branch verified.!`);
|
|
242
|
-
} else {
|
|
243
|
-
log.error('Branch not found', config.cloneContext);
|
|
244
|
-
completeSpinner(`${isSource ? 'Source' : 'Target'} branch not found.!`, 'fail');
|
|
245
|
-
process.exit();
|
|
246
|
-
}
|
|
247
|
-
} catch (e) {
|
|
248
|
-
completeSpinner(`${isSource ? 'Source' : 'Target'} branch not found.!`, 'fail');
|
|
249
|
-
throw e;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
displayBackOptionMessage() {
|
|
253
|
-
const ui = new inquirer.ui.BottomBar();
|
|
254
|
-
ui.updateBottomBar(chalk.cyan('\nPress shift & left arrow together to undo the operation\n'));
|
|
255
|
-
}
|
|
256
|
-
setBackKeyPressHandler(backKeyPressHandler) {
|
|
257
|
-
this.backKeyPressHandler = backKeyPressHandler;
|
|
258
|
-
}
|
|
259
|
-
removeBackKeyPressHandler() {
|
|
260
|
-
if (this.backKeyPressHandler) {
|
|
261
|
-
process.stdin.removeListener('keypress', this.backKeyPressHandler);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
setExectingCommand(command) {
|
|
265
|
-
// 0 for org, 1 for stack, 1 for branch, 3 stack cancelled, 4 branch cancelled
|
|
266
|
-
this.executingCommand = command;
|
|
267
|
-
}
|
|
268
|
-
execute() {
|
|
269
|
-
return new Promise(async (resolve, reject) => {
|
|
270
|
-
let keyPressHandler;
|
|
271
|
-
try {
|
|
272
|
-
log.debug('Starting clone execution', { ...config.cloneContext, sourceStack: config.source_stack, targetStack: config.target_stack });
|
|
273
|
-
if (!config.source_stack) {
|
|
274
|
-
const orgMsg = 'Choose an organization where your source stack exists:';
|
|
275
|
-
log.debug('Source stack not provided, prompting for organization', config.cloneContext);
|
|
276
|
-
this.setExectingCommand(0);
|
|
277
|
-
this.removeBackKeyPressHandler();
|
|
278
|
-
const org = await cloneCommand.execute(new HandleOrgCommand({ msg: orgMsg, isSource: true }, this));
|
|
279
|
-
let self = this;
|
|
280
|
-
if (org) {
|
|
281
|
-
keyPressHandler = async function (_ch, key) {
|
|
282
|
-
// executingCommand is a tracking property to determine which method invoked this key press.
|
|
283
|
-
if (key.name === 'left' && key.shift) {
|
|
284
|
-
if (self.executingCommand === 1) {
|
|
285
|
-
self.setExectingCommand(3);
|
|
286
|
-
} else if (self.executingCommand === 2) {
|
|
287
|
-
self.setExectingCommand(4);
|
|
288
|
-
}
|
|
289
|
-
config.source_stack = null;
|
|
290
|
-
config.sourceStackBranch = null;
|
|
291
|
-
if (self.executingCommand != 0) {
|
|
292
|
-
console.clear();
|
|
293
|
-
await cloneCommand.undo();
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
};
|
|
297
|
-
process.stdin.addListener('keypress', keyPressHandler);
|
|
298
|
-
this.setBackKeyPressHandler(keyPressHandler);
|
|
299
|
-
|
|
300
|
-
await this.executeStackPrompt({ org, isSource: true, msg: 'Select the source stack' });
|
|
301
|
-
} else {
|
|
302
|
-
return reject('Org not found.');
|
|
303
|
-
}
|
|
304
|
-
} else {
|
|
305
|
-
log.debug('Source stack provided, proceeding with branch selection and export', config.cloneContext);
|
|
306
|
-
this.setExectingCommand(2);
|
|
307
|
-
await this.handleBranchSelection({ api_key: config.sourceStack });
|
|
308
|
-
log.debug('Starting export operation', config.cloneContext);
|
|
309
|
-
const exportRes = await cloneCommand.execute(new HandleExportCommand(null, this));
|
|
310
|
-
await cloneCommand.execute(new SetBranchCommand(null, this));
|
|
311
|
-
|
|
312
|
-
if (exportRes) {
|
|
313
|
-
log.debug('Export completed, proceeding with destination setup', config.cloneContext);
|
|
314
|
-
this.executeDestination().catch((error) => {
|
|
315
|
-
return reject(error);
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
log.debug('Clone execution completed successfully', config.cloneContext);
|
|
320
|
-
return resolve();
|
|
321
|
-
} catch (error) {
|
|
322
|
-
return reject(error);
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
async executeStackPrompt(params = {}) {
|
|
328
|
-
try {
|
|
329
|
-
this.setExectingCommand(1);
|
|
330
|
-
const sourceStack = await cloneCommand.execute(new HandleStackCommand(params, this));
|
|
331
|
-
if (config.source_stack) {
|
|
332
|
-
await this.executeBranchPrompt(params);
|
|
333
|
-
}
|
|
334
|
-
stackName.default = config.stackName || `Copy of ${sourceStack.stack || config.source_alias}`;
|
|
335
|
-
} catch (error) {
|
|
336
|
-
throw error;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
async executeBranchPrompt(parentParams) {
|
|
341
|
-
try {
|
|
342
|
-
this.setExectingCommand(2);
|
|
343
|
-
await cloneCommand.execute(
|
|
344
|
-
new HandleBranchCommand(
|
|
345
|
-
{ api_key: config.source_stack },
|
|
346
|
-
this,
|
|
347
|
-
this.executeStackPrompt.bind(this, parentParams),
|
|
348
|
-
),
|
|
349
|
-
);
|
|
350
|
-
await this.executeExport();
|
|
351
|
-
} catch (error) {
|
|
352
|
-
throw error;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
async executeExport() {
|
|
357
|
-
try {
|
|
358
|
-
log.debug('Executing export operation', config.cloneContext);
|
|
359
|
-
const exportRes = await cloneCommand.execute(new HandleExportCommand(null, this));
|
|
360
|
-
await cloneCommand.execute(new SetBranchCommand(null, this));
|
|
361
|
-
|
|
362
|
-
if (exportRes) {
|
|
363
|
-
log.debug('Export operation completed, proceeding with destination', config.cloneContext);
|
|
364
|
-
this.executeDestination().catch(() => {
|
|
365
|
-
throw '';
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
} catch (error) {
|
|
369
|
-
throw error;
|
|
370
|
-
} finally {
|
|
371
|
-
this.removeBackKeyPressHandler();
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
async executeDestination() {
|
|
376
|
-
return new Promise(async (resolve, reject) => {
|
|
377
|
-
let keyPressHandler;
|
|
378
|
-
try {
|
|
379
|
-
log.debug('Executing destination setup', config.cloneContext);
|
|
380
|
-
let canCreateStack = false;
|
|
381
|
-
if (!config.target_stack) {
|
|
382
|
-
log.debug('Target stack not provided, prompting for stack creation', config.cloneContext);
|
|
383
|
-
canCreateStack = await inquirer.prompt(stackCreationConfirmation);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
this.setExectingCommand(0);
|
|
387
|
-
this.removeBackKeyPressHandler();
|
|
388
|
-
|
|
389
|
-
const orgMsgExistingStack = 'Choose an organization where the destination stack exists: ';
|
|
390
|
-
const orgMsgNewStack = 'Choose an organization where you want to create a stack: ';
|
|
391
|
-
|
|
392
|
-
let org;
|
|
393
|
-
if (!config.target_stack) {
|
|
394
|
-
org = await cloneCommand.execute(
|
|
395
|
-
new HandleOrgCommand(
|
|
396
|
-
{
|
|
397
|
-
msg: !canCreateStack.stackCreate ? orgMsgExistingStack : orgMsgNewStack,
|
|
398
|
-
},
|
|
399
|
-
this,
|
|
400
|
-
),
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
const params = { org, canCreateStack };
|
|
405
|
-
if (!config.target_stack) {
|
|
406
|
-
let self = this;
|
|
407
|
-
keyPressHandler = async function (_ch, key) {
|
|
408
|
-
if (key.name === 'left' && key.shift) {
|
|
409
|
-
if (self.executingCommand === 1) {
|
|
410
|
-
self.setExectingCommand(3);
|
|
411
|
-
} else if (self.executingCommand === 2) {
|
|
412
|
-
self.setExectingCommand(4);
|
|
413
|
-
}
|
|
414
|
-
if (self.createNewStackPrompt) {
|
|
415
|
-
self.createNewStackPrompt.stop();
|
|
416
|
-
}
|
|
417
|
-
config.target_stack = null;
|
|
418
|
-
config.targetStackBranch = null;
|
|
419
|
-
if (self.executingCommand != 0) {
|
|
420
|
-
console.clear();
|
|
421
|
-
await cloneCommand.undo();
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
};
|
|
425
|
-
process.stdin.addListener('keypress', keyPressHandler);
|
|
426
|
-
this.setBackKeyPressHandler(keyPressHandler);
|
|
427
|
-
await this.executeStackDestinationPrompt(params);
|
|
428
|
-
} else {
|
|
429
|
-
await this.executeBranchDestinationPrompt(params);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
log.debug('Destination setup completed successfully', config.cloneContext);
|
|
433
|
-
return resolve();
|
|
434
|
-
} catch (error) {
|
|
435
|
-
reject(error);
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
async executeStackDestinationPrompt(params) {
|
|
441
|
-
try {
|
|
442
|
-
this.setExectingCommand(1);
|
|
443
|
-
const { org, canCreateStack } = params;
|
|
444
|
-
if (!canCreateStack.stackCreate) {
|
|
445
|
-
const stackMsg = 'Choose the destination stack:';
|
|
446
|
-
await cloneCommand.execute(new HandleDestinationStackCommand({ org, msg: stackMsg, isSource: false }, this));
|
|
447
|
-
this.executeBranchDestinationPrompt(params);
|
|
448
|
-
} else {
|
|
449
|
-
const orgUid = orgUidList[org.Organization];
|
|
450
|
-
await cloneCommand.execute(new CreateNewStackCommand({ orgUid }, this));
|
|
451
|
-
this.removeBackKeyPressHandler();
|
|
452
|
-
await cloneCommand.execute(new CloneTypeSelectionCommand(null, this));
|
|
453
|
-
}
|
|
454
|
-
} catch (error) {
|
|
455
|
-
throw error;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
async executeBranchDestinationPrompt(parentParams) {
|
|
460
|
-
try {
|
|
461
|
-
this.setExectingCommand(2);
|
|
462
|
-
await cloneCommand.execute(
|
|
463
|
-
new HandleBranchCommand(
|
|
464
|
-
{ isSource: false, api_key: config.target_stack },
|
|
465
|
-
this,
|
|
466
|
-
this.executeStackDestinationPrompt.bind(this, parentParams),
|
|
467
|
-
),
|
|
468
|
-
);
|
|
469
|
-
this.removeBackKeyPressHandler();
|
|
470
|
-
await cloneCommand.execute(new CloneTypeSelectionCommand(null, this));
|
|
471
|
-
} catch (error) {
|
|
472
|
-
throw error;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
setCreateNewStackPrompt(createNewStackPrompt) {
|
|
477
|
-
this.createNewStackPrompt = createNewStackPrompt;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
async setBranch() {
|
|
481
|
-
if (!config.sourceStackBranch) {
|
|
482
|
-
try {
|
|
483
|
-
const branches = await client
|
|
484
|
-
.stack({ api_key: config.source_stack })
|
|
485
|
-
.branch()
|
|
486
|
-
.query()
|
|
487
|
-
.find()
|
|
488
|
-
.catch((_err) => {});
|
|
489
|
-
|
|
490
|
-
if (branches && branches.items && branches.items.length) {
|
|
491
|
-
config.sourceStackBranch = 'main';
|
|
492
|
-
}
|
|
493
|
-
} catch (_error) {}
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
async getOrganizationChoices(orgMessage) {
|
|
498
|
-
let orgChoice = {
|
|
499
|
-
type: 'list',
|
|
500
|
-
name: 'Organization',
|
|
501
|
-
message: orgMessage !== undefined ? orgMessage : 'Choose an organization',
|
|
502
|
-
choices: [],
|
|
503
|
-
};
|
|
504
|
-
return new Promise(async (resolve, reject) => {
|
|
505
|
-
log.debug('Fetching organization choices', config.cloneContext);
|
|
506
|
-
const spinner = ora('Fetching Organization').start();
|
|
507
|
-
try {
|
|
508
|
-
let organizations;
|
|
509
|
-
const configOrgUid = configHandler.get('oauthOrgUid');
|
|
510
|
-
log.debug('Getting organizations', config.cloneContext, { hasConfigOrgUid: !!configOrgUid });
|
|
511
|
-
|
|
512
|
-
if (configOrgUid) {
|
|
513
|
-
organizations = await client.organization(configOrgUid).fetch();
|
|
514
|
-
} else {
|
|
515
|
-
organizations = await client.organization().fetchAll({ limit: 100 });
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
spinner.succeed('Fetched Organization');
|
|
519
|
-
log.debug('Fetched organizations', config.cloneContext);
|
|
520
|
-
for (const element of organizations.items || [organizations]) {
|
|
521
|
-
orgUidList[element.name] = element.uid;
|
|
522
|
-
orgChoice.choices.push(element.name);
|
|
523
|
-
}
|
|
524
|
-
return resolve(orgChoice);
|
|
525
|
-
} catch (e) {
|
|
526
|
-
spinner.fail();
|
|
527
|
-
return reject(e);
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
async getStack(answer, stkMessage) {
|
|
533
|
-
return new Promise(async (resolve, reject) => {
|
|
534
|
-
let stackChoice = {
|
|
535
|
-
type: 'list',
|
|
536
|
-
name: 'stack',
|
|
537
|
-
message: stkMessage !== undefined ? stkMessage : 'Select the stack',
|
|
538
|
-
choices: [],
|
|
539
|
-
};
|
|
540
|
-
log.debug('Fetching stacks', config.cloneContext);
|
|
541
|
-
const spinner = ora('Fetching stacks').start();
|
|
542
|
-
try {
|
|
543
|
-
const organization_uid = orgUidList[answer.Organization];
|
|
544
|
-
log.debug('Querying stacks for organization', config.cloneContext, { organizationUid: organization_uid });
|
|
545
|
-
const stackList = client.stack().query({ organization_uid }).find();
|
|
546
|
-
stackList
|
|
547
|
-
.then((stacklist) => {
|
|
548
|
-
log.debug('Fetched stacks', config.cloneContext, { count: stacklist.items ? stacklist.items.length : 0 });
|
|
549
|
-
for (const element of stacklist.items) {
|
|
550
|
-
stackUidList[element.name] = element.api_key;
|
|
551
|
-
masterLocaleList[element.name] = element.master_locale;
|
|
552
|
-
stackChoice.choices.push(element.name);
|
|
553
|
-
}
|
|
554
|
-
spinner.succeed('Fetched stack');
|
|
555
|
-
return resolve(stackChoice);
|
|
556
|
-
})
|
|
557
|
-
.catch((error) => {
|
|
558
|
-
spinner.fail();
|
|
559
|
-
return reject(error);
|
|
560
|
-
});
|
|
561
|
-
} catch (e) {
|
|
562
|
-
spinner.fail();
|
|
563
|
-
return reject(e);
|
|
564
|
-
}
|
|
565
|
-
});
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
async createNewStack(options) {
|
|
569
|
-
return new Promise(async (resolve, reject) => {
|
|
570
|
-
try {
|
|
571
|
-
const { orgUid } = options;
|
|
572
|
-
log.debug('Creating new stack', config.cloneContext, { orgUid, masterLocale: master_locale, stackName: config.stackName });
|
|
573
|
-
this.displayBackOptionMessage();
|
|
574
|
-
let inputvalue;
|
|
575
|
-
if (!config.stackName) {
|
|
576
|
-
log.debug('Stack name not provided, prompting user', config.cloneContext);
|
|
577
|
-
prompt.start();
|
|
578
|
-
prompt.message = '';
|
|
579
|
-
this.setCreateNewStackPrompt(prompt);
|
|
580
|
-
inputvalue = await this.getNewStackPromptResult();
|
|
581
|
-
this.setCreateNewStackPrompt(null);
|
|
582
|
-
} else {
|
|
583
|
-
inputvalue = { stack: config.stackName };
|
|
584
|
-
}
|
|
585
|
-
if (this.executingCommand === 0 || !inputvalue) {
|
|
586
|
-
log.debug('Stack creation cancelled or invalid input', config.cloneContext);
|
|
587
|
-
return reject();
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
let stack = { name: inputvalue.stack, master_locale: master_locale };
|
|
591
|
-
log.debug('Creating stack with configuration', config.cloneContext);
|
|
592
|
-
const spinner = ora('Creating New stack').start();
|
|
593
|
-
log.debug('Sending stack creation API request', config.cloneContext);
|
|
594
|
-
let newStack = client.stack().create({ stack }, { organization_uid: orgUid });
|
|
595
|
-
newStack
|
|
596
|
-
.then((result) => {
|
|
597
|
-
log.debug('Stack created successfully', config.cloneContext, {
|
|
598
|
-
stackName: result.name,
|
|
599
|
-
});
|
|
600
|
-
spinner.succeed('New Stack created Successfully name as ' + result.name);
|
|
601
|
-
config.target_stack = result.api_key;
|
|
602
|
-
config.destinationStackName = result.name;
|
|
603
|
-
log.debug('Target stack configuration updated', config.cloneContext);
|
|
604
|
-
return resolve(result);
|
|
605
|
-
})
|
|
606
|
-
.catch((error) => {
|
|
607
|
-
spinner.fail();
|
|
608
|
-
return reject(error.errorMessage + ' Contact the Organization owner for Stack Creation access.');
|
|
609
|
-
});
|
|
610
|
-
} catch (error) {
|
|
611
|
-
return reject(error);
|
|
612
|
-
}
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
getNewStackPromptResult() {
|
|
617
|
-
return new Promise((resolve) => {
|
|
618
|
-
prompt.get(
|
|
619
|
-
{
|
|
620
|
-
properties: {
|
|
621
|
-
name: { description: colors.white(stackName.message), default: colors.grey(stackName.default) },
|
|
622
|
-
},
|
|
623
|
-
},
|
|
624
|
-
function (_, result) {
|
|
625
|
-
if (prompt.stopped) {
|
|
626
|
-
prompt.stopped = false;
|
|
627
|
-
resolve();
|
|
628
|
-
} else {
|
|
629
|
-
let _name = result.name.replace(/\[\d+m/g, '');
|
|
630
|
-
_name = _name.replace(//g, '');
|
|
631
|
-
resolve({ stack: _name });
|
|
632
|
-
}
|
|
633
|
-
},
|
|
634
|
-
);
|
|
635
|
-
});
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
async resolveBranchAliases(isSource = false) {
|
|
639
|
-
try {
|
|
640
|
-
log.debug('Resolving branch aliases', { ...config.cloneContext, isSource, alias: isSource ? config.sourceStackBranchAlias : config.targetStackBranchAlias });
|
|
641
|
-
if (isSource) {
|
|
642
|
-
const sourceStack = client.stack({ api_key: config.source_stack });
|
|
643
|
-
config.sourceStackBranch = await getBranchFromAlias(sourceStack, config.sourceStackBranchAlias);
|
|
644
|
-
log.debug('Source branch alias resolved', { ...config.cloneContext, alias: config.sourceStackBranchAlias, branch: config.sourceStackBranch });
|
|
645
|
-
} else {
|
|
646
|
-
const targetStack = client.stack({ api_key: config.target_stack });
|
|
647
|
-
config.targetStackBranch = await getBranchFromAlias(targetStack, config.targetStackBranchAlias);
|
|
648
|
-
log.debug('Target branch alias resolved', { ...config.cloneContext, alias: config.targetStackBranchAlias, branch: config.targetStackBranch });
|
|
649
|
-
}
|
|
650
|
-
} catch (error) {
|
|
651
|
-
throw error;
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
async cloneTypeSelection() {
|
|
656
|
-
console.clear();
|
|
657
|
-
return new Promise(async (resolve, reject) => {
|
|
658
|
-
log.debug('Starting clone type selection', config.cloneContext);
|
|
659
|
-
const choices = [
|
|
660
|
-
'Structure (all modules except entries & assets)',
|
|
661
|
-
'Structure with content (all modules including entries & assets)',
|
|
662
|
-
];
|
|
663
|
-
const cloneTypeSelection = [
|
|
664
|
-
{
|
|
665
|
-
choices,
|
|
666
|
-
type: 'list',
|
|
667
|
-
name: 'type',
|
|
668
|
-
message: 'Choose the type of data to clone:',
|
|
669
|
-
},
|
|
670
|
-
];
|
|
671
|
-
let successMsg;
|
|
672
|
-
let selectedValue = {};
|
|
673
|
-
config['data'] = path.join(__dirname.split('src')[0], 'contents', config.sourceStackBranch || '');
|
|
674
|
-
log.debug(`Clone data directory: ${config['data']}`, config.cloneContext);
|
|
675
|
-
|
|
676
|
-
if (!config.cloneType) {
|
|
677
|
-
log.debug('Clone type not specified, prompting user for selection', config.cloneContext);
|
|
678
|
-
selectedValue = await inquirer.prompt(cloneTypeSelection);
|
|
679
|
-
} else {
|
|
680
|
-
log.debug(`Using pre-configured clone type: ${config.cloneType}`, config.cloneContext);
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
if (config.cloneType === 'a' || selectedValue.type === 'Structure (all modules except entries & assets)') {
|
|
684
|
-
config['modules'] = structureList;
|
|
685
|
-
successMsg = 'Stack clone Structure completed';
|
|
686
|
-
log.debug(`Clone type: Structure only. Modules to clone: ${structureList.join(', ')}`, config.cloneContext);
|
|
687
|
-
} else {
|
|
688
|
-
successMsg = 'Stack clone completed with structure and content';
|
|
689
|
-
log.debug('Clone type: Structure with content (all modules)', config.cloneContext);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
this.cmdImport()
|
|
693
|
-
.then(() => {
|
|
694
|
-
log.debug('Clone type selection and import completed successfully', config.cloneContext);
|
|
695
|
-
resolve(successMsg);
|
|
696
|
-
})
|
|
697
|
-
.catch(reject);
|
|
698
|
-
});
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
async cmdExport() {
|
|
702
|
-
return new Promise((resolve, reject) => {
|
|
703
|
-
log.debug('Preparing export command', { ...config.cloneContext, sourceStack: config.source_stack, cloneType: config.cloneType });
|
|
704
|
-
// Creating export specific config by merging external configurations
|
|
705
|
-
let exportConfig = Object.assign({}, cloneDeep(config), { ...config?.export });
|
|
706
|
-
delete exportConfig.import;
|
|
707
|
-
delete exportConfig.export;
|
|
708
|
-
|
|
709
|
-
const exportDir = __dirname.split('src')[0] + 'contents';
|
|
710
|
-
log.debug(`Export directory: ${exportDir}`, config.cloneContext);
|
|
711
|
-
const cmd = ['-k', exportConfig.source_stack, '-d', exportDir];
|
|
712
|
-
|
|
713
|
-
if (exportConfig.cloneType === 'a') {
|
|
714
|
-
exportConfig.filteredModules = ['stack'].concat(structureList);
|
|
715
|
-
log.debug(`Filtered modules for structure-only export: ${exportConfig.filteredModules.join(', ')}`, config.cloneContext);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
if (exportConfig.source_alias) {
|
|
719
|
-
cmd.push('-a', exportConfig.source_alias);
|
|
720
|
-
log.debug(`Using source alias: ${exportConfig.source_alias}`, config.cloneContext);
|
|
721
|
-
}
|
|
722
|
-
if (exportConfig.sourceStackBranch) {
|
|
723
|
-
cmd.push('--branch', exportConfig.sourceStackBranch);
|
|
724
|
-
log.debug(`Using source branch: ${exportConfig.sourceStackBranch}`, config.cloneContext);
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
if (exportConfig.forceStopMarketplaceAppsPrompt) {
|
|
728
|
-
cmd.push('-y');
|
|
729
|
-
log.debug('Force stop marketplace apps prompt enabled', config.cloneContext);
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
const configFilePath = path.join(__dirname, 'dummyConfig.json');
|
|
733
|
-
cmd.push('-c');
|
|
734
|
-
cmd.push(configFilePath);
|
|
735
|
-
log.debug(`Writing export config to: ${configFilePath}`, config.cloneContext);
|
|
736
|
-
|
|
737
|
-
fs.writeFileSync(configFilePath, JSON.stringify(exportConfig));
|
|
738
|
-
log.debug('Export command prepared', config.cloneContext, {
|
|
739
|
-
cmd: cmd.join(' '),
|
|
740
|
-
exportDir,
|
|
741
|
-
sourceStack: exportConfig.source_stack,
|
|
742
|
-
branch: exportConfig.sourceStackBranch
|
|
743
|
-
});
|
|
744
|
-
log.debug('Running export command', config.cloneContext, { cmd });
|
|
745
|
-
let exportData = exportCmd.run(cmd);
|
|
746
|
-
exportData.then(() => {
|
|
747
|
-
log.debug('Export command completed successfully', config.cloneContext);
|
|
748
|
-
resolve(true);
|
|
749
|
-
}).catch((error) => {
|
|
750
|
-
reject(error);
|
|
751
|
-
});
|
|
752
|
-
});
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
async cmdImport() {
|
|
756
|
-
return new Promise(async (resolve, _reject) => {
|
|
757
|
-
log.debug('Preparing import command', { ...config.cloneContext, targetStack: config.target_stack, targetBranch: config.targetStackBranch });
|
|
758
|
-
// Creating export specific config by merging external configurations
|
|
759
|
-
let importConfig = Object.assign({}, cloneDeep(config), { ...config?.import });
|
|
760
|
-
delete importConfig.import;
|
|
761
|
-
delete importConfig.export;
|
|
762
|
-
|
|
763
|
-
const configFilePath = path.join(__dirname, 'dummyConfig.json');
|
|
764
|
-
const cmd = ['-c', configFilePath];
|
|
765
|
-
|
|
766
|
-
if (importConfig.destination_alias) {
|
|
767
|
-
cmd.push('-a', importConfig.destination_alias);
|
|
768
|
-
log.debug(`Using destination alias: ${importConfig.destination_alias}`, config.cloneContext);
|
|
769
|
-
}
|
|
770
|
-
if (!importConfig.data && importConfig.sourceStackBranch) {
|
|
771
|
-
const dataPath = path.join(importConfig.pathDir, importConfig.sourceStackBranch);
|
|
772
|
-
cmd.push('-d', dataPath);
|
|
773
|
-
log.debug(`Import data path: ${dataPath}`, config.cloneContext);
|
|
774
|
-
}
|
|
775
|
-
if (importConfig.targetStackBranch) {
|
|
776
|
-
cmd.push('--branch', importConfig.targetStackBranch);
|
|
777
|
-
log.debug(`Using target branch: ${importConfig.targetStackBranch}`, config.cloneContext);
|
|
778
|
-
}
|
|
779
|
-
if (importConfig.importWebhookStatus) {
|
|
780
|
-
cmd.push('--import-webhook-status', importConfig.importWebhookStatus);
|
|
781
|
-
log.debug(`Import webhook status: ${importConfig.importWebhookStatus}`, config.cloneContext);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
if (importConfig.skipAudit) {
|
|
785
|
-
cmd.push('--skip-audit');
|
|
786
|
-
log.debug('Skip audit flag enabled', config.cloneContext);
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
if (importConfig.forceStopMarketplaceAppsPrompt) {
|
|
790
|
-
cmd.push('-y');
|
|
791
|
-
log.debug('Force stop marketplace apps prompt enabled', config.cloneContext);
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
log.debug(`Writing import config to: ${configFilePath}`, config.cloneContext);
|
|
795
|
-
fs.writeFileSync(configFilePath, JSON.stringify(importConfig));
|
|
796
|
-
log.debug('Import command prepared', config.cloneContext, {
|
|
797
|
-
cmd: cmd.join(' '),
|
|
798
|
-
targetStack: importConfig.target_stack,
|
|
799
|
-
targetBranch: importConfig.targetStackBranch,
|
|
800
|
-
dataPath: importConfig.data || path.join(importConfig.pathDir, importConfig.sourceStackBranch)
|
|
801
|
-
});
|
|
802
|
-
log.debug('Running import command', config.cloneContext, { cmd });
|
|
803
|
-
await importCmd.run(cmd);
|
|
804
|
-
log.debug('Import command completed successfully', config.cloneContext);
|
|
805
|
-
log.debug('Clearing import config file', config.cloneContext);
|
|
806
|
-
fs.writeFileSync(configFilePath, JSON.stringify({}));
|
|
807
|
-
return resolve();
|
|
808
|
-
});
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
module.exports = {
|
|
813
|
-
CloneHandler,
|
|
814
|
-
client,
|
|
815
|
-
};
|