@contentstack/cli-cm-clone 2.0.0-beta.4 → 2.0.0-beta.6
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 +1 -1
- package/README.md +61 -2
- package/bin/run.cmd +3 -0
- package/bin/run.js +7 -0
- package/package.json +29 -15
- package/src/commands/cm/stacks/clone.js +0 -350
- 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
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ $ npm install -g @contentstack/cli-cm-clone
|
|
|
16
16
|
$ csdx COMMAND
|
|
17
17
|
running command...
|
|
18
18
|
$ csdx (--version)
|
|
19
|
-
@contentstack/cli-cm-clone/2.0.0-beta.
|
|
19
|
+
@contentstack/cli-cm-clone/2.0.0-beta.6 darwin-arm64 node-v24.12.0
|
|
20
20
|
$ csdx --help [COMMAND]
|
|
21
21
|
USAGE
|
|
22
22
|
$ csdx COMMAND
|
|
@@ -38,6 +38,62 @@ USAGE
|
|
|
38
38
|
|
|
39
39
|
<!-- commands -->
|
|
40
40
|
* [`csdx 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]`](#csdx-cmstacksclone---source-branch-value---target-branch-value---source-management-token-alias-value---destination-management-token-alias-value--n-value---type-ab---source-stack-api-key-value---destination-stack-api-key-value---import-webhook-status-disablecurrent)
|
|
41
|
+
* [`csdx 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]`](#csdx-cmstacksclone---source-branch-value---target-branch-value---source-management-token-alias-value---destination-management-token-alias-value--n-value---type-ab---source-stack-api-key-value---destination-stack-api-key-value---import-webhook-status-disablecurrent)
|
|
42
|
+
|
|
43
|
+
## `csdx 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]`
|
|
44
|
+
|
|
45
|
+
Clone data (structure/content or both) of a stack into another stack
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
USAGE
|
|
49
|
+
$ csdx cm:stack-clone cm:stacks:clone [--source-branch <value>] [--target-branch <value>]
|
|
50
|
+
[--source-management-token-alias <value>] [--destination-management-token-alias <value>] [-n <value>] [--type a|b]
|
|
51
|
+
[--source-stack-api-key <value>] [--destination-stack-api-key <value>] [--import-webhook-status disable|current]
|
|
52
|
+
|
|
53
|
+
FLAGS
|
|
54
|
+
-c, --config=<value> Path for the external configuration
|
|
55
|
+
-n, --stack-name=<value> Provide a name for the new stack to store the cloned content.
|
|
56
|
+
-y, --yes Force override all Marketplace prompts.
|
|
57
|
+
--destination-management-token-alias=<value> Destination management token alias.
|
|
58
|
+
--destination-stack-api-key=<value> Destination stack API key
|
|
59
|
+
--import-webhook-status=<option> [default: disable] [default: disable] (optional) The status of the
|
|
60
|
+
import webhook. <options: disable|current>
|
|
61
|
+
<options: disable|current>
|
|
62
|
+
--skip-audit (optional) Skips the audit fix that occurs during an import
|
|
63
|
+
operation.
|
|
64
|
+
--source-branch=<value> Branch of the source stack.
|
|
65
|
+
--source-branch-alias=<value> Alias of Branch of the source stack.
|
|
66
|
+
--source-management-token-alias=<value> Source management token alias.
|
|
67
|
+
--source-stack-api-key=<value> Source stack API key
|
|
68
|
+
--target-branch=<value> Branch of the target stack.
|
|
69
|
+
--target-branch-alias=<value> Alias of Branch of the target stack.
|
|
70
|
+
--type=<option> Type of data to clone. You can select option a or b.
|
|
71
|
+
a) Structure (all modules except entries & assets).
|
|
72
|
+
b) Structure with content (all modules including entries & assets).
|
|
73
|
+
|
|
74
|
+
<options: a|b>
|
|
75
|
+
|
|
76
|
+
DESCRIPTION
|
|
77
|
+
Clone data (structure/content or both) of a stack into another stack
|
|
78
|
+
Use this plugin to automate the process of cloning a stack in few steps.
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
ALIASES
|
|
82
|
+
$ csdx cm:stack-clone
|
|
83
|
+
|
|
84
|
+
EXAMPLES
|
|
85
|
+
$ csdx cm:stacks:clone
|
|
86
|
+
|
|
87
|
+
$ csdx cm:stacks:clone --source-branch <source-branch-name> --target-branch <target-branch-name> --yes
|
|
88
|
+
|
|
89
|
+
$ csdx cm:stacks:clone --source-stack-api-key <apiKey> --destination-stack-api-key <apiKey>
|
|
90
|
+
|
|
91
|
+
$ csdx cm:stacks:clone --source-management-token-alias <management token alias> --destination-management-token-alias <management token alias>
|
|
92
|
+
|
|
93
|
+
$ csdx cm:stacks:clone --source-branch --target-branch --source-management-token-alias <management token alias> --destination-management-token-alias <management token alias>
|
|
94
|
+
|
|
95
|
+
$ 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>
|
|
96
|
+
```
|
|
41
97
|
|
|
42
98
|
## `csdx 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]`
|
|
43
99
|
|
|
@@ -77,6 +133,9 @@ DESCRIPTION
|
|
|
77
133
|
Use this plugin to automate the process of cloning a stack in few steps.
|
|
78
134
|
|
|
79
135
|
|
|
136
|
+
ALIASES
|
|
137
|
+
$ csdx cm:stack-clone
|
|
138
|
+
|
|
80
139
|
EXAMPLES
|
|
81
140
|
$ csdx cm:stacks:clone
|
|
82
141
|
|
|
@@ -91,5 +150,5 @@ EXAMPLES
|
|
|
91
150
|
$ 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>
|
|
92
151
|
```
|
|
93
152
|
|
|
94
|
-
_See code: [src/commands/cm/stacks/clone.
|
|
153
|
+
_See code: [src/commands/cm/stacks/clone.ts](https://github.com/contentstack/cli/blob/main/packages/contentstack-clone/src/commands/cm/stacks/clone.ts)_
|
|
95
154
|
<!-- commandsstop -->
|
package/bin/run.cmd
ADDED
package/bin/run.js
ADDED
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentstack/cli-cm-clone",
|
|
3
3
|
"description": "Contentstack stack clone plugin",
|
|
4
|
-
"version": "2.0.0-beta.
|
|
4
|
+
"version": "2.0.0-beta.6",
|
|
5
5
|
"author": "Contentstack",
|
|
6
6
|
"bugs": "https://github.com/rohitmishra209/cli-cm-clone/issues",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@colors/colors": "^1.6.0",
|
|
9
|
-
"@contentstack/cli-cm-export": "~2.0.0-beta.
|
|
10
|
-
"@contentstack/cli-cm-import": "~2.0.0-beta.
|
|
11
|
-
"@contentstack/cli-command": "~1.7.
|
|
12
|
-
"@contentstack/cli-utilities": "~1.
|
|
9
|
+
"@contentstack/cli-cm-export": "~2.0.0-beta.5",
|
|
10
|
+
"@contentstack/cli-cm-import": "~2.0.0-beta.5",
|
|
11
|
+
"@contentstack/cli-command": "~1.7.2",
|
|
12
|
+
"@contentstack/cli-utilities": "~1.17.0",
|
|
13
13
|
"@oclif/core": "^4.3.0",
|
|
14
14
|
"@oclif/plugin-help": "^6.2.28",
|
|
15
15
|
"chalk": "^4.1.2",
|
|
@@ -22,21 +22,29 @@
|
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@oclif/test": "^4.1.13",
|
|
25
|
+
"@types/chai": "^4.3.0",
|
|
26
|
+
"@types/mocha": "^10.0.0",
|
|
27
|
+
"@types/node": "^14.18.63",
|
|
28
|
+
"@types/sinon": "^10.0.0",
|
|
29
|
+
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
|
25
30
|
"chai": "^4.5.0",
|
|
26
31
|
"eslint": "^8.57.1",
|
|
27
32
|
"eslint-config-oclif": "^6.0.62",
|
|
28
33
|
"mocha": "^10.8.2",
|
|
29
34
|
"nyc": "^15.1.0",
|
|
30
35
|
"oclif": "^4.17.46",
|
|
31
|
-
"sinon": "^
|
|
36
|
+
"sinon": "^21.0.1",
|
|
37
|
+
"ts-node": "^10.9.2",
|
|
38
|
+
"typescript": "^4.9.5"
|
|
32
39
|
},
|
|
33
40
|
"engines": {
|
|
34
41
|
"node": ">=14.0.0"
|
|
35
42
|
},
|
|
36
43
|
"files": [
|
|
44
|
+
"/bin",
|
|
45
|
+
"/lib",
|
|
37
46
|
"/npm-shrinkwrap.json",
|
|
38
|
-
"/oclif.manifest.json"
|
|
39
|
-
"/src"
|
|
47
|
+
"/oclif.manifest.json"
|
|
40
48
|
],
|
|
41
49
|
"homepage": "https://github.com/rohitmishra209/cli-cm-clone",
|
|
42
50
|
"keywords": [
|
|
@@ -45,23 +53,29 @@
|
|
|
45
53
|
"plugin"
|
|
46
54
|
],
|
|
47
55
|
"license": "MIT",
|
|
56
|
+
"main": "./lib/commands/cm/stacks/clone.js",
|
|
48
57
|
"oclif": {
|
|
49
|
-
"commands": "./
|
|
58
|
+
"commands": "./lib/commands",
|
|
50
59
|
"bin": "csdx",
|
|
51
60
|
"repositoryPrefix": "<%- repo %>/blob/main/packages/contentstack-clone/<%- commandPath %>"
|
|
52
61
|
},
|
|
53
62
|
"repository": "https://github.com/contentstack/cli",
|
|
54
63
|
"scripts": {
|
|
64
|
+
"build": "npm run clean && npm run compile",
|
|
65
|
+
"clean": "rm -rf ./lib ./node_modules tsconfig.build.tsbuildinfo",
|
|
66
|
+
"compile": "tsc -b tsconfig.json",
|
|
55
67
|
"postpack": "rm -f oclif.manifest.json",
|
|
56
|
-
"prepack": "oclif manifest && oclif readme",
|
|
57
|
-
"test": "nyc --reporter=
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
68
|
+
"prepack": "pnpm compile && oclif manifest && oclif readme",
|
|
69
|
+
"test:report": "tsc -p test && nyc --reporter=lcov --extension .ts mocha --forbid-only \"test/**/*.test.ts\" 2>&1 | grep -v 'ERR_INVALID_ARG_TYPE' ; npx nyc report --reporter=text-summary --reporter=text || true",
|
|
70
|
+
"pretest": "tsc -p test",
|
|
71
|
+
"test:unit": "nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\" 2>&1 | grep -v 'ERR_INVALID_ARG_TYPE' ; npx nyc report --reporter=text-summary --reporter=text || true",
|
|
72
|
+
"lint": "eslint src/**/*.ts",
|
|
73
|
+
"format": "eslint src/**/*.ts --fix",
|
|
74
|
+
"test:unit:report": "nyc --reporter=text --reporter=text-summary --extension .ts mocha --forbid-only \"test/**/*.test.ts\""
|
|
61
75
|
},
|
|
62
76
|
"csdxConfig": {
|
|
63
77
|
"shortCommandName": {
|
|
64
78
|
"cm:stacks:clone": "CLN"
|
|
65
79
|
}
|
|
66
80
|
}
|
|
67
|
-
}
|
|
81
|
+
}
|
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
const { Command } = require('@contentstack/cli-command');
|
|
2
|
-
const { configHandler, flags, isAuthenticated, managementSDKClient, log, handleAndLogError } = require('@contentstack/cli-utilities');
|
|
3
|
-
const { CloneHandler } = require('../../../lib/util/clone-handler');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { rimraf } = require('rimraf');
|
|
6
|
-
const merge = require('merge');
|
|
7
|
-
let pathdir = path.join(__dirname.split('src')[0], 'contents');
|
|
8
|
-
const { readdirSync, readFileSync } = require('fs');
|
|
9
|
-
let config = {};
|
|
10
|
-
|
|
11
|
-
class StackCloneCommand extends Command {
|
|
12
|
-
/**
|
|
13
|
-
* Determine authentication method based on user preference
|
|
14
|
-
*/
|
|
15
|
-
determineAuthenticationMethod(sourceManagementTokenAlias, destinationManagementTokenAlias) {
|
|
16
|
-
// Track authentication method
|
|
17
|
-
let authenticationMethod = 'unknown';
|
|
18
|
-
|
|
19
|
-
// Determine authentication method based on user preference
|
|
20
|
-
if (sourceManagementTokenAlias || destinationManagementTokenAlias) {
|
|
21
|
-
authenticationMethod = 'Management Token';
|
|
22
|
-
} else if (isAuthenticated()) {
|
|
23
|
-
// Check if user is authenticated via OAuth
|
|
24
|
-
const isOAuthUser = configHandler.get('authorisationType') === 'OAUTH' || false;
|
|
25
|
-
if (isOAuthUser) {
|
|
26
|
-
authenticationMethod = 'OAuth';
|
|
27
|
-
} else {
|
|
28
|
-
authenticationMethod = 'Basic Auth';
|
|
29
|
-
}
|
|
30
|
-
} else {
|
|
31
|
-
authenticationMethod = 'Basic Auth';
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return authenticationMethod;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Create clone context object for logging
|
|
39
|
-
*/
|
|
40
|
-
createCloneContext(authenticationMethod) {
|
|
41
|
-
return {
|
|
42
|
-
command: this.context?.info?.command || 'cm:stacks:clone',
|
|
43
|
-
module: 'clone',
|
|
44
|
-
email: configHandler.get('email') || '',
|
|
45
|
-
sessionId: this.context?.sessionId || '',
|
|
46
|
-
authenticationMethod: authenticationMethod || 'Basic Auth',
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async run() {
|
|
51
|
-
try {
|
|
52
|
-
let self = this;
|
|
53
|
-
const { flags: cloneCommandFlags } = await self.parse(StackCloneCommand);
|
|
54
|
-
const {
|
|
55
|
-
yes,
|
|
56
|
-
type: cloneType,
|
|
57
|
-
'stack-name': stackName,
|
|
58
|
-
'source-branch': sourceStackBranch,
|
|
59
|
-
'source-branch-alias': sourceStackBranchAlias,
|
|
60
|
-
'target-branch': targetStackBranch,
|
|
61
|
-
'target-branch-alias': targetStackBranchAlias,
|
|
62
|
-
'source-stack-api-key': sourceStackApiKey,
|
|
63
|
-
'destination-stack-api-key': destinationStackApiKey,
|
|
64
|
-
'source-management-token-alias': sourceManagementTokenAlias,
|
|
65
|
-
'destination-management-token-alias': destinationManagementTokenAlias,
|
|
66
|
-
'import-webhook-status': importWebhookStatus,
|
|
67
|
-
config: externalConfigPath,
|
|
68
|
-
} = cloneCommandFlags;
|
|
69
|
-
|
|
70
|
-
const handleClone = async () => {
|
|
71
|
-
const listOfTokens = configHandler.get('tokens');
|
|
72
|
-
const authenticationMethod = this.determineAuthenticationMethod(
|
|
73
|
-
sourceManagementTokenAlias,
|
|
74
|
-
destinationManagementTokenAlias,
|
|
75
|
-
);
|
|
76
|
-
const cloneContext = this.createCloneContext(authenticationMethod);
|
|
77
|
-
log.debug('Starting clone operation setup', cloneContext);
|
|
78
|
-
|
|
79
|
-
if (externalConfigPath) {
|
|
80
|
-
log.debug(`Loading external configuration from: ${externalConfigPath}`, cloneContext);
|
|
81
|
-
let externalConfig = readFileSync(externalConfigPath, 'utf-8');
|
|
82
|
-
externalConfig = JSON.parse(externalConfig);
|
|
83
|
-
config = merge.recursive(config, externalConfig);
|
|
84
|
-
}
|
|
85
|
-
config.forceStopMarketplaceAppsPrompt = yes;
|
|
86
|
-
config.skipAudit = cloneCommandFlags['skip-audit'];
|
|
87
|
-
log.debug('Clone configuration prepared', {
|
|
88
|
-
...cloneContext,
|
|
89
|
-
cloneType: config.cloneType,
|
|
90
|
-
skipAudit: config.skipAudit,
|
|
91
|
-
forceStopMarketplaceAppsPrompt: config.forceStopMarketplaceAppsPrompt
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
if (cloneType) {
|
|
95
|
-
config.cloneType = cloneType;
|
|
96
|
-
}
|
|
97
|
-
if (stackName) {
|
|
98
|
-
config.stackName = stackName;
|
|
99
|
-
}
|
|
100
|
-
if (sourceStackBranch) {
|
|
101
|
-
config.sourceStackBranch = sourceStackBranch;
|
|
102
|
-
}
|
|
103
|
-
if (sourceStackBranchAlias) {
|
|
104
|
-
config.sourceStackBranchAlias = sourceStackBranchAlias;
|
|
105
|
-
}
|
|
106
|
-
if (targetStackBranch) {
|
|
107
|
-
config.targetStackBranch = targetStackBranch;
|
|
108
|
-
}
|
|
109
|
-
if (targetStackBranchAlias) {
|
|
110
|
-
config.targetStackBranchAlias = targetStackBranchAlias;
|
|
111
|
-
}
|
|
112
|
-
if (sourceStackApiKey) {
|
|
113
|
-
config.source_stack = sourceStackApiKey;
|
|
114
|
-
}
|
|
115
|
-
if (destinationStackApiKey) {
|
|
116
|
-
config.target_stack = destinationStackApiKey;
|
|
117
|
-
}
|
|
118
|
-
if (sourceManagementTokenAlias && listOfTokens[sourceManagementTokenAlias]) {
|
|
119
|
-
config.source_alias = sourceManagementTokenAlias;
|
|
120
|
-
config.source_stack = listOfTokens[sourceManagementTokenAlias].apiKey;
|
|
121
|
-
log.debug(`Using source token alias: ${sourceManagementTokenAlias}`, cloneContext);
|
|
122
|
-
} else if (sourceManagementTokenAlias) {
|
|
123
|
-
log.warn(`Provided source token alias (${sourceManagementTokenAlias}) not found in your config.!`, cloneContext);
|
|
124
|
-
}
|
|
125
|
-
if (destinationManagementTokenAlias && listOfTokens[destinationManagementTokenAlias]) {
|
|
126
|
-
config.destination_alias = destinationManagementTokenAlias;
|
|
127
|
-
config.target_stack = listOfTokens[destinationManagementTokenAlias].apiKey;
|
|
128
|
-
log.debug(`Using destination token alias: ${destinationManagementTokenAlias}`, cloneContext);
|
|
129
|
-
} else if (destinationManagementTokenAlias) {
|
|
130
|
-
log.warn(
|
|
131
|
-
`Provided destination token alias (${destinationManagementTokenAlias}) not found in your config.!`,
|
|
132
|
-
cloneContext,
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
if (importWebhookStatus) {
|
|
136
|
-
config.importWebhookStatus = importWebhookStatus;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const managementAPIClient = await managementSDKClient(config);
|
|
140
|
-
log.debug('Management API client initialized successfully', cloneContext);
|
|
141
|
-
|
|
142
|
-
log.debug(`Content directory path: ${pathdir}`, cloneContext);
|
|
143
|
-
await this.removeContentDirIfNotEmptyBeforeClone(pathdir, cloneContext); // NOTE remove if folder not empty before clone
|
|
144
|
-
this.registerCleanupOnInterrupt(pathdir, cloneContext);
|
|
145
|
-
|
|
146
|
-
config.auth_token = configHandler.get('authtoken');
|
|
147
|
-
config.host = this.cmaHost;
|
|
148
|
-
config.cdn = this.cdaHost;
|
|
149
|
-
config.pathDir = pathdir;
|
|
150
|
-
config.cloneContext = cloneContext;
|
|
151
|
-
log.debug('Clone configuration finalized', cloneContext);
|
|
152
|
-
const cloneHandler = new CloneHandler(config);
|
|
153
|
-
cloneHandler.setClient(managementAPIClient);
|
|
154
|
-
log.debug('Starting clone operation', cloneContext);
|
|
155
|
-
cloneHandler.execute().catch((error) => {
|
|
156
|
-
handleAndLogError(error, cloneContext);
|
|
157
|
-
});
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
if (sourceManagementTokenAlias && destinationManagementTokenAlias) {
|
|
161
|
-
if (sourceStackBranch || targetStackBranch) {
|
|
162
|
-
if (isAuthenticated()) {
|
|
163
|
-
handleClone();
|
|
164
|
-
} else {
|
|
165
|
-
log.error('Log in to execute this command,csdx auth:login', cloneContext);
|
|
166
|
-
this.exit(1);
|
|
167
|
-
}
|
|
168
|
-
} else {
|
|
169
|
-
handleClone();
|
|
170
|
-
}
|
|
171
|
-
} else if (isAuthenticated()) {
|
|
172
|
-
handleClone();
|
|
173
|
-
} else {
|
|
174
|
-
log.error('Please login to execute this command, csdx auth:login', cloneContext);
|
|
175
|
-
this.exit(1);
|
|
176
|
-
}
|
|
177
|
-
} catch (error) {
|
|
178
|
-
if (error) {
|
|
179
|
-
await this.cleanUp(pathdir, null, cloneContext);
|
|
180
|
-
log.error('Stack clone command failed', { ...cloneContext, error: error?.message || error });
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
async removeContentDirIfNotEmptyBeforeClone(dir, cloneContext) {
|
|
188
|
-
try {
|
|
189
|
-
log.debug('Checking if content directory is empty', { ...cloneContext, dir });
|
|
190
|
-
const dirNotEmpty = readdirSync(dir).length;
|
|
191
|
-
|
|
192
|
-
if (dirNotEmpty) {
|
|
193
|
-
log.debug('Content directory is not empty, cleaning up', { ...cloneContext, dir });
|
|
194
|
-
await this.cleanUp(dir, null, cloneContext);
|
|
195
|
-
}
|
|
196
|
-
} catch (error) {
|
|
197
|
-
const omit = ['ENOENT']; // NOTE add emittable error codes in the array
|
|
198
|
-
|
|
199
|
-
if (!omit.includes(error.code)) {
|
|
200
|
-
log.error('Error checking content directory', { ...cloneContext, error: error?.message, code: error.code });
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async cleanUp(pathDir, message, cloneContext) {
|
|
206
|
-
try {
|
|
207
|
-
log.debug('Starting cleanup', { ...cloneContext, pathDir });
|
|
208
|
-
await rimraf(pathDir);
|
|
209
|
-
if (message) {
|
|
210
|
-
log.info(message, cloneContext);
|
|
211
|
-
}
|
|
212
|
-
log.debug('Cleanup completed', { ...cloneContext, pathDir });
|
|
213
|
-
} catch (err) {
|
|
214
|
-
if (err) {
|
|
215
|
-
log.debug('Cleaning up', cloneContext);
|
|
216
|
-
const skipCodeArr = ['ENOENT', 'EBUSY', 'EPERM', 'EMFILE', 'ENOTEMPTY'];
|
|
217
|
-
|
|
218
|
-
if (skipCodeArr.includes(err.code)) {
|
|
219
|
-
log.debug('Cleanup error code is in skip list, exiting', { ...cloneContext, code: err?.code });
|
|
220
|
-
process.exit();
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
registerCleanupOnInterrupt(pathDir, cloneContext) {
|
|
227
|
-
const interrupt = ['SIGINT', 'SIGQUIT', 'SIGTERM'];
|
|
228
|
-
const exceptions = ['unhandledRejection', 'uncaughtException'];
|
|
229
|
-
|
|
230
|
-
const cleanUp = async (exitOrError) => {
|
|
231
|
-
if (exitOrError) {
|
|
232
|
-
log.debug('Cleaning up on interrupt', cloneContext);
|
|
233
|
-
await this.cleanUp(pathDir, null, cloneContext);
|
|
234
|
-
log.info('Cleanup done', cloneContext);
|
|
235
|
-
|
|
236
|
-
if (exitOrError instanceof Promise) {
|
|
237
|
-
exitOrError.catch((error) => {
|
|
238
|
-
log.error('Error during cleanup', { ...cloneContext, error: (error && error?.message) || '' });
|
|
239
|
-
});
|
|
240
|
-
} else if (exitOrError.message) {
|
|
241
|
-
log.error('Cleanup error', { ...cloneContext, error: exitOrError?.message });
|
|
242
|
-
} else if (exitOrError.errorMessage) {
|
|
243
|
-
log.error('Cleanup error', { ...cloneContext, error: exitOrError?.errorMessage });
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (exitOrError === true) process.exit();
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
exceptions.forEach((event) => process.on(event, cleanUp));
|
|
251
|
-
interrupt.forEach((signal) => process.on(signal, () => cleanUp(true)));
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
StackCloneCommand.description = `Clone data (structure/content or both) of a stack into another stack
|
|
256
|
-
Use this plugin to automate the process of cloning a stack in few steps.
|
|
257
|
-
`;
|
|
258
|
-
|
|
259
|
-
StackCloneCommand.examples = [
|
|
260
|
-
'csdx cm:stacks:clone',
|
|
261
|
-
'csdx cm:stacks:clone --source-branch <source-branch-name> --target-branch <target-branch-name> --yes',
|
|
262
|
-
'csdx cm:stacks:clone --source-stack-api-key <apiKey> --destination-stack-api-key <apiKey>',
|
|
263
|
-
'csdx cm:stacks:clone --source-management-token-alias <management token alias> --destination-management-token-alias <management token alias>',
|
|
264
|
-
'csdx cm:stacks:clone --source-branch --target-branch --source-management-token-alias <management token alias> --destination-management-token-alias <management token alias>',
|
|
265
|
-
'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>',
|
|
266
|
-
];
|
|
267
|
-
|
|
268
|
-
StackCloneCommand.aliases = [];
|
|
269
|
-
|
|
270
|
-
StackCloneCommand.flags = {
|
|
271
|
-
'source-branch': flags.string({
|
|
272
|
-
required: false,
|
|
273
|
-
multiple: false,
|
|
274
|
-
description: 'Branch of the source stack.',
|
|
275
|
-
exclusive: ['source-branch-alias'],
|
|
276
|
-
}),
|
|
277
|
-
'source-branch-alias': flags.string({
|
|
278
|
-
required: false,
|
|
279
|
-
multiple: false,
|
|
280
|
-
description: 'Alias of Branch of the source stack.',
|
|
281
|
-
exclusive: ['source-branch'],
|
|
282
|
-
}),
|
|
283
|
-
'target-branch': flags.string({
|
|
284
|
-
required: false,
|
|
285
|
-
multiple: false,
|
|
286
|
-
description: 'Branch of the target stack.',
|
|
287
|
-
exclusive: ['target-branch-alias'],
|
|
288
|
-
}),
|
|
289
|
-
'target-branch-alias': flags.string({
|
|
290
|
-
required: false,
|
|
291
|
-
multiple: false,
|
|
292
|
-
description: 'Alias of Branch of the target stack.',
|
|
293
|
-
exclusive: ['target-branch'],
|
|
294
|
-
}),
|
|
295
|
-
'source-management-token-alias': flags.string({
|
|
296
|
-
required: false,
|
|
297
|
-
multiple: false,
|
|
298
|
-
description: 'Source management token alias.',
|
|
299
|
-
}),
|
|
300
|
-
'destination-management-token-alias': flags.string({
|
|
301
|
-
required: false,
|
|
302
|
-
multiple: false,
|
|
303
|
-
description: 'Destination management token alias.',
|
|
304
|
-
}),
|
|
305
|
-
'stack-name': flags.string({
|
|
306
|
-
char: 'n',
|
|
307
|
-
required: false,
|
|
308
|
-
multiple: false,
|
|
309
|
-
description: 'Provide a name for the new stack to store the cloned content.',
|
|
310
|
-
}),
|
|
311
|
-
type: flags.string({
|
|
312
|
-
required: false,
|
|
313
|
-
multiple: false,
|
|
314
|
-
options: ['a', 'b'],
|
|
315
|
-
description: ` Type of data to clone. You can select option a or b.
|
|
316
|
-
a) Structure (all modules except entries & assets).
|
|
317
|
-
b) Structure with content (all modules including entries & assets).
|
|
318
|
-
`,
|
|
319
|
-
}),
|
|
320
|
-
'source-stack-api-key': flags.string({
|
|
321
|
-
description: 'Source stack API key',
|
|
322
|
-
}),
|
|
323
|
-
'destination-stack-api-key': flags.string({
|
|
324
|
-
description: 'Destination stack API key',
|
|
325
|
-
}),
|
|
326
|
-
'import-webhook-status': flags.string({
|
|
327
|
-
description: '[default: disable] (optional) The status of the import webhook. <options: disable|current>',
|
|
328
|
-
options: ['disable', 'current'],
|
|
329
|
-
required: false,
|
|
330
|
-
default: 'disable',
|
|
331
|
-
}),
|
|
332
|
-
yes: flags.boolean({
|
|
333
|
-
char: 'y',
|
|
334
|
-
required: false,
|
|
335
|
-
description: 'Force override all Marketplace prompts.',
|
|
336
|
-
}),
|
|
337
|
-
'skip-audit': flags.boolean({
|
|
338
|
-
description: ' (optional) Skips the audit fix that occurs during an import operation.',
|
|
339
|
-
}),
|
|
340
|
-
config: flags.string({
|
|
341
|
-
char: 'c',
|
|
342
|
-
required: false,
|
|
343
|
-
description: 'Path for the external configuration',
|
|
344
|
-
}),
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
StackCloneCommand.usage =
|
|
348
|
-
'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]';
|
|
349
|
-
|
|
350
|
-
module.exports = StackCloneCommand;
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
const CloneCommand = function (execute, undo, params, parentContext) {
|
|
2
|
-
this.execute = execute.bind(parentContext);
|
|
3
|
-
this.undo = undo && undo.bind(parentContext);
|
|
4
|
-
this.params = params;
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
const HandleOrgCommand = function (params, parentContext) {
|
|
8
|
-
return new CloneCommand(parentContext.handleOrgSelection, null, params, parentContext);
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const HandleStackCommand = function (params, parentContext) {
|
|
12
|
-
return new CloneCommand(parentContext.handleStackSelection, parentContext.execute, params, parentContext);
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const HandleBranchCommand = function (params, parentContext, backStepHandler) {
|
|
16
|
-
return new CloneCommand(parentContext.handleBranchSelection, backStepHandler, params, parentContext);
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const HandleDestinationStackCommand = function (params, parentContext) {
|
|
20
|
-
return new CloneCommand(parentContext.handleStackSelection, parentContext.executeDestination, params, parentContext);
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const HandleExportCommand = function (params, parentContext) {
|
|
24
|
-
return new CloneCommand(parentContext.cmdExport, null, params, parentContext);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const SetBranchCommand = function (params, parentContext) {
|
|
28
|
-
return new CloneCommand(parentContext.setBranch, null, params, parentContext);
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const CreateNewStackCommand = function (params, parentContext) {
|
|
32
|
-
return new CloneCommand(parentContext.createNewStack, parentContext.executeDestination, params, parentContext);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const CloneTypeSelectionCommand = function (params, parentContext) {
|
|
36
|
-
return new CloneCommand(parentContext.cloneTypeSelection, null, params, parentContext);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const Clone = function () {
|
|
40
|
-
const commands = [];
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
execute: async function (command) {
|
|
44
|
-
commands.push(command);
|
|
45
|
-
const result = await command.execute(command.params);
|
|
46
|
-
return result;
|
|
47
|
-
},
|
|
48
|
-
undo: async function () {
|
|
49
|
-
if (commands.length) {
|
|
50
|
-
const command = commands.pop();
|
|
51
|
-
command.undo && await command.undo(command.params);
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
module.exports = {
|
|
58
|
-
HandleOrgCommand,
|
|
59
|
-
HandleStackCommand,
|
|
60
|
-
HandleBranchCommand,
|
|
61
|
-
HandleDestinationStackCommand,
|
|
62
|
-
HandleExportCommand,
|
|
63
|
-
SetBranchCommand,
|
|
64
|
-
CreateNewStackCommand,
|
|
65
|
-
CloneTypeSelectionCommand,
|
|
66
|
-
Clone,
|
|
67
|
-
};
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { EventEmitter } = require('events');
|
|
4
|
-
|
|
5
|
-
class CustomAbortSignal {
|
|
6
|
-
constructor() {
|
|
7
|
-
this.eventEmitter = new EventEmitter();
|
|
8
|
-
this.onabort = null;
|
|
9
|
-
this.aborted = false;
|
|
10
|
-
}
|
|
11
|
-
toString() {
|
|
12
|
-
return '[object CustomAbortSignal]';
|
|
13
|
-
}
|
|
14
|
-
get [Symbol.toStringTag]() {
|
|
15
|
-
return 'CustomAbortSignal';
|
|
16
|
-
}
|
|
17
|
-
removeEventListener(name, handler) {
|
|
18
|
-
this.eventEmitter.removeListener(name, handler);
|
|
19
|
-
}
|
|
20
|
-
addEventListener(name, handler) {
|
|
21
|
-
this.eventEmitter.on(name, handler);
|
|
22
|
-
}
|
|
23
|
-
dispatchEvent(type) {
|
|
24
|
-
const event = { type, target: this };
|
|
25
|
-
const handlerName = `on${type}`;
|
|
26
|
-
|
|
27
|
-
if (typeof this[handlerName] === 'function') this[handlerName](event);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
class CustomAbortController {
|
|
32
|
-
constructor() {
|
|
33
|
-
this.signal = new CustomAbortSignal();
|
|
34
|
-
}
|
|
35
|
-
abort() {
|
|
36
|
-
if (this.signal.aborted) return;
|
|
37
|
-
|
|
38
|
-
this.signal.aborted = true;
|
|
39
|
-
this.signal.dispatchEvent('abort');
|
|
40
|
-
}
|
|
41
|
-
toString() {
|
|
42
|
-
return '[object CustomAbortController]';
|
|
43
|
-
}
|
|
44
|
-
get [Symbol.toStringTag]() {
|
|
45
|
-
return 'CustomAbortController';
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
module.exports = { CustomAbortController, CustomAbortSignal };
|