@contentstack/cli-cm-clone 0.1.0-beta.6 → 1.0.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/LICENSE +21 -0
- package/README.md +5 -5
- package/package.json +17 -12
- package/src/commands/cm/stacks/clone.js +239 -0
- package/src/lib/util/clone-handler.js +267 -187
- package/src/lib/util/contentstack-management-sdk.js +10 -11
- package/src/lib/util/log.js +32 -27
- package/src/commands/cm/stack-clone.js +0 -102
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Contentstack
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -33,23 +33,23 @@ USAGE
|
|
|
33
33
|
```
|
|
34
34
|
# Commands
|
|
35
35
|
<!-- commands -->
|
|
36
|
-
* [`csdx cm:
|
|
36
|
+
* [`csdx cm:stacks:clone`](#csdx-cmstack-clone)
|
|
37
37
|
|
|
38
|
-
## `csdx cm:
|
|
38
|
+
## `csdx cm:stacks:clone`
|
|
39
39
|
|
|
40
40
|
This command allows you to migrate data (structure or content or both) from one stack to another stack (either new or existing)
|
|
41
41
|
|
|
42
42
|
```
|
|
43
43
|
USAGE
|
|
44
|
-
$ csdx cm:
|
|
44
|
+
$ csdx cm:stacks:clone
|
|
45
45
|
|
|
46
46
|
DESCRIPTION
|
|
47
47
|
...
|
|
48
48
|
Use this plugin to automate the process of cloning a stack in a few steps.
|
|
49
49
|
|
|
50
50
|
EXAMPLE
|
|
51
|
-
csdx cm:
|
|
51
|
+
csdx cm:stacks:clone
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
_See code: [src/commands/cm/
|
|
54
|
+
_See code: [src/commands/cm/stacks/clone.js](https://github.com/contentstack/cli/blob/v0.1.0-beta-1/src/commands/cm/stack-clone.js)_
|
|
55
55
|
<!-- commandsstop -->
|
package/package.json
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentstack/cli-cm-clone",
|
|
3
3
|
"description": "Contentstack stack clone plugin",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"author": "Contentstack",
|
|
6
6
|
"bugs": "https://github.com/rohitmishra209/cli-cm-clone/issues",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@contentstack/cli-cm-export": "^
|
|
9
|
-
"@contentstack/cli-cm-import": "^
|
|
10
|
-
"@contentstack/cli-command": "^
|
|
8
|
+
"@contentstack/cli-cm-export": "^1.0.0",
|
|
9
|
+
"@contentstack/cli-cm-import": "^1.0.0",
|
|
10
|
+
"@contentstack/cli-command": "^1.0.0",
|
|
11
11
|
"@contentstack/management": "^1.3.0",
|
|
12
|
-
"@
|
|
13
|
-
"@oclif/
|
|
14
|
-
"
|
|
12
|
+
"@contentstack/cli-utilities": "^1.0.0",
|
|
13
|
+
"@oclif/command": "^1.8.16",
|
|
14
|
+
"@oclif/config": "^1.18.3",
|
|
15
|
+
"async": "^3.2.4",
|
|
15
16
|
"child_process": "^1.0.2",
|
|
16
|
-
"configstore": "^5.0.1",
|
|
17
17
|
"fancy-test": "^1.4.10",
|
|
18
18
|
"inquirer": "^7.3.3",
|
|
19
19
|
"ora": "^5.1.0",
|
|
20
20
|
"rimraf": "^3.0.2",
|
|
21
|
-
"winston": "^3.
|
|
21
|
+
"winston": "^3.7.2"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@oclif/dev-cli": "^1.22.2",
|
|
25
|
-
"@oclif/plugin-help": "^
|
|
25
|
+
"@oclif/plugin-help": "^5.1.12",
|
|
26
26
|
"@oclif/test": "^1.2.7",
|
|
27
27
|
"chai": "^4.2.0",
|
|
28
|
-
"eslint": "^
|
|
28
|
+
"eslint": "^8.18.0",
|
|
29
29
|
"eslint-config-oclif": "^3.1.0",
|
|
30
30
|
"globby": "^10.0.2",
|
|
31
31
|
"jest": "^26.6.3",
|
|
32
|
-
"mocha": "^
|
|
32
|
+
"mocha": "^10.0.0",
|
|
33
33
|
"nyc": "^14.1.1",
|
|
34
34
|
"sinon": "^9.2.4"
|
|
35
35
|
},
|
|
@@ -59,5 +59,10 @@
|
|
|
59
59
|
"scripts": {
|
|
60
60
|
"test": "nyc --reporter=html mocha \"test/**/*.test.js\"",
|
|
61
61
|
"version": "oclif-dev readme && git add README.md"
|
|
62
|
+
},
|
|
63
|
+
"csdxConfig": {
|
|
64
|
+
"expiredCommands": {
|
|
65
|
+
"cm:stack-clone": "csdx cm:stacks:clone"
|
|
66
|
+
}
|
|
62
67
|
}
|
|
63
68
|
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
const { Command, flags } = require('@contentstack/cli-command');
|
|
2
|
+
const { configHandler } = require('@contentstack/cli-utilities');
|
|
3
|
+
const { CloneHandler } = require('../../../lib/util/clone-handler');
|
|
4
|
+
let config = require('../../../lib/util/dummyConfig.json');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const rimraf = require('rimraf');
|
|
7
|
+
let pathdir = path.join(__dirname.split('src')[0], 'contents');
|
|
8
|
+
const { readdirSync } = require('fs');
|
|
9
|
+
|
|
10
|
+
class StackCloneCommand extends Command {
|
|
11
|
+
async run() {
|
|
12
|
+
try {
|
|
13
|
+
let self = this;
|
|
14
|
+
let _authToken = configHandler.get('authtoken');
|
|
15
|
+
const cloneCommandFlags = self.parse(StackCloneCommand).flags;
|
|
16
|
+
const {
|
|
17
|
+
type: cloneType,
|
|
18
|
+
'stack-name': stackName,
|
|
19
|
+
'source-branch': sourceStackBranch,
|
|
20
|
+
'target-branch': targetStackBranch,
|
|
21
|
+
'source-stack-api-key': sourceStackApiKey,
|
|
22
|
+
'destination-stack-api-key': destinationStackApiKey,
|
|
23
|
+
'source-management-token-alias': sourceManagementTokenAlias,
|
|
24
|
+
'destination-management-token-alias': destinationManagementTokenAlias,
|
|
25
|
+
'import-webhook-status': importWebhookStatus,
|
|
26
|
+
} = cloneCommandFlags;
|
|
27
|
+
|
|
28
|
+
const handleClone = async () => {
|
|
29
|
+
const listOfTokens = configHandler.get('tokens');
|
|
30
|
+
|
|
31
|
+
if (cloneType) {
|
|
32
|
+
config.cloneType = cloneType;
|
|
33
|
+
}
|
|
34
|
+
if (stackName) {
|
|
35
|
+
config.stackName = stackName;
|
|
36
|
+
}
|
|
37
|
+
if (sourceStackBranch) {
|
|
38
|
+
config.sourceStackBranch = sourceStackBranch;
|
|
39
|
+
}
|
|
40
|
+
if (targetStackBranch) {
|
|
41
|
+
config.targetStackBranch = targetStackBranch;
|
|
42
|
+
}
|
|
43
|
+
if (sourceStackApiKey) {
|
|
44
|
+
config.source_stack = sourceStackApiKey;
|
|
45
|
+
}
|
|
46
|
+
if (destinationStackApiKey) {
|
|
47
|
+
config.target_stack = destinationStackApiKey;
|
|
48
|
+
}
|
|
49
|
+
if (sourceManagementTokenAlias && listOfTokens[sourceManagementTokenAlias]) {
|
|
50
|
+
config.source_alias = sourceManagementTokenAlias;
|
|
51
|
+
config.source_stack = listOfTokens[sourceManagementTokenAlias].apiKey;
|
|
52
|
+
} else if (sourceManagementTokenAlias) {
|
|
53
|
+
console.log(`Provided source token alias (${sourceManagementTokenAlias}) not found in your config.!`);
|
|
54
|
+
}
|
|
55
|
+
if (destinationManagementTokenAlias && listOfTokens[destinationManagementTokenAlias]) {
|
|
56
|
+
config.destination_alias = destinationManagementTokenAlias;
|
|
57
|
+
config.target_stack = listOfTokens[destinationManagementTokenAlias].apiKey;
|
|
58
|
+
} else if (destinationManagementTokenAlias) {
|
|
59
|
+
console.log(
|
|
60
|
+
`Provided destination token alias (${destinationManagementTokenAlias}) not found in your config.!`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if (importWebhookStatus) {
|
|
64
|
+
config.importWebhookStatus = importWebhookStatus;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
await this.removeContentDirIfNotEmptyBeforeClone(pathdir); // NOTE remove if folder not empty before clone
|
|
68
|
+
this.registerCleanupOnInterrupt(pathdir);
|
|
69
|
+
|
|
70
|
+
config.auth_token = _authToken;
|
|
71
|
+
config.host = this.cmaHost;
|
|
72
|
+
config.cdn = this.cdaHost;
|
|
73
|
+
const cloneHandler = new CloneHandler(config);
|
|
74
|
+
await cloneHandler.start();
|
|
75
|
+
let successMessage = 'Stack cloning process have been completed successfully';
|
|
76
|
+
await this.cleanUp(pathdir, successMessage);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
if (sourceManagementTokenAlias && destinationManagementTokenAlias) {
|
|
80
|
+
if (sourceStackBranch || targetStackBranch) {
|
|
81
|
+
if (_authToken) {
|
|
82
|
+
handleClone();
|
|
83
|
+
} else {
|
|
84
|
+
console.log('Please login to execute this command, csdx auth:login');
|
|
85
|
+
this.exit(1);
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
handleClone();
|
|
89
|
+
}
|
|
90
|
+
} else if (_authToken) {
|
|
91
|
+
handleClone();
|
|
92
|
+
} else {
|
|
93
|
+
console.log('Please login to execute this command, csdx auth:login');
|
|
94
|
+
this.exit(1);
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
await this.cleanUp(pathdir);
|
|
98
|
+
// eslint-disable-next-line no-console
|
|
99
|
+
console.log(error.message || error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async removeContentDirIfNotEmptyBeforeClone(dir) {
|
|
104
|
+
try {
|
|
105
|
+
const dirNotEmpty = readdirSync(dir).length;
|
|
106
|
+
|
|
107
|
+
if (dirNotEmpty) {
|
|
108
|
+
await this.cleanUp(dir);
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
const omit = ['ENOENT']; // NOTE add emittable error codes in the array
|
|
112
|
+
|
|
113
|
+
if (!omit.includes(error.code)) {
|
|
114
|
+
console.log(error.message);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
cleanUp(pathDir, message) {
|
|
120
|
+
return new Promise((resolve) => {
|
|
121
|
+
rimraf(pathDir, function (err) {
|
|
122
|
+
if (err) {
|
|
123
|
+
console.log('\nCleaning up');
|
|
124
|
+
const skipCodeArr = ['ENOENT', 'EBUSY', 'EPERM', 'EMFILE', 'ENOTEMPTY'];
|
|
125
|
+
|
|
126
|
+
if (skipCodeArr.includes(err.code)) {
|
|
127
|
+
process.exit();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (message) {
|
|
132
|
+
// eslint-disable-next-line no-console
|
|
133
|
+
console.log(message);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
resolve();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
registerCleanupOnInterrupt(pathDir) {
|
|
142
|
+
const interrupt = ['SIGINT', 'SIGQUIT', 'SIGTERM'];
|
|
143
|
+
const exceptions = ['unhandledRejection', 'uncaughtException'];
|
|
144
|
+
|
|
145
|
+
const cleanUp = async (exitOrError = null) => {
|
|
146
|
+
// eslint-disable-next-line no-console
|
|
147
|
+
console.log('\nCleaning up');
|
|
148
|
+
await this.cleanUp(pathDir);
|
|
149
|
+
// eslint-disable-next-line no-console
|
|
150
|
+
console.log('done');
|
|
151
|
+
// eslint-disable-next-line no-process-exit
|
|
152
|
+
|
|
153
|
+
if (exitOrError instanceof Promise) {
|
|
154
|
+
exitOrError.catch((error) => {
|
|
155
|
+
console.log((error && error.message) || '');
|
|
156
|
+
});
|
|
157
|
+
} else if (exitOrError && exitOrError.message) {
|
|
158
|
+
console.log(exitOrError.message);
|
|
159
|
+
} else if (exitOrError && exitOrError.errorMessage) {
|
|
160
|
+
console.log(exitOrError.message);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (exitOrError === true) process.exit();
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
exceptions.forEach((event) => process.on(event, cleanUp));
|
|
167
|
+
interrupt.forEach((signal) => process.on(signal, () => cleanUp(true)));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
StackCloneCommand.description = `Clone data (structure/content or both) of a stack into another stack
|
|
172
|
+
Use this plugin to automate the process of cloning a stack in few steps.
|
|
173
|
+
`;
|
|
174
|
+
|
|
175
|
+
StackCloneCommand.examples = [
|
|
176
|
+
'csdx cm:stacks:clone',
|
|
177
|
+
'csdx cm:stacks:clone --source-branch <source-branch-name> --target-branch <target-branch-name>',
|
|
178
|
+
'csdx cm:stacks:clone --source-stack-api-key <apiKey> --destination-stack-api-key <apiKey>',
|
|
179
|
+
'csdx cm:stacks:clone --source-management-token-alias <management token alias> --destination-management-token-alias <management token alias>',
|
|
180
|
+
'csdx cm:stacks:clone --source-branch --target-branch --source-management-token-alias <management token alias> --destination-management-token-alias <management token alias>',
|
|
181
|
+
'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>',
|
|
182
|
+
];
|
|
183
|
+
|
|
184
|
+
StackCloneCommand.aliases = ['cm:stack-clone'];
|
|
185
|
+
|
|
186
|
+
StackCloneCommand.flags = {
|
|
187
|
+
'source-branch': flags.string({
|
|
188
|
+
required: false,
|
|
189
|
+
multiple: false,
|
|
190
|
+
description: 'Branch of the source stack.',
|
|
191
|
+
}),
|
|
192
|
+
'target-branch': flags.string({
|
|
193
|
+
required: false,
|
|
194
|
+
multiple: false,
|
|
195
|
+
description: 'Branch of the target stack.',
|
|
196
|
+
}),
|
|
197
|
+
'source-management-token-alias': flags.string({
|
|
198
|
+
required: false,
|
|
199
|
+
multiple: false,
|
|
200
|
+
description: 'Source API key of the target stack token alias.',
|
|
201
|
+
}),
|
|
202
|
+
'destination-management-token-alias': flags.string({
|
|
203
|
+
required: false,
|
|
204
|
+
multiple: false,
|
|
205
|
+
description: 'Source API key of the target stack token alias.',
|
|
206
|
+
}),
|
|
207
|
+
'stack-name': flags.string({
|
|
208
|
+
char: 'n',
|
|
209
|
+
required: false,
|
|
210
|
+
multiple: false,
|
|
211
|
+
description: 'Name for the new stack to store the cloned content.',
|
|
212
|
+
}),
|
|
213
|
+
type: flags.string({
|
|
214
|
+
required: false,
|
|
215
|
+
multiple: false,
|
|
216
|
+
options: ['a', 'b'],
|
|
217
|
+
description: `Type of data to clone
|
|
218
|
+
a) Structure (all modules except entries & assets)
|
|
219
|
+
b) Structure with content (all modules including entries & assets)
|
|
220
|
+
`,
|
|
221
|
+
}),
|
|
222
|
+
'source-stack-api-key': flags.string({
|
|
223
|
+
description: 'Source stack API Key',
|
|
224
|
+
}),
|
|
225
|
+
'destination-stack-api-key': flags.string({
|
|
226
|
+
description: 'Destination stack API Key',
|
|
227
|
+
}),
|
|
228
|
+
'import-webhook-status': flags.string({
|
|
229
|
+
description: 'Webhook state',
|
|
230
|
+
options: ['disable', 'current'],
|
|
231
|
+
required: false,
|
|
232
|
+
default: 'disable',
|
|
233
|
+
}),
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
StackCloneCommand.usage =
|
|
237
|
+
'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]';
|
|
238
|
+
|
|
239
|
+
module.exports = StackCloneCommand;
|
|
@@ -1,255 +1,335 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
let
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
let
|
|
10
|
-
|
|
11
|
-
let
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
initial: true
|
|
20
|
-
}]
|
|
1
|
+
const ora = require('ora');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const inquirer = require('inquirer');
|
|
4
|
+
|
|
5
|
+
let sdkInstance = require('../../lib/util/contentstack-management-sdk');
|
|
6
|
+
let exportCmd = require('@contentstack/cli-cm-export');
|
|
7
|
+
let importCmd = require('@contentstack/cli-cm-import');
|
|
8
|
+
let client = {};
|
|
9
|
+
let config;
|
|
10
|
+
|
|
11
|
+
let stackCreationConfirmation = [
|
|
12
|
+
{
|
|
13
|
+
type: 'confirm',
|
|
14
|
+
name: 'stackCreate',
|
|
15
|
+
message: 'Want to clone content into a new stack ?',
|
|
16
|
+
initial: true,
|
|
17
|
+
},
|
|
18
|
+
];
|
|
21
19
|
|
|
22
20
|
let stackName = {
|
|
23
21
|
type: 'input',
|
|
24
22
|
name: 'stack',
|
|
23
|
+
default: 'ABC',
|
|
25
24
|
message: 'Enter name for the new stack to store the cloned content ?',
|
|
26
|
-
|
|
27
|
-
}
|
|
25
|
+
};
|
|
28
26
|
|
|
29
|
-
let orgUidList = {}
|
|
30
|
-
let stackUidList = {}
|
|
31
|
-
let masterLocaleList = {}
|
|
27
|
+
let orgUidList = {};
|
|
28
|
+
let stackUidList = {};
|
|
29
|
+
let masterLocaleList = {};
|
|
32
30
|
|
|
33
|
-
let structureList = [
|
|
31
|
+
let structureList = [
|
|
32
|
+
'locales',
|
|
34
33
|
'environments',
|
|
35
34
|
'extensions',
|
|
36
35
|
'webhooks',
|
|
37
36
|
'global-fields',
|
|
38
37
|
'content-types',
|
|
39
38
|
'workflows',
|
|
40
|
-
'labels'
|
|
41
|
-
|
|
42
|
-
let master_locale
|
|
39
|
+
'labels',
|
|
40
|
+
];
|
|
41
|
+
let master_locale;
|
|
43
42
|
|
|
44
43
|
class CloneHandler {
|
|
45
44
|
constructor(opt) {
|
|
46
|
-
config = opt
|
|
47
|
-
client = sdkInstance.Client(config)
|
|
45
|
+
config = opt;
|
|
46
|
+
client = sdkInstance.Client(config);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#handleOrgSelection(options = {}) {
|
|
50
|
+
return new Promise(async (resolve, reject) => {
|
|
51
|
+
const { msg = '', isSource = true } = options || {};
|
|
52
|
+
const orgList = await this.getOrganizationChoices(msg).catch(reject);
|
|
53
|
+
|
|
54
|
+
if (orgList) {
|
|
55
|
+
const orgSelected = await inquirer.prompt(orgList);
|
|
56
|
+
|
|
57
|
+
if (isSource) {
|
|
58
|
+
config.sourceOrg = orgUidList[orgSelected.Organization];
|
|
59
|
+
} else {
|
|
60
|
+
config.targetOrg = orgUidList[orgSelected.Organization];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
resolve(orgSelected);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
#handleStackSelection(options = {}) {
|
|
69
|
+
return new Promise(async (resolve, reject) => {
|
|
70
|
+
const { org = {}, msg = '', isSource = true } = options || {};
|
|
71
|
+
const stackList = await this.getStack(org, msg, isSource).catch(reject);
|
|
72
|
+
|
|
73
|
+
if (stackList) {
|
|
74
|
+
const selectedStack = await inquirer.prompt(stackList);
|
|
75
|
+
|
|
76
|
+
if (isSource) {
|
|
77
|
+
config.sourceStackName = selectedStack.stack;
|
|
78
|
+
master_locale = masterLocaleList[selectedStack.stack];
|
|
79
|
+
config.source_stack = stackUidList[selectedStack.stack];
|
|
80
|
+
} else {
|
|
81
|
+
config.target_stack = stackUidList[selectedStack.stack];
|
|
82
|
+
config.destinationStackName = selectedStack.stack;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
resolve(selectedStack);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
48
88
|
}
|
|
49
89
|
|
|
50
|
-
|
|
90
|
+
start() {
|
|
51
91
|
return new Promise(async (resolve, reject) => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
master_locale = masterLocaleList[stackSelected.stack]
|
|
65
|
-
config.sourceStackName = stackSelected.stack
|
|
66
|
-
stackName.default = "Copy of " + stackSelected.stack
|
|
67
|
-
let cmdExport = this.cmdExport()
|
|
68
|
-
cmdExport.then(async () => {
|
|
69
|
-
//Import section starts from here
|
|
70
|
-
var stackCreateConfirmation = await inquirer.prompt(stackCreationConfirmation)
|
|
71
|
-
if (stackCreateConfirmation.stackCreate !== true) {
|
|
72
|
-
oraMessage = 'Choose an organization where the destination stack exists: '
|
|
73
|
-
let orgdetails = this.getOrganizationChoices(oraMessage)
|
|
74
|
-
orgdetails
|
|
75
|
-
.then(async (orgList)=>{
|
|
76
|
-
var stackMessage = 'Choose the destination stack:'
|
|
77
|
-
var orgSelected = await inquirer.prompt(orgList)
|
|
78
|
-
let stackDetails = this.getStack(orgSelected, stackMessage)
|
|
79
|
-
stackDetails
|
|
80
|
-
.then(async (stackList)=> {
|
|
81
|
-
let stackSelected = await inquirer.prompt(stackList)
|
|
82
|
-
config.target_stack = stackUidList[stackSelected.stack]
|
|
83
|
-
config.destinationStackName = stackSelected.stack
|
|
84
|
-
this.cloneTypeSelection()
|
|
85
|
-
.then((msgData)=>{
|
|
86
|
-
return resolve(msgData)
|
|
87
|
-
}).catch((error)=>{
|
|
88
|
-
return reject(error.errorMessage)
|
|
89
|
-
})
|
|
90
|
-
}).catch((error) => {
|
|
91
|
-
return reject( error.errorMessage)
|
|
92
|
-
})
|
|
93
|
-
}).catch((error)=>{
|
|
94
|
-
return reject(error.errorMessage)
|
|
95
|
-
})
|
|
96
|
-
} else {
|
|
97
|
-
oraMessage = 'Choose an organization where you want to create a stack: '
|
|
98
|
-
let orgdetails = this.getOrganizationChoices(oraMessage)
|
|
99
|
-
orgdetails
|
|
100
|
-
.then(async (orgList)=>{
|
|
101
|
-
var orgSelected = await inquirer.prompt(orgList)
|
|
102
|
-
let orgUid = orgUidList[orgSelected.Organization]
|
|
103
|
-
this.createNewStack(orgUid)
|
|
104
|
-
.then(()=>{
|
|
105
|
-
this.cloneTypeSelection()
|
|
106
|
-
.then((msgData)=>{
|
|
107
|
-
return resolve(msgData)
|
|
108
|
-
}).catch((error) => {
|
|
109
|
-
return reject(error)
|
|
110
|
-
})
|
|
111
|
-
}).catch((error)=>{
|
|
112
|
-
return reject(error.errorMessage + ' Contact the Organization owner for Stack Creation access.')
|
|
113
|
-
})
|
|
114
|
-
}).catch((error) => {
|
|
115
|
-
return reject(error.errorMessage)
|
|
116
|
-
})
|
|
117
|
-
}
|
|
118
|
-
}).catch((error) => {
|
|
119
|
-
return reject(error)
|
|
120
|
-
})
|
|
92
|
+
let sourceStack = {};
|
|
93
|
+
const handleOrgAndStackSelection = (orgMsg, stackMsg, isSource = true) => {
|
|
94
|
+
return new Promise(async (_resolve) => {
|
|
95
|
+
const org = await this.#handleOrgSelection({ msg: orgMsg, isSource }).catch((error) =>
|
|
96
|
+
reject(error.errorMessage),
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
if (org) {
|
|
100
|
+
await this.#handleStackSelection({
|
|
101
|
+
org,
|
|
102
|
+
isSource,
|
|
103
|
+
msg: stackMsg,
|
|
121
104
|
})
|
|
122
|
-
|
|
123
|
-
|
|
105
|
+
.then(_resolve)
|
|
106
|
+
.catch((error) => reject(error.errorMessage));
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
if (!config.source_stack) {
|
|
112
|
+
// NOTE Export section
|
|
113
|
+
sourceStack = await handleOrgAndStackSelection(
|
|
114
|
+
'Choose an organization where your source stack exists:',
|
|
115
|
+
'Select the source stack',
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (config.source_stack) {
|
|
120
|
+
stackName.default = config.stackName || `Copy of ${sourceStack.stack || config.source_alias}`;
|
|
121
|
+
const exportRes = await this.cmdExport().catch(reject);
|
|
122
|
+
|
|
123
|
+
if (!config.sourceStackBranch) {
|
|
124
|
+
try {
|
|
125
|
+
const branches = await client.stack({ api_key: config.source_stack }).branch().query().find();
|
|
126
|
+
|
|
127
|
+
if (branches && branches.items && branches.items.length) {
|
|
128
|
+
config.sourceStackBranch = 'main';
|
|
129
|
+
}
|
|
130
|
+
} catch (_error) {}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// NOTE Import section
|
|
134
|
+
if (exportRes) {
|
|
135
|
+
let canCreateStack = false;
|
|
136
|
+
|
|
137
|
+
if (!config.target_stack) {
|
|
138
|
+
canCreateStack = await inquirer.prompt(stackCreationConfirmation);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (canCreateStack.stackCreate !== true) {
|
|
142
|
+
if (!config.target_stack) {
|
|
143
|
+
await handleOrgAndStackSelection(
|
|
144
|
+
'Choose an organization where the destination stack exists: ',
|
|
145
|
+
'Choose the destination stack:',
|
|
146
|
+
false,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (config.target_stack) {
|
|
151
|
+
this.cloneTypeSelection()
|
|
152
|
+
.then(resolve)
|
|
153
|
+
.catch((error) => reject(error.errorMessage));
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
const destinationOrg = await this.#handleOrgSelection({
|
|
157
|
+
isSource: false,
|
|
158
|
+
msg: 'Choose an organization where you want to create a stack: ',
|
|
159
|
+
}).catch((error) => reject(error.errorMessage));
|
|
160
|
+
const orgUid = orgUidList[destinationOrg.Organization];
|
|
161
|
+
await this.createNewStack(orgUid).catch((error) => {
|
|
162
|
+
return reject(error.errorMessage + ' Contact the Organization owner for Stack Creation access.');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
if (config.target_stack) {
|
|
166
|
+
this.cloneTypeSelection().then(resolve).catch(reject);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
});
|
|
124
172
|
}
|
|
125
173
|
|
|
126
|
-
getOrganizationChoices = async (
|
|
174
|
+
getOrganizationChoices = async (orgMessage) => {
|
|
127
175
|
let orgChoice = {
|
|
128
176
|
type: 'list',
|
|
129
177
|
name: 'Organization',
|
|
130
|
-
message:
|
|
178
|
+
message: orgMessage !== undefined ? orgMessage : 'Choose an organization',
|
|
131
179
|
choices: [],
|
|
132
|
-
}
|
|
180
|
+
};
|
|
133
181
|
return new Promise(async (resolve, reject) => {
|
|
134
|
-
const spinner = ora(
|
|
182
|
+
const spinner = ora('Fetching Organization').start();
|
|
135
183
|
try {
|
|
136
|
-
let organizations = await client.organization().fetchAll({ limit: 100 })
|
|
137
|
-
spinner.succeed(
|
|
184
|
+
let organizations = await client.organization().fetchAll({ limit: 100 });
|
|
185
|
+
spinner.succeed('Fetched Organization');
|
|
138
186
|
for (let i = 0; i < organizations.items.length; i++) {
|
|
139
|
-
orgUidList[organizations.items[i].name] = organizations.items[i].uid
|
|
140
|
-
orgChoice.choices.push(organizations.items[i].name)
|
|
187
|
+
orgUidList[organizations.items[i].name] = organizations.items[i].uid;
|
|
188
|
+
orgChoice.choices.push(organizations.items[i].name);
|
|
141
189
|
}
|
|
142
|
-
return resolve(orgChoice)
|
|
190
|
+
return resolve(orgChoice);
|
|
143
191
|
} catch (e) {
|
|
144
|
-
spinner.fail()
|
|
145
|
-
return reject(e)
|
|
192
|
+
spinner.fail();
|
|
193
|
+
return reject(e);
|
|
146
194
|
}
|
|
147
|
-
})
|
|
148
|
-
}
|
|
195
|
+
});
|
|
196
|
+
};
|
|
149
197
|
|
|
150
198
|
getStack = async (answer, stkMessage) => {
|
|
151
199
|
return new Promise(async (resolve, reject) => {
|
|
152
200
|
let stackChoice = {
|
|
153
201
|
type: 'list',
|
|
154
202
|
name: 'stack',
|
|
155
|
-
message: stkMessage,
|
|
156
|
-
message: stkMessage !== undefined ? stkMessage : "Select the stack",
|
|
203
|
+
message: stkMessage !== undefined ? stkMessage : 'Select the stack',
|
|
157
204
|
choices: [],
|
|
158
|
-
}
|
|
159
|
-
const spinner = ora('Fetching stacks').start()
|
|
205
|
+
};
|
|
206
|
+
const spinner = ora('Fetching stacks').start();
|
|
160
207
|
try {
|
|
161
|
-
|
|
162
|
-
|
|
208
|
+
const organization_uid = orgUidList[answer.Organization];
|
|
209
|
+
const stackList = client.stack().query({ organization_uid }).find();
|
|
163
210
|
stackList
|
|
164
|
-
.then(
|
|
211
|
+
.then((stacklist) => {
|
|
165
212
|
for (let j = 0; j < stacklist.items.length; j++) {
|
|
166
|
-
stackUidList[stacklist.items[j].name] = stacklist.items[j].api_key
|
|
167
|
-
masterLocaleList[stacklist.items[j].name] = stacklist.items[j].master_locale
|
|
168
|
-
stackChoice.choices.push(stacklist.items[j].name)
|
|
213
|
+
stackUidList[stacklist.items[j].name] = stacklist.items[j].api_key;
|
|
214
|
+
masterLocaleList[stacklist.items[j].name] = stacklist.items[j].master_locale;
|
|
215
|
+
stackChoice.choices.push(stacklist.items[j].name);
|
|
169
216
|
}
|
|
170
|
-
spinner.succeed(
|
|
171
|
-
return resolve(stackChoice)
|
|
172
|
-
}).catch(error => {
|
|
173
|
-
spinner.fail()
|
|
174
|
-
return reject(error)
|
|
217
|
+
spinner.succeed('Fetched stack');
|
|
218
|
+
return resolve(stackChoice);
|
|
175
219
|
})
|
|
220
|
+
.catch((error) => {
|
|
221
|
+
spinner.fail();
|
|
222
|
+
return reject(error);
|
|
223
|
+
});
|
|
176
224
|
} catch (e) {
|
|
177
|
-
spinner.fail()
|
|
178
|
-
return reject(e)
|
|
225
|
+
spinner.fail();
|
|
226
|
+
return reject(e);
|
|
179
227
|
}
|
|
180
|
-
})
|
|
181
|
-
}
|
|
228
|
+
});
|
|
229
|
+
};
|
|
182
230
|
|
|
183
231
|
async createNewStack(orgUid) {
|
|
184
232
|
return new Promise(async (resolve, reject) => {
|
|
185
|
-
let inputvalue
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
233
|
+
let inputvalue;
|
|
234
|
+
|
|
235
|
+
if (!config.stackName) {
|
|
236
|
+
inputvalue = await inquirer.prompt(stackName);
|
|
237
|
+
} else {
|
|
238
|
+
inputvalue = { stack: config.stackName };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let stack = { name: inputvalue.stack, master_locale: master_locale };
|
|
242
|
+
const spinner = ora('Creating New stack').start();
|
|
243
|
+
let newStack = client.stack().create({ stack }, { organization_uid: orgUid });
|
|
189
244
|
newStack
|
|
190
|
-
.then(result => {
|
|
191
|
-
spinner.succeed(
|
|
192
|
-
config.target_stack = result.api_key
|
|
193
|
-
config.destinationStackName = result.name
|
|
194
|
-
return resolve(result)
|
|
195
|
-
}).catch(error => {
|
|
196
|
-
spinner.fail()
|
|
197
|
-
return reject(error)
|
|
245
|
+
.then((result) => {
|
|
246
|
+
spinner.succeed('New Stack created Successfully name as ' + result.name);
|
|
247
|
+
config.target_stack = result.api_key;
|
|
248
|
+
config.destinationStackName = result.name;
|
|
249
|
+
return resolve(result);
|
|
198
250
|
})
|
|
199
|
-
|
|
251
|
+
.catch((error) => {
|
|
252
|
+
spinner.fail();
|
|
253
|
+
return reject(error);
|
|
254
|
+
});
|
|
255
|
+
});
|
|
200
256
|
}
|
|
201
257
|
|
|
202
258
|
async cloneTypeSelection() {
|
|
203
259
|
return new Promise(async (resolve, reject) => {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
260
|
+
const choices = [
|
|
261
|
+
'Structure (all modules except entries & assets)',
|
|
262
|
+
'Structure with content (all modules including entries & assets)',
|
|
263
|
+
];
|
|
264
|
+
const cloneTypeSelection = [
|
|
265
|
+
{
|
|
266
|
+
choices,
|
|
267
|
+
type: 'list',
|
|
268
|
+
name: 'type',
|
|
269
|
+
message: 'Choose the type of data to clone:',
|
|
270
|
+
},
|
|
271
|
+
];
|
|
272
|
+
let successMsg;
|
|
273
|
+
let selectedValue = {};
|
|
274
|
+
config['data'] = path.join(__dirname.split('src')[0], 'contents', config.sourceStackBranch || '');
|
|
275
|
+
|
|
276
|
+
if (!config.cloneType) {
|
|
277
|
+
selectedValue = await inquirer.prompt(cloneTypeSelection);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (config.cloneType === 'a' || selectedValue.type === 'Structure (all modules except entries & assets)') {
|
|
281
|
+
config['modules'] = structureList;
|
|
282
|
+
successMsg = 'Stack clone Structure completed';
|
|
223
283
|
} else {
|
|
224
|
-
|
|
225
|
-
cmdImport.then(() => {
|
|
226
|
-
return resolve("Stack clone completed with structure and content")
|
|
227
|
-
}).catch((error) => {
|
|
228
|
-
return reject(error)
|
|
229
|
-
})
|
|
284
|
+
successMsg = 'Stack clone completed with structure and content';
|
|
230
285
|
}
|
|
231
|
-
|
|
286
|
+
|
|
287
|
+
this.cmdImport()
|
|
288
|
+
.then(() => resolve(successMsg))
|
|
289
|
+
.catch(reject);
|
|
290
|
+
});
|
|
232
291
|
}
|
|
233
292
|
|
|
234
293
|
async cmdExport() {
|
|
235
294
|
return new Promise((resolve, reject) => {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
295
|
+
const cmd = ['-k', config.source_stack, '-d', __dirname.split('src')[0] + 'contents'];
|
|
296
|
+
|
|
297
|
+
if (config.source_alias) {
|
|
298
|
+
cmd.push('-a', config.source_alias);
|
|
299
|
+
}
|
|
300
|
+
if (config.sourceStackBranch) {
|
|
301
|
+
cmd.push('--branch', config.sourceStackBranch);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
let exportData = exportCmd.run(cmd);
|
|
305
|
+
exportData.then(() => resolve(true)).catch(reject);
|
|
306
|
+
});
|
|
243
307
|
}
|
|
244
308
|
|
|
245
309
|
async cmdImport() {
|
|
246
|
-
return new Promise(async (resolve,
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
310
|
+
return new Promise(async (resolve, _reject) => {
|
|
311
|
+
const cmd = ['-c', path.join(__dirname, 'dummyConfig.json')];
|
|
312
|
+
|
|
313
|
+
if (config.destination_alias) {
|
|
314
|
+
cmd.push('-a', config.destination_alias);
|
|
315
|
+
}
|
|
316
|
+
if (config.sourceStackBranch) {
|
|
317
|
+
cmd.push('-d', path.join(__dirname, config.sourceStackBranch));
|
|
318
|
+
}
|
|
319
|
+
if (config.targetStackBranch) {
|
|
320
|
+
cmd.push('--branch', config.targetStackBranch);
|
|
321
|
+
}
|
|
322
|
+
if (config.importWebhookStatus) {
|
|
323
|
+
cmd.push('--import-webhook-status', config.importWebhookStatus);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
await importCmd.run(cmd);
|
|
327
|
+
return resolve();
|
|
328
|
+
});
|
|
250
329
|
}
|
|
251
330
|
}
|
|
252
331
|
|
|
253
332
|
module.exports = {
|
|
254
|
-
CloneHandler,
|
|
255
|
-
|
|
333
|
+
CloneHandler,
|
|
334
|
+
client,
|
|
335
|
+
};
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
const contentstacksdk = require('@contentstack/management')
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
exports.Client = function (config) {
|
|
1
|
+
const contentstacksdk = require('@contentstack/management');
|
|
5
2
|
|
|
3
|
+
exports.Client = function (config) {
|
|
6
4
|
const option = {
|
|
7
5
|
host: config.host,
|
|
8
6
|
authtoken: config.auth_token,
|
|
9
7
|
maxContentLength: 100000000,
|
|
10
8
|
maxBodyLength: 1000000000,
|
|
11
|
-
logHandler: (
|
|
12
|
-
|
|
9
|
+
logHandler: (_level, _data) => {
|
|
10
|
+
// empty log handler
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
13
|
|
|
14
14
|
if (config.target_stack) {
|
|
15
|
-
option.api_key = config.target_stack
|
|
15
|
+
option.api_key = config.target_stack;
|
|
16
16
|
}
|
|
17
17
|
if (config.source_stack) {
|
|
18
|
-
option.api_key = config.source_stack
|
|
18
|
+
option.api_key = config.source_stack;
|
|
19
19
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
20
|
+
return contentstacksdk.client(option);
|
|
21
|
+
};
|
package/src/lib/util/log.js
CHANGED
|
@@ -9,15 +9,18 @@ var path = require('path');
|
|
|
9
9
|
var mkdirp = require('mkdirp');
|
|
10
10
|
var slice = Array.prototype.slice;
|
|
11
11
|
|
|
12
|
-
function returnString
|
|
12
|
+
function returnString(args) {
|
|
13
13
|
var returnStr = '';
|
|
14
14
|
if (args && args.length) {
|
|
15
|
-
returnStr = args
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
returnStr = args
|
|
16
|
+
.map(function (item) {
|
|
17
|
+
if (item && typeof item === 'object') {
|
|
18
|
+
return JSON.stringify(item);
|
|
19
|
+
}
|
|
20
|
+
return item;
|
|
21
|
+
})
|
|
22
|
+
.join(' ')
|
|
23
|
+
.trim();
|
|
21
24
|
}
|
|
22
25
|
return returnStr;
|
|
23
26
|
}
|
|
@@ -27,35 +30,37 @@ var myCustomLevels = {
|
|
|
27
30
|
error: 0,
|
|
28
31
|
warn: 1,
|
|
29
32
|
info: 2,
|
|
30
|
-
debug: 3
|
|
33
|
+
debug: 3,
|
|
31
34
|
},
|
|
32
35
|
colors: {
|
|
33
36
|
info: 'blue',
|
|
34
37
|
debug: 'green',
|
|
35
38
|
warn: 'yellow',
|
|
36
|
-
error: 'red'
|
|
37
|
-
}
|
|
39
|
+
error: 'red',
|
|
40
|
+
},
|
|
38
41
|
};
|
|
39
42
|
|
|
40
|
-
function init
|
|
41
|
-
var logsDir = path.resolve(_logPath, 'logs', 'import')
|
|
43
|
+
function init(_logPath, logfileName) {
|
|
44
|
+
var logsDir = path.resolve(_logPath, 'logs', 'import');
|
|
42
45
|
// Create dir if doesn't already exist
|
|
43
|
-
mkdirp.sync(logsDir)
|
|
46
|
+
mkdirp.sync(logsDir);
|
|
44
47
|
var logPath = path.join(logsDir, logfileName + '.log');
|
|
45
48
|
|
|
46
|
-
var transports = [
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
var transports = [
|
|
50
|
+
new winston.transports.File({
|
|
51
|
+
filename: logPath,
|
|
52
|
+
maxFiles: 20,
|
|
53
|
+
maxsize: 1000000,
|
|
54
|
+
tailable: true,
|
|
55
|
+
json: true,
|
|
56
|
+
}),
|
|
57
|
+
];
|
|
53
58
|
|
|
54
|
-
transports.push(new
|
|
59
|
+
transports.push(new winston.transports.Console());
|
|
55
60
|
|
|
56
|
-
var logger =
|
|
61
|
+
var logger = winston.createLogger({
|
|
57
62
|
transports: transports,
|
|
58
|
-
levels: myCustomLevels.levels
|
|
63
|
+
levels: myCustomLevels.levels,
|
|
59
64
|
});
|
|
60
65
|
|
|
61
66
|
return {
|
|
@@ -86,14 +91,14 @@ function init (_logPath, logfileName) {
|
|
|
86
91
|
if (logString) {
|
|
87
92
|
logger.log('debug', logString);
|
|
88
93
|
}
|
|
89
|
-
}
|
|
94
|
+
},
|
|
90
95
|
};
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
exports.addlogs = async (config, message, type) => {
|
|
94
99
|
if (type !== 'error') {
|
|
95
|
-
init(config.oldPath, type).log(message)
|
|
100
|
+
init(config.oldPath, type).log(message);
|
|
96
101
|
} else {
|
|
97
|
-
init(config.oldPath, type).error(message)
|
|
102
|
+
init(config.oldPath, type).error(message);
|
|
98
103
|
}
|
|
99
|
-
}
|
|
104
|
+
};
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
const {Command} = require('@contentstack/cli-command')
|
|
2
|
-
const Configstore = require('configstore')
|
|
3
|
-
const credStore = new Configstore('contentstack_cli')
|
|
4
|
-
const {CloneHandler} = require('../../lib/util/clone-handler')
|
|
5
|
-
let config = require('../../lib/util/dummyConfig.json')
|
|
6
|
-
const path = require('path')
|
|
7
|
-
const rimraf = require('rimraf')
|
|
8
|
-
let pathdir = path.join(__dirname.split('src')[0], 'contents')
|
|
9
|
-
const { readdirSync } = require('fs')
|
|
10
|
-
|
|
11
|
-
class StackCloneCommand extends Command {
|
|
12
|
-
async run() {
|
|
13
|
-
try {
|
|
14
|
-
await this.removeContentDirIfNotEmptyBeforeClone(pathdir) // NOTE remove if folder not empty before clone
|
|
15
|
-
|
|
16
|
-
this.registerCleanupOnInterrupt(pathdir)
|
|
17
|
-
let _authToken = credStore.get('authtoken')
|
|
18
|
-
if (_authToken && _authToken !== undefined) {
|
|
19
|
-
config.auth_token = _authToken
|
|
20
|
-
config.host = this.cmaHost
|
|
21
|
-
config.cdn = this.cdaHost
|
|
22
|
-
const cloneHandler = new CloneHandler(config)
|
|
23
|
-
await cloneHandler.start()
|
|
24
|
-
let successMessage = 'Stack cloning process have been completed successfully'
|
|
25
|
-
await this.cleanUp(pathdir, successMessage)
|
|
26
|
-
} else {
|
|
27
|
-
console.log("AuthToken is not present in local drive, Hence use 'csdx auth:login' command for login");
|
|
28
|
-
}
|
|
29
|
-
} catch (error) {
|
|
30
|
-
await this.cleanUp(pathdir)
|
|
31
|
-
// eslint-disable-next-line no-console
|
|
32
|
-
console.log(error.message || error)
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async removeContentDirIfNotEmptyBeforeClone(dir) {
|
|
37
|
-
try {
|
|
38
|
-
const dirNotEmpty = readdirSync(dir).length
|
|
39
|
-
|
|
40
|
-
if (dirNotEmpty) {
|
|
41
|
-
await this.cleanUp(dir)
|
|
42
|
-
}
|
|
43
|
-
} catch (error) {
|
|
44
|
-
const omit = ['ENOENT'] // NOTE add emittable error codes in the array
|
|
45
|
-
|
|
46
|
-
if (!omit.includes(error.code)) {
|
|
47
|
-
console.log(error.message)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async cleanUp(pathDir, message) {
|
|
53
|
-
return new Promise(resolve => {
|
|
54
|
-
rimraf(pathDir, function (err) {
|
|
55
|
-
if (err)
|
|
56
|
-
throw err
|
|
57
|
-
if (message) {
|
|
58
|
-
// eslint-disable-next-line no-console
|
|
59
|
-
console.log(message)
|
|
60
|
-
}
|
|
61
|
-
resolve()
|
|
62
|
-
})
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
registerCleanupOnInterrupt(pathDir) {
|
|
67
|
-
const interrupt = ['SIGINT', 'SIGQUIT', 'SIGTERM']
|
|
68
|
-
const exceptions = ['unhandledRejection', 'uncaughtException']
|
|
69
|
-
|
|
70
|
-
const cleanUp = async (exitOrError = null) => {
|
|
71
|
-
// eslint-disable-next-line no-console
|
|
72
|
-
console.log('\nCleaning up')
|
|
73
|
-
await this.cleanUp(pathDir)
|
|
74
|
-
// eslint-disable-next-line no-console
|
|
75
|
-
console.log('done')
|
|
76
|
-
// eslint-disable-next-line no-process-exit
|
|
77
|
-
|
|
78
|
-
if (exitOrError instanceof Promise) {
|
|
79
|
-
exitOrError.catch(error => {
|
|
80
|
-
console.log(error && error.message || '')
|
|
81
|
-
})
|
|
82
|
-
} else if (exitOrError && exitOrError.message) {
|
|
83
|
-
console.log(exitOrError.message)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (exitOrError === true) process.exit()
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
exceptions.forEach(event => process.on(event, cleanUp))
|
|
90
|
-
interrupt.forEach(signal => process.on(signal, () => cleanUp(true)))
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
StackCloneCommand.description = `Clone data (structure or content or both) of a stack into another stack
|
|
95
|
-
Use this plugin to automate the process of cloning a stack in a few steps.
|
|
96
|
-
`
|
|
97
|
-
|
|
98
|
-
StackCloneCommand.examples = [
|
|
99
|
-
'csdx cm:stack-clone',
|
|
100
|
-
]
|
|
101
|
-
|
|
102
|
-
module.exports = StackCloneCommand
|