@contentstack/cli-cm-branches 1.0.10 → 1.0.11
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 +1 -1
- package/lib/branch/merge-handler.js +55 -4
- package/lib/commands/cm/branches/merge.js +1 -1
- package/lib/utils/asset-folder-create-script.js +161 -0
- package/lib/utils/create-merge-scripts.js +53 -20
- package/lib/utils/entry-create-script.js +497 -58
- package/lib/utils/entry-create-update-script.js +571 -0
- package/lib/utils/entry-update-script.js +547 -103
- package/lib/utils/interactive.js +84 -1
- package/oclif.manifest.json +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ $ npm install -g @contentstack/cli-cm-branches
|
|
|
37
37
|
$ csdx COMMAND
|
|
38
38
|
running command...
|
|
39
39
|
$ csdx (--version)
|
|
40
|
-
@contentstack/cli-cm-branches/1.0.
|
|
40
|
+
@contentstack/cli-cm-branches/1.0.11 linux-x64 node-v18.17.1
|
|
41
41
|
$ csdx --help [COMMAND]
|
|
42
42
|
USAGE
|
|
43
43
|
$ csdx COMMAND
|
|
@@ -5,7 +5,6 @@ const path_1 = tslib_1.__importDefault(require("path"));
|
|
|
5
5
|
const forEach_1 = tslib_1.__importDefault(require("lodash/forEach"));
|
|
6
6
|
const cli_utilities_1 = require("@contentstack/cli-utilities");
|
|
7
7
|
const utils_1 = require("../utils");
|
|
8
|
-
const enableEntryExp = false;
|
|
9
8
|
class MergeHandler {
|
|
10
9
|
constructor(options) {
|
|
11
10
|
this.stackAPIKey = options.stackAPIKey;
|
|
@@ -42,6 +41,14 @@ class MergeHandler {
|
|
|
42
41
|
else if (this.executeOption === 'export') {
|
|
43
42
|
await this.exportSummary(mergePayload);
|
|
44
43
|
}
|
|
44
|
+
else if (this.executeOption === 'merge_n_scripts') {
|
|
45
|
+
this.enableEntryExp = true;
|
|
46
|
+
await this.executeMerge(mergePayload);
|
|
47
|
+
}
|
|
48
|
+
else if (this.executeOption === 'summary_n_scripts') {
|
|
49
|
+
this.enableEntryExp = true;
|
|
50
|
+
await this.exportSummary(mergePayload);
|
|
51
|
+
}
|
|
45
52
|
else {
|
|
46
53
|
await this.exportSummary(mergePayload);
|
|
47
54
|
await this.executeMerge(mergePayload);
|
|
@@ -183,6 +190,9 @@ class MergeHandler {
|
|
|
183
190
|
};
|
|
184
191
|
await (0, utils_1.writeFile)(path_1.default.join(this.exportSummaryPath, 'merge-summary.json'), summary);
|
|
185
192
|
cli_utilities_1.cliux.success('Exported the summary successfully');
|
|
193
|
+
if (this.enableEntryExp) {
|
|
194
|
+
this.executeEntryExpFlow(this.stackAPIKey, mergePayload);
|
|
195
|
+
}
|
|
186
196
|
}
|
|
187
197
|
async executeMerge(mergePayload) {
|
|
188
198
|
let spinner;
|
|
@@ -204,11 +214,52 @@ class MergeHandler {
|
|
|
204
214
|
cli_utilities_1.cliux.error('Failed to merge the changes', error.message || error);
|
|
205
215
|
}
|
|
206
216
|
}
|
|
207
|
-
executeEntryExpFlow(mergeJobUID, mergePayload) {
|
|
208
|
-
|
|
217
|
+
async executeEntryExpFlow(mergeJobUID, mergePayload) {
|
|
218
|
+
const { mergeContent } = this.mergeSettings;
|
|
219
|
+
let mergePreference = await (0, utils_1.selectContentMergePreference)();
|
|
220
|
+
let selectedMergePreference;
|
|
221
|
+
const updateEntryMergeStrategy = (items, mergeStrategy) => {
|
|
222
|
+
items &&
|
|
223
|
+
items.forEach((item) => {
|
|
224
|
+
item.entry_merge_strategy = mergeStrategy;
|
|
225
|
+
});
|
|
226
|
+
};
|
|
227
|
+
switch (mergePreference) {
|
|
228
|
+
case 'existing_new':
|
|
229
|
+
selectedMergePreference = 'merge_existing_new';
|
|
230
|
+
updateEntryMergeStrategy(mergeContent.content_types.added, selectedMergePreference);
|
|
231
|
+
updateEntryMergeStrategy(mergeContent.content_types.modified, selectedMergePreference);
|
|
232
|
+
break;
|
|
233
|
+
case 'new':
|
|
234
|
+
selectedMergePreference = 'merge_new';
|
|
235
|
+
updateEntryMergeStrategy(mergeContent.content_types.added, selectedMergePreference);
|
|
236
|
+
break;
|
|
237
|
+
case 'existing':
|
|
238
|
+
selectedMergePreference = 'merge_existing';
|
|
239
|
+
updateEntryMergeStrategy(mergeContent.content_types.modified, selectedMergePreference);
|
|
240
|
+
break;
|
|
241
|
+
case 'ask_preference':
|
|
242
|
+
selectedMergePreference = 'custom';
|
|
243
|
+
const selectedMergeItems = await (0, utils_1.selectContentMergeCustomPreferences)(mergeContent.content_types);
|
|
244
|
+
mergeContent.content_types = {
|
|
245
|
+
added: [],
|
|
246
|
+
modified: [],
|
|
247
|
+
deleted: [],
|
|
248
|
+
};
|
|
249
|
+
selectedMergeItems.forEach((item) => {
|
|
250
|
+
mergeContent.content_types[item.status].push(item.value);
|
|
251
|
+
});
|
|
252
|
+
break;
|
|
253
|
+
default:
|
|
254
|
+
cli_utilities_1.cliux.error(`error: Invalid preference ${mergePreference}`);
|
|
255
|
+
process.exit(1);
|
|
256
|
+
}
|
|
257
|
+
let scriptFolderPath = (0, utils_1.generateMergeScripts)(mergeContent.content_types, mergeJobUID);
|
|
209
258
|
if (scriptFolderPath !== undefined) {
|
|
210
259
|
cli_utilities_1.cliux.success(`\nSuccess! We have generated entry migration files in the folder ${scriptFolderPath}`);
|
|
211
|
-
cli_utilities_1.cliux.print(
|
|
260
|
+
cli_utilities_1.cliux.print('\nWARNING!!! Migration is not intended to be run more than once. Migrated(entries/assets) will be duplicated if run more than once', { color: 'yellow' });
|
|
261
|
+
const migrationCommand = `csdx cm:stacks:migration --multiple --file-path ./${scriptFolderPath} --config {compare-branch:${mergePayload.compare_branch},file-path:./${scriptFolderPath}} --branch ${mergePayload.base_branch} --stack-api-key ${this.stackAPIKey}`;
|
|
262
|
+
cli_utilities_1.cliux.print(`\nKindly follow the steps in the guide "https://www.contentstack.com/docs/developers/cli/migrate-branch-entries" to update the migration scripts, and then run the command:\n\n${migrationCommand}`, { color: 'blue' });
|
|
212
263
|
}
|
|
213
264
|
}
|
|
214
265
|
async restartMergeProcess() {
|
|
@@ -35,7 +35,7 @@ class BranchMergeCommand extends cli_command_1.Command {
|
|
|
35
35
|
exportSummaryPath: branchMergeFlags['export-summary-path'],
|
|
36
36
|
mergeSummary: branchMergeFlags.mergeSummary,
|
|
37
37
|
host: this.cmaHost,
|
|
38
|
-
enableEntryExp:
|
|
38
|
+
enableEntryExp: false,
|
|
39
39
|
}).start();
|
|
40
40
|
}
|
|
41
41
|
catch (error) {
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assetFolderCreateScript = void 0;
|
|
4
|
+
function assetFolderCreateScript(contentType) {
|
|
5
|
+
return `
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
module.exports = async ({ migration, stackSDKInstance, managementAPIClient, config, branch, apiKey }) => {
|
|
9
|
+
let filePath = config['file-path'] || process.cwd();
|
|
10
|
+
let compareBranch = config['compare-branch'];
|
|
11
|
+
let folderMapper = {};
|
|
12
|
+
let folderBucket = [];
|
|
13
|
+
|
|
14
|
+
const getAssetCount = async function (branchName, isDir = false) {
|
|
15
|
+
const queryParam = {
|
|
16
|
+
asc: 'created_at',
|
|
17
|
+
include_count: true,
|
|
18
|
+
skip: 10 ** 100,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
if (isDir) queryParam.query = { is_dir: true };
|
|
22
|
+
|
|
23
|
+
return await managementAPIClient
|
|
24
|
+
.stack({ api_key: stackSDKInstance.api_key, branch_uid: branchName })
|
|
25
|
+
.asset()
|
|
26
|
+
.query(queryParam)
|
|
27
|
+
.count()
|
|
28
|
+
.then(({ assets }) => assets)
|
|
29
|
+
.catch((error) => {});
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
async function getFolderJSON(skip, fCount, branchName, folderData = []) {
|
|
33
|
+
const queryRequestObj = {
|
|
34
|
+
skip,
|
|
35
|
+
include_folders: true,
|
|
36
|
+
query: { is_dir: true },
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return await managementAPIClient
|
|
40
|
+
.stack({ api_key: stackSDKInstance.api_key, branch_uid: branchName })
|
|
41
|
+
.asset()
|
|
42
|
+
.query(queryRequestObj)
|
|
43
|
+
.find()
|
|
44
|
+
.then(async (response) => {
|
|
45
|
+
skip += 100;
|
|
46
|
+
folderData = [...folderData, ...response.items];
|
|
47
|
+
if (skip >= fCount) {
|
|
48
|
+
return folderData;
|
|
49
|
+
}
|
|
50
|
+
return await getFolderJSON(skip, fCount, branchName, folderData);
|
|
51
|
+
})
|
|
52
|
+
.catch((error) => {});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function buildTree(coll) {
|
|
56
|
+
let tree = {};
|
|
57
|
+
for (let i = 0; i < coll.length; i++) {
|
|
58
|
+
if (coll[i].parent_uid === null || !coll[i].hasOwnProperty('parent_uid')) {
|
|
59
|
+
tree[coll[i].uid] = {};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
findBranches(tree, Object.keys(tree), coll);
|
|
63
|
+
return tree;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function findBranches(tree, branches, coll) {
|
|
67
|
+
branches.forEach((branch) => {
|
|
68
|
+
for (const element of coll) {
|
|
69
|
+
if (branch === element.parent_uid) {
|
|
70
|
+
let childUid = element.uid;
|
|
71
|
+
tree[branch][childUid] = {};
|
|
72
|
+
return findBranches(tree[branch], [childUid], coll);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function buildFolderReqObjs(baseFolderUIDs, compareAssetsFolder, tree, parent_uid) {
|
|
79
|
+
for (let leaf in tree) {
|
|
80
|
+
//folder doesn't exists
|
|
81
|
+
if (baseFolderUIDs.indexOf(leaf) === -1) {
|
|
82
|
+
let folderObj = compareAssetsFolder.filter((folder) => folder.uid === leaf);
|
|
83
|
+
if (folderObj && folderObj.length) {
|
|
84
|
+
let requestOption = {
|
|
85
|
+
folderReq: {
|
|
86
|
+
asset: {
|
|
87
|
+
name: folderObj[0].name,
|
|
88
|
+
parent_uid: parent_uid || null,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
oldUid: leaf,
|
|
92
|
+
};
|
|
93
|
+
folderBucket.push(requestOption);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (Object.keys(tree[leaf]).length > 0) {
|
|
97
|
+
buildFolderReqObjs(baseFolderUIDs, compareAssetsFolder, tree[leaf], leaf, folderBucket);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function createFolder(payload) {
|
|
103
|
+
if (folderMapper.hasOwnProperty(payload.folderReq.asset.parent_uid)) {
|
|
104
|
+
// replace old uid with new
|
|
105
|
+
payload.folderReq.asset.parent_uid = folderMapper[payload.folderReq.asset.parent_uid];
|
|
106
|
+
}
|
|
107
|
+
await managementAPIClient
|
|
108
|
+
.stack({ api_key: apiKey, branch_uid: branch })
|
|
109
|
+
.asset()
|
|
110
|
+
.folder()
|
|
111
|
+
.create(payload.folderReq)
|
|
112
|
+
.then((res) => {
|
|
113
|
+
folderMapper[payload.oldUid] = res.uid;
|
|
114
|
+
})
|
|
115
|
+
.catch((err) => console.log(err));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const createAssetTask = () => {
|
|
119
|
+
return {
|
|
120
|
+
title: 'Create Assets Folder',
|
|
121
|
+
successTitle: 'Assets folder Created Successfully',
|
|
122
|
+
failedTitle: 'Failed to create assets folder',
|
|
123
|
+
task: async () => {
|
|
124
|
+
try {
|
|
125
|
+
const baseAssetsFolderCount = await getAssetCount(branch, true);
|
|
126
|
+
const compareAssetsFolderCount = await getAssetCount(compareBranch, true);
|
|
127
|
+
const baseAssetsFolder = await getFolderJSON(0, baseAssetsFolderCount, branch);
|
|
128
|
+
const compareAssetsFolder = await getFolderJSON(0, compareAssetsFolderCount, compareBranch);
|
|
129
|
+
if (Array.isArray(compareAssetsFolder) && Array.isArray(baseAssetsFolder)) {
|
|
130
|
+
const baseAssetUIDs = baseAssetsFolder.map((bAsset) => bAsset.uid);
|
|
131
|
+
//create new asset folder in base branch and update it in mapper
|
|
132
|
+
const tree = buildTree(compareAssetsFolder);
|
|
133
|
+
buildFolderReqObjs(baseAssetUIDs, compareAssetsFolder, tree, null);
|
|
134
|
+
for (let i = 0; i < folderBucket.length; i++) {
|
|
135
|
+
await createFolder(folderBucket[i]);
|
|
136
|
+
}
|
|
137
|
+
fs.writeFileSync(path.resolve(filePath, 'folder-mapper.json'), JSON.stringify(folderMapper));
|
|
138
|
+
}
|
|
139
|
+
} catch (error) {
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
if (compareBranch && branch.length !== 0 && apiKey.length !== 0) {
|
|
146
|
+
migration.addTask(createAssetTask());
|
|
147
|
+
} else {
|
|
148
|
+
if (apiKey.length === 0) {
|
|
149
|
+
console.error('Please provide api key using --stack-api-key flag');
|
|
150
|
+
}
|
|
151
|
+
if (!compareBranch) {
|
|
152
|
+
console.error('Please provide compare branch through --config compare-branch:<value> flag');
|
|
153
|
+
}
|
|
154
|
+
if (branch.length === 0) {
|
|
155
|
+
console.error('Please provide branch name through --branch flag');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
`;
|
|
160
|
+
}
|
|
161
|
+
exports.assetFolderCreateScript = assetFolderCreateScript;
|
|
@@ -1,26 +1,49 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createMergeScripts = exports.
|
|
3
|
+
exports.createMergeScripts = exports.getContentTypeMergeStatus = exports.generateMergeScripts = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
6
|
+
const cli_utilities_1 = require("@contentstack/cli-utilities");
|
|
6
7
|
const entry_create_script_1 = require("./entry-create-script");
|
|
7
8
|
const entry_update_script_1 = require("./entry-update-script");
|
|
9
|
+
const entry_create_update_script_1 = require("./entry-create-update-script");
|
|
10
|
+
const asset_folder_create_script_1 = require("./asset-folder-create-script");
|
|
8
11
|
function generateMergeScripts(mergeSummary, mergeJobUID) {
|
|
9
|
-
var _a, _b, _c;
|
|
10
12
|
try {
|
|
11
13
|
let scriptFolderPath;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
const processContentType = (contentType, scriptFunction) => {
|
|
15
|
+
let data;
|
|
16
|
+
if (contentType.uid) {
|
|
17
|
+
data = scriptFunction(contentType.uid);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
data = scriptFunction();
|
|
21
|
+
}
|
|
22
|
+
scriptFolderPath = createMergeScripts(contentType, mergeJobUID, data);
|
|
23
|
+
};
|
|
24
|
+
const mergeStrategies = {
|
|
25
|
+
asset_create_folder: asset_folder_create_script_1.assetFolderCreateScript,
|
|
26
|
+
merge_existing_new: entry_create_update_script_1.entryCreateUpdateScript,
|
|
27
|
+
merge_existing: entry_update_script_1.entryUpdateScript,
|
|
28
|
+
merge_new: entry_create_script_1.entryCreateScript,
|
|
29
|
+
ignore: entry_create_update_script_1.entryCreateUpdateScript,
|
|
30
|
+
};
|
|
31
|
+
const processContentTypes = (contentTypes, messageType) => {
|
|
32
|
+
if (contentTypes && contentTypes.length > 0) {
|
|
33
|
+
processContentType({ type: 'assets', uid: '', entry_merge_strategy: '' }, mergeStrategies['asset_create_folder']);
|
|
34
|
+
contentTypes.forEach((contentType) => {
|
|
35
|
+
const mergeStrategy = contentType.entry_merge_strategy;
|
|
36
|
+
if (mergeStrategies.hasOwnProperty(mergeStrategy)) {
|
|
37
|
+
processContentType(contentType, mergeStrategies[mergeStrategy]);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
cli_utilities_1.cliux.print(`No ${messageType} entries selected for merge`, { color: 'yellow' });
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
processContentTypes(mergeSummary.modified, 'Modified');
|
|
46
|
+
processContentTypes(mergeSummary.added, 'New');
|
|
24
47
|
return scriptFolderPath;
|
|
25
48
|
}
|
|
26
49
|
catch (error) {
|
|
@@ -28,19 +51,22 @@ function generateMergeScripts(mergeSummary, mergeJobUID) {
|
|
|
28
51
|
}
|
|
29
52
|
}
|
|
30
53
|
exports.generateMergeScripts = generateMergeScripts;
|
|
31
|
-
function
|
|
32
|
-
if (status === '
|
|
54
|
+
function getContentTypeMergeStatus(status) {
|
|
55
|
+
if (status === 'merge_existing') {
|
|
33
56
|
return 'updated';
|
|
34
57
|
}
|
|
35
|
-
else if (status === '
|
|
58
|
+
else if (status === 'merge_new') {
|
|
36
59
|
return 'created';
|
|
37
60
|
}
|
|
61
|
+
else if (status === 'merge_existing_new') {
|
|
62
|
+
return 'created_updated';
|
|
63
|
+
}
|
|
38
64
|
else {
|
|
39
65
|
return '';
|
|
40
66
|
}
|
|
41
67
|
}
|
|
42
|
-
exports.
|
|
43
|
-
function createMergeScripts(contentType,
|
|
68
|
+
exports.getContentTypeMergeStatus = getContentTypeMergeStatus;
|
|
69
|
+
function createMergeScripts(contentType, mergeJobUID, content) {
|
|
44
70
|
const date = new Date();
|
|
45
71
|
const rootFolder = 'merge_scripts';
|
|
46
72
|
const fileCreatedAt = `${date.getFullYear()}${date.getMonth().toString.length === 1 ? `0${date.getMonth() + 1}` : date.getMonth() + 1}${date.getUTCDate()}${date.getHours()}${date.getMinutes()}${date.getSeconds()}`;
|
|
@@ -56,7 +82,14 @@ function createMergeScripts(contentType, content, mergeJobUID) {
|
|
|
56
82
|
if (!fs_1.default.existsSync(fullPath)) {
|
|
57
83
|
fs_1.default.mkdirSync(fullPath);
|
|
58
84
|
}
|
|
59
|
-
|
|
85
|
+
let filePath;
|
|
86
|
+
if (contentType.type === 'assets') {
|
|
87
|
+
filePath = `${fullPath}/${fileCreatedAt}_create_assets_folder.js`;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
filePath = `${fullPath}/${fileCreatedAt}_${getContentTypeMergeStatus(contentType.entry_merge_strategy)}_${contentType.uid}.js`;
|
|
91
|
+
}
|
|
92
|
+
fs_1.default.writeFileSync(filePath, content, 'utf-8');
|
|
60
93
|
}
|
|
61
94
|
return fullPath;
|
|
62
95
|
}
|