mcdev 3.1.3 → 4.0.1
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/.eslintrc.json +67 -7
- package/.github/ISSUE_TEMPLATE/bug.yml +2 -1
- package/.github/PULL_REQUEST_TEMPLATE.md +5 -3
- package/.github/dependabot.yml +14 -0
- package/.github/workflows/code-analysis.yml +57 -0
- package/.husky/commit-msg +10 -0
- package/.husky/post-checkout +5 -0
- package/.husky/pre-commit +2 -1
- package/.prettierrc +8 -0
- package/.vscode/settings.json +1 -1
- package/LICENSE +2 -2
- package/README.md +134 -45
- package/boilerplate/config.json +5 -11
- package/boilerplate/files/.prettierrc +8 -0
- package/boilerplate/files/.vscode/extensions.json +0 -1
- package/boilerplate/files/.vscode/settings.json +30 -2
- package/boilerplate/files/README.md +2 -2
- package/boilerplate/forcedUpdates.json +10 -0
- package/boilerplate/npm-dependencies.json +5 -5
- package/docs/dist/documentation.md +2807 -1730
- package/jsconfig.json +1 -1
- package/lib/Builder.js +171 -74
- package/lib/Deployer.js +244 -96
- package/lib/MetadataTypeDefinitions.js +2 -0
- package/lib/MetadataTypeInfo.js +2 -0
- package/lib/Retriever.js +61 -84
- package/lib/cli.js +116 -11
- package/lib/index.js +241 -561
- package/lib/metadataTypes/AccountUser.js +117 -103
- package/lib/metadataTypes/Asset.js +705 -255
- package/lib/metadataTypes/AttributeGroup.js +23 -12
- package/lib/metadataTypes/Automation.js +489 -392
- package/lib/metadataTypes/Campaign.js +33 -93
- package/lib/metadataTypes/ContentArea.js +31 -11
- package/lib/metadataTypes/DataExtension.js +387 -372
- package/lib/metadataTypes/DataExtensionField.js +131 -54
- package/lib/metadataTypes/DataExtensionTemplate.js +22 -4
- package/lib/metadataTypes/DataExtract.js +61 -48
- package/lib/metadataTypes/DataExtractType.js +14 -8
- package/lib/metadataTypes/Discovery.js +21 -16
- package/lib/metadataTypes/Email.js +32 -12
- package/lib/metadataTypes/EmailSendDefinition.js +85 -80
- package/lib/metadataTypes/EventDefinition.js +61 -43
- package/lib/metadataTypes/FileTransfer.js +72 -52
- package/lib/metadataTypes/Filter.js +11 -4
- package/lib/metadataTypes/Folder.js +149 -117
- package/lib/metadataTypes/FtpLocation.js +14 -8
- package/lib/metadataTypes/ImportFile.js +61 -64
- package/lib/metadataTypes/Interaction.js +19 -4
- package/lib/metadataTypes/List.js +54 -13
- package/lib/metadataTypes/MetadataType.js +664 -454
- package/lib/metadataTypes/MobileCode.js +46 -0
- package/lib/metadataTypes/MobileKeyword.js +114 -0
- package/lib/metadataTypes/Query.js +206 -105
- package/lib/metadataTypes/Role.js +76 -61
- package/lib/metadataTypes/Script.js +147 -83
- package/lib/metadataTypes/SetDefinition.js +20 -8
- package/lib/metadataTypes/TriggeredSendDefinition.js +78 -58
- package/lib/metadataTypes/definitions/Asset.definition.js +21 -10
- package/lib/metadataTypes/definitions/AttributeGroup.definition.js +12 -0
- package/lib/metadataTypes/definitions/Automation.definition.js +10 -5
- package/lib/metadataTypes/definitions/Campaign.definition.js +44 -1
- package/lib/metadataTypes/definitions/DataExtension.definition.js +4 -0
- package/lib/metadataTypes/definitions/DataExtensionTemplate.definition.js +6 -0
- package/lib/metadataTypes/definitions/DataExtract.definition.js +18 -14
- package/lib/metadataTypes/definitions/Discovery.definition.js +12 -0
- package/lib/metadataTypes/definitions/EmailSendDefinition.definition.js +4 -0
- package/lib/metadataTypes/definitions/EventDefinition.definition.js +22 -0
- package/lib/metadataTypes/definitions/FileTransfer.definition.js +4 -0
- package/lib/metadataTypes/definitions/Filter.definition.js +4 -0
- package/lib/metadataTypes/definitions/Folder.definition.js +6 -0
- package/lib/metadataTypes/definitions/FtpLocation.definition.js +4 -0
- package/lib/metadataTypes/definitions/ImportFile.definition.js +10 -5
- package/lib/metadataTypes/definitions/Interaction.definition.js +4 -0
- package/lib/metadataTypes/definitions/MobileCode.definition.js +163 -0
- package/lib/metadataTypes/definitions/MobileKeyword.definition.js +253 -0
- package/lib/metadataTypes/definitions/Query.definition.js +4 -0
- package/lib/metadataTypes/definitions/Role.definition.js +5 -0
- package/lib/metadataTypes/definitions/Script.definition.js +4 -0
- package/lib/metadataTypes/definitions/SetDefinition.definition.js +28 -0
- package/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js +4 -0
- package/lib/retrieveChangelog.js +7 -6
- package/lib/util/auth.js +117 -0
- package/lib/util/businessUnit.js +55 -66
- package/lib/util/cache.js +194 -0
- package/lib/util/cli.js +90 -116
- package/lib/util/config.js +302 -0
- package/lib/util/devops.js +250 -50
- package/lib/util/file.js +141 -201
- package/lib/util/init.config.js +208 -75
- package/lib/util/init.git.js +45 -50
- package/lib/util/init.js +72 -59
- package/lib/util/init.npm.js +48 -39
- package/lib/util/util.js +280 -564
- package/package.json +45 -34
- package/test/dataExtension.test.js +152 -0
- package/test/mockRoot/.mcdev-auth.json +8 -0
- package/test/mockRoot/.mcdevrc.json +67 -0
- package/test/mockRoot/deploy/testInstance/testBU/dataExtension/childBU_dataextension_test.dataExtension-meta.json +39 -0
- package/test/mockRoot/deploy/testInstance/testBU/dataExtension/testDataExtension.dataExtension-meta.json +23 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.json +11 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.sql +4 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.json +11 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.sql +4 -0
- package/test/query.test.js +149 -0
- package/test/resourceFactory.js +142 -0
- package/test/resources/1111111/dataFolder/retrieve-response.xml +43 -0
- package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json +18 -0
- package/test/resources/9999999/automation/v1/queries/get-response.json +24 -0
- package/test/resources/9999999/automation/v1/queries/post-response.json +18 -0
- package/test/resources/9999999/dataExtension/build-expected.json +51 -0
- package/test/resources/9999999/dataExtension/create-expected.json +23 -0
- package/test/resources/9999999/dataExtension/create-response.xml +54 -0
- package/test/resources/9999999/dataExtension/retrieve-expected.json +51 -0
- package/test/resources/9999999/dataExtension/retrieve-response.xml +47 -0
- package/test/resources/9999999/dataExtension/template-expected.json +51 -0
- package/test/resources/9999999/dataExtension/update-expected.json +55 -0
- package/test/resources/9999999/dataExtension/update-response.xml +52 -0
- package/test/resources/9999999/dataExtensionField/retrieve-response.xml +93 -0
- package/test/resources/9999999/dataExtensionTemplate/retrieve-response.xml +303 -0
- package/test/resources/9999999/dataFolder/retrieve-response.xml +65 -0
- package/test/resources/9999999/query/build-expected.json +8 -0
- package/test/resources/9999999/query/get-expected.json +11 -0
- package/test/resources/9999999/query/patch-expected.json +11 -0
- package/test/resources/9999999/query/post-expected.json +11 -0
- package/test/resources/9999999/query/template-expected.json +8 -0
- package/test/resources/auth.json +32 -0
- package/test/resources/rest404-response.json +5 -0
- package/test/resources/retrieve-response.xml +21 -0
- package/test/utils.js +107 -0
- package/types/mcdev.d.js +301 -0
- package/CHANGELOG.md +0 -126
- package/PULL_REQUEST_TEMPLATE.md +0 -19
- package/test/util/file.js +0 -51
package/lib/util/init.git.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
2
|
+
const TYPE = require('../../types/mcdev.d');
|
|
3
3
|
const File = require('./file');
|
|
4
4
|
const inquirer = require('inquirer');
|
|
5
5
|
const Util = require('./util');
|
|
6
6
|
const commandExists = require('command-exists');
|
|
7
|
-
const git = require('simple-git
|
|
7
|
+
const git = require('simple-git')();
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* CLI helper class
|
|
@@ -13,9 +13,9 @@ const git = require('simple-git/promise')();
|
|
|
13
13
|
const Init = {
|
|
14
14
|
/**
|
|
15
15
|
* check if git repo exists and otherwise create one
|
|
16
|
-
*
|
|
17
|
-
* @param {
|
|
18
|
-
* @returns {Promise
|
|
16
|
+
*
|
|
17
|
+
* @param {TYPE.skipInteraction} [skipInteraction] signals what to insert automatically for things usually asked via wizard
|
|
18
|
+
* @returns {Promise.<{status: string, repoName: string}>} success flag
|
|
19
19
|
*/
|
|
20
20
|
async initGitRepo(skipInteraction) {
|
|
21
21
|
const result = { status: null, repoName: null };
|
|
@@ -23,13 +23,13 @@ const Init = {
|
|
|
23
23
|
if (!commandExists.sync('git')) {
|
|
24
24
|
Util.logger.error('Git installation not found.');
|
|
25
25
|
Util.logger.error(
|
|
26
|
-
'Please follow our tutorial on installing Git: https://
|
|
26
|
+
'Please follow our tutorial on installing Git: https://github.com/Accenture/sfmc-devtools#212-install-the-git-command-line'
|
|
27
27
|
);
|
|
28
28
|
result.status = 'error';
|
|
29
29
|
return result;
|
|
30
30
|
}
|
|
31
31
|
// 3. test if in git repo
|
|
32
|
-
const gitRepoFoundInCWD = File.
|
|
32
|
+
const gitRepoFoundInCWD = await File.pathExists('.git');
|
|
33
33
|
let newRepoInitialized = null;
|
|
34
34
|
if (gitRepoFoundInCWD) {
|
|
35
35
|
Util.logger.info(`✔️ Git repository found`);
|
|
@@ -37,7 +37,7 @@ const Init = {
|
|
|
37
37
|
} else {
|
|
38
38
|
Util.logger.warn('No Git repository found. Initializing git:');
|
|
39
39
|
Util.execSync('git', ['init']);
|
|
40
|
-
if (File.
|
|
40
|
+
if (await File.pathExists('.git')) {
|
|
41
41
|
newRepoInitialized = true;
|
|
42
42
|
} else {
|
|
43
43
|
Util.logger.error(
|
|
@@ -50,7 +50,7 @@ const Init = {
|
|
|
50
50
|
Util.logger.info('Ensuring long file paths are not causing issues with git:');
|
|
51
51
|
try {
|
|
52
52
|
Util.execSync('git', ['config', '--local', 'core.longpaths', 'true']);
|
|
53
|
-
} catch
|
|
53
|
+
} catch {
|
|
54
54
|
Util.logger.warn(
|
|
55
55
|
`Updating your git config failed. We recommend running the above command manually yourself to avoid issues.`
|
|
56
56
|
);
|
|
@@ -58,7 +58,7 @@ const Init = {
|
|
|
58
58
|
Util.logger.info('Ensuring checkout (git pull) as-is and commit Unix-style line endings:');
|
|
59
59
|
try {
|
|
60
60
|
Util.execSync('git', ['config', '--local', 'core.autocrlf', 'input']);
|
|
61
|
-
} catch
|
|
61
|
+
} catch {
|
|
62
62
|
Util.logger.warn(
|
|
63
63
|
`Updating your git config failed. We recommend running the above command manually yourself to avoid issues.`
|
|
64
64
|
);
|
|
@@ -77,7 +77,8 @@ const Init = {
|
|
|
77
77
|
},
|
|
78
78
|
/**
|
|
79
79
|
* offer to push the new repo straight to the server
|
|
80
|
-
*
|
|
80
|
+
*
|
|
81
|
+
* @param {boolean | TYPE.skipInteraction} [skipInteraction] signals what to insert automatically for things usually asked via wizard
|
|
81
82
|
* @returns {void}
|
|
82
83
|
*/
|
|
83
84
|
async gitPush(skipInteraction) {
|
|
@@ -101,19 +102,14 @@ const Init = {
|
|
|
101
102
|
);
|
|
102
103
|
let responses;
|
|
103
104
|
if (!skipInteraction) {
|
|
104
|
-
|
|
105
|
+
responses = await inquirer.prompt([
|
|
105
106
|
{
|
|
106
107
|
type: 'confirm',
|
|
107
108
|
name: 'gitPush',
|
|
108
109
|
message: `Would you like to 'push' your backup to the remote Git repo?`,
|
|
109
110
|
default: true,
|
|
110
111
|
},
|
|
111
|
-
];
|
|
112
|
-
responses = await new Promise((resolve) => {
|
|
113
|
-
inquirer.prompt(questions).then((answers) => {
|
|
114
|
-
resolve(answers);
|
|
115
|
-
});
|
|
116
|
-
});
|
|
112
|
+
]);
|
|
117
113
|
}
|
|
118
114
|
if (skipInteraction || responses.gitPush) {
|
|
119
115
|
Util.execSync('git', ['push', '-u', 'origin', 'master']);
|
|
@@ -127,34 +123,30 @@ const Init = {
|
|
|
127
123
|
},
|
|
128
124
|
/**
|
|
129
125
|
* offers to add the git remote origin
|
|
130
|
-
*
|
|
131
|
-
* @param {
|
|
132
|
-
* @returns {
|
|
126
|
+
*
|
|
127
|
+
* @param {TYPE.skipInteraction} [skipInteraction] signals what to insert automatically for things usually asked via wizard
|
|
128
|
+
* @returns {string} repo name (optionally)
|
|
133
129
|
*/
|
|
134
130
|
async _addGitRemote(skipInteraction) {
|
|
135
131
|
// #1 ask if the user wants to do it now
|
|
136
132
|
let responses;
|
|
137
133
|
if (!skipInteraction) {
|
|
138
|
-
|
|
134
|
+
responses = await inquirer.prompt([
|
|
139
135
|
{
|
|
140
136
|
type: 'confirm',
|
|
141
137
|
name: 'gitOriginKnown',
|
|
142
138
|
message: `Do you know the remote/clone URL of your Git repo (starts with ssh:// or http:// and ends on '.git')?`,
|
|
143
139
|
default: true,
|
|
144
140
|
},
|
|
145
|
-
];
|
|
146
|
-
responses = await new Promise((resolve) => {
|
|
147
|
-
inquirer.prompt(questions).then((answers) => {
|
|
148
|
-
resolve(answers);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
141
|
+
]);
|
|
151
142
|
}
|
|
152
143
|
if (skipInteraction || responses.gitOriginKnown) {
|
|
153
144
|
// #2 if yes, guide the user to input the right url
|
|
145
|
+
/* eslint-disable unicorn/prefer-ternary */
|
|
154
146
|
if (skipInteraction) {
|
|
155
147
|
responses = skipInteraction;
|
|
156
148
|
} else {
|
|
157
|
-
|
|
149
|
+
responses = await inquirer.prompt([
|
|
158
150
|
{
|
|
159
151
|
type: 'input',
|
|
160
152
|
name: 'gitRemoteUrl',
|
|
@@ -173,13 +165,10 @@ const Init = {
|
|
|
173
165
|
}
|
|
174
166
|
},
|
|
175
167
|
},
|
|
176
|
-
];
|
|
177
|
-
responses = await new Promise((resolve) => {
|
|
178
|
-
inquirer.prompt(questions).then((answers) => {
|
|
179
|
-
resolve(answers);
|
|
180
|
-
});
|
|
181
|
-
});
|
|
168
|
+
]);
|
|
182
169
|
}
|
|
170
|
+
/* eslint-enable unicorn/prefer-ternary */
|
|
171
|
+
|
|
183
172
|
responses.gitRemoteUrl = responses.gitRemoteUrl.trim();
|
|
184
173
|
Util.execSync('git', ['remote', 'add', 'origin', responses.gitRemoteUrl]);
|
|
185
174
|
return responses.gitRemoteUrl.split('/').pop().split('.')[0];
|
|
@@ -187,7 +176,8 @@ const Init = {
|
|
|
187
176
|
},
|
|
188
177
|
/**
|
|
189
178
|
* checks global config and ask to config the user info and then store it locally
|
|
190
|
-
*
|
|
179
|
+
*
|
|
180
|
+
* @param {boolean | TYPE.skipInteraction} [skipInteraction] signals what to insert automatically for things usually asked via wizard
|
|
191
181
|
* @returns {void}
|
|
192
182
|
*/
|
|
193
183
|
async _updateGitConfigUser(skipInteraction) {
|
|
@@ -196,19 +186,20 @@ const Init = {
|
|
|
196
186
|
`Please confirm your Git user name & email. It should be in the format 'FirstName LastName' and 'your.email@accenture.com'. The current (potentially wrong) values are provided as default. If correct, confirm with ENTER, otherwise please update:`
|
|
197
187
|
);
|
|
198
188
|
let responses;
|
|
189
|
+
/* eslint-disable unicorn/prefer-ternary */
|
|
199
190
|
if (skipInteraction) {
|
|
200
191
|
responses = {
|
|
201
192
|
name: gitUser['user.name'],
|
|
202
193
|
email: gitUser['user.email'],
|
|
203
194
|
};
|
|
204
195
|
} else {
|
|
205
|
-
|
|
196
|
+
responses = await inquirer.prompt([
|
|
206
197
|
{
|
|
207
198
|
type: 'input',
|
|
208
199
|
name: 'name',
|
|
209
200
|
message: 'Git user.name',
|
|
210
201
|
default: gitUser['user.name'] || null,
|
|
211
|
-
// eslint-disable-next-line require-jsdoc
|
|
202
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
212
203
|
validate: function (value) {
|
|
213
204
|
if (
|
|
214
205
|
!value ||
|
|
@@ -226,7 +217,7 @@ const Init = {
|
|
|
226
217
|
name: 'email',
|
|
227
218
|
message: 'Git user.email',
|
|
228
219
|
default: gitUser['user.email'] || null,
|
|
229
|
-
// eslint-disable-next-line require-jsdoc
|
|
220
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
230
221
|
validate: function (value) {
|
|
231
222
|
value = value.trim();
|
|
232
223
|
const regex =
|
|
@@ -237,24 +228,28 @@ const Init = {
|
|
|
237
228
|
return true;
|
|
238
229
|
},
|
|
239
230
|
},
|
|
240
|
-
];
|
|
241
|
-
responses = await new Promise((resolve) => {
|
|
242
|
-
inquirer.prompt(questions).then((answers) => {
|
|
243
|
-
resolve(answers);
|
|
244
|
-
});
|
|
245
|
-
});
|
|
231
|
+
]);
|
|
246
232
|
}
|
|
233
|
+
/* eslint-enable unicorn/prefer-ternary */
|
|
234
|
+
|
|
247
235
|
if (responses.name && responses.email) {
|
|
248
236
|
// name can contain spaces - wrap it in quotes
|
|
249
237
|
const name = `"${responses.name.trim()}"`;
|
|
250
238
|
const email = responses.email.trim();
|
|
251
|
-
|
|
252
|
-
|
|
239
|
+
try {
|
|
240
|
+
Util.execSync('git', ['config', '--local', 'user.name', name]);
|
|
241
|
+
Util.execSync('git', ['config', '--local', 'user.email', email]);
|
|
242
|
+
} catch (ex) {
|
|
243
|
+
// if project folder is not a git folder then using --local will lead to a fatal error
|
|
244
|
+
Util.logger.warn('- Could not update git user name and email');
|
|
245
|
+
Util.logger.debug(ex.message);
|
|
246
|
+
}
|
|
253
247
|
}
|
|
254
248
|
},
|
|
255
249
|
/**
|
|
256
250
|
* retrieves the global user.name and user.email values
|
|
257
|
-
*
|
|
251
|
+
*
|
|
252
|
+
* @returns {Promise.<{'user.name': string, 'user.email': string}>} user.name and user.email
|
|
258
253
|
*/
|
|
259
254
|
async _getGitConfigUser() {
|
|
260
255
|
const gitConfigs = await git.listConfig();
|
|
@@ -262,14 +257,14 @@ const Init = {
|
|
|
262
257
|
delete gitConfigs.values['.git/config'];
|
|
263
258
|
const result = {};
|
|
264
259
|
|
|
265
|
-
Object.keys(gitConfigs.values)
|
|
260
|
+
for (const file of Object.keys(gitConfigs.values)) {
|
|
266
261
|
if (gitConfigs.values[file]['user.name']) {
|
|
267
262
|
result['user.name'] = gitConfigs.values[file]['user.name'];
|
|
268
263
|
}
|
|
269
264
|
if (gitConfigs.values[file]['user.email']) {
|
|
270
265
|
result['user.email'] = gitConfigs.values[file]['user.email'];
|
|
271
266
|
}
|
|
272
|
-
}
|
|
267
|
+
}
|
|
273
268
|
if (!result['user.name'] || !result['user.email']) {
|
|
274
269
|
return null;
|
|
275
270
|
}
|
package/lib/util/init.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
2
|
+
const TYPE = require('../../types/mcdev.d');
|
|
3
3
|
const Cli = require('./cli');
|
|
4
4
|
const File = require('./file');
|
|
5
|
+
const config = require('./config');
|
|
5
6
|
const InitGit = require('./init.git');
|
|
6
7
|
const InitNpm = require('./init.npm');
|
|
7
8
|
const InitConfig = require('./init.config');
|
|
@@ -15,27 +16,27 @@ const Util = require('./util');
|
|
|
15
16
|
const Init = {
|
|
16
17
|
/**
|
|
17
18
|
* Creates template file for properties.json
|
|
18
|
-
*
|
|
19
|
-
* @param {
|
|
20
|
-
* @param {
|
|
21
|
-
* @param {
|
|
22
|
-
* @
|
|
23
|
-
* @param {String} skipInteraction.tenant client id of installed package
|
|
24
|
-
* @param {String} skipInteraction.credentialsName how you would like the credential to be named
|
|
25
|
-
* @param {String} skipInteraction.gitRemoteUrl URL of Git remote server
|
|
26
|
-
* @returns {Promise<void>} -
|
|
19
|
+
*
|
|
20
|
+
* @param {TYPE.Mcdevrc} properties config file's json
|
|
21
|
+
* @param {string} credentialName identifying name of the installed package / project
|
|
22
|
+
* @param {TYPE.skipInteraction} [skipInteraction] signals what to insert automatically for things usually asked via wizard
|
|
23
|
+
* @returns {Promise.<void>} -
|
|
27
24
|
*/
|
|
28
|
-
async initProject(properties,
|
|
25
|
+
async initProject(properties, credentialName, skipInteraction) {
|
|
26
|
+
if (!properties) {
|
|
27
|
+
// try to get cached properties because we return null in case of a crucial error
|
|
28
|
+
properties = config.properties;
|
|
29
|
+
}
|
|
29
30
|
const missingCredentials = this._getMissingCredentials(properties);
|
|
30
|
-
if (File.
|
|
31
|
+
if ((await File.pathExists(Util.configFileName)) && properties) {
|
|
31
32
|
// config exists
|
|
32
|
-
if (
|
|
33
|
-
Util.logger.info(`Updating credential '${
|
|
33
|
+
if (credentialName) {
|
|
34
|
+
Util.logger.info(`Updating credential '${credentialName}'`);
|
|
34
35
|
// update-credential mode
|
|
35
|
-
if (!properties.credentials[
|
|
36
|
-
Util.logger.error(`Could not find credential '${
|
|
36
|
+
if (!properties.credentials[credentialName]) {
|
|
37
|
+
Util.logger.error(`Could not find credential '${credentialName}'`);
|
|
37
38
|
const response = await Cli._selectBU(properties, null, true);
|
|
38
|
-
|
|
39
|
+
credentialName = response.credential;
|
|
39
40
|
}
|
|
40
41
|
let error;
|
|
41
42
|
do {
|
|
@@ -43,20 +44,20 @@ const Init = {
|
|
|
43
44
|
try {
|
|
44
45
|
const success = await Cli.updateCredential(
|
|
45
46
|
properties,
|
|
46
|
-
|
|
47
|
+
credentialName,
|
|
47
48
|
skipInteraction
|
|
48
49
|
);
|
|
49
50
|
if (success) {
|
|
50
|
-
Util.logger.info(`✔️ Credential '${
|
|
51
|
+
Util.logger.info(`✔️ Credential '${credentialName}' updated.`);
|
|
51
52
|
} else {
|
|
52
53
|
error = true;
|
|
53
54
|
}
|
|
54
|
-
} catch
|
|
55
|
+
} catch {
|
|
55
56
|
error = true;
|
|
56
57
|
}
|
|
57
58
|
} while (error && !skipInteraction);
|
|
58
59
|
Util.logger.debug('reloading config');
|
|
59
|
-
properties =
|
|
60
|
+
properties = await config.getProperties(true);
|
|
60
61
|
} else if (missingCredentials.length) {
|
|
61
62
|
// forced update-credential mode - user likely cloned repo and is missing mcdev-auth.json
|
|
62
63
|
Util.logger.warn(
|
|
@@ -81,12 +82,12 @@ const Init = {
|
|
|
81
82
|
} else {
|
|
82
83
|
error = true;
|
|
83
84
|
}
|
|
84
|
-
} catch
|
|
85
|
+
} catch {
|
|
85
86
|
error = true;
|
|
86
87
|
}
|
|
87
88
|
} while (error);
|
|
88
89
|
Util.logger.debug('reloading config');
|
|
89
|
-
properties =
|
|
90
|
+
properties = await config.getProperties(true);
|
|
90
91
|
}
|
|
91
92
|
Util.logger.info('✔️ All credentials updated.');
|
|
92
93
|
// assume node dependencies are not installed
|
|
@@ -100,34 +101,30 @@ const Init = {
|
|
|
100
101
|
let responses;
|
|
101
102
|
if (skipInteraction) {
|
|
102
103
|
if (
|
|
103
|
-
skipInteraction.
|
|
104
|
-
skipInteraction.
|
|
105
|
-
skipInteraction.
|
|
106
|
-
skipInteraction.
|
|
104
|
+
skipInteraction.client_id &&
|
|
105
|
+
skipInteraction.client_secret &&
|
|
106
|
+
skipInteraction.auth_url &&
|
|
107
|
+
skipInteraction.account_id &&
|
|
108
|
+
skipInteraction.credentialName
|
|
107
109
|
) {
|
|
108
110
|
// assume automated input; only option here is to add a new credential
|
|
109
|
-
// requires skipInteraction=={
|
|
111
|
+
// requires skipInteraction=={client_id,client_secret,auth_url,account_id,credentialName}
|
|
110
112
|
// will be checked inside of Cli.addExtraCredential()
|
|
111
113
|
Util.logger.info('Adding another credential');
|
|
112
114
|
} else {
|
|
113
115
|
throw new Error(
|
|
114
|
-
'--skipInteraction flag found but missing required input for
|
|
116
|
+
'--skipInteraction flag found but missing required input for client_id,client_secret,auth_url,account_id,credentialName'
|
|
115
117
|
);
|
|
116
118
|
}
|
|
117
119
|
} else {
|
|
118
|
-
|
|
120
|
+
responses = await inquirer.prompt([
|
|
119
121
|
{
|
|
120
122
|
type: 'confirm',
|
|
121
123
|
name: 'isAddCredential',
|
|
122
124
|
message: 'Do you want to add another credential instead?',
|
|
123
125
|
default: false,
|
|
124
126
|
},
|
|
125
|
-
];
|
|
126
|
-
responses = await new Promise((resolve) => {
|
|
127
|
-
inquirer.prompt(questions).then((answers) => {
|
|
128
|
-
resolve(answers);
|
|
129
|
-
});
|
|
130
|
-
});
|
|
127
|
+
]);
|
|
131
128
|
}
|
|
132
129
|
let credentialName;
|
|
133
130
|
if (skipInteraction || responses.isAddCredential) {
|
|
@@ -148,6 +145,7 @@ const Init = {
|
|
|
148
145
|
}
|
|
149
146
|
|
|
150
147
|
// set up IDE files and load npm dependencies
|
|
148
|
+
|
|
151
149
|
let status = await this.upgradeProject(properties, true, initGit.repoName);
|
|
152
150
|
if (!status) {
|
|
153
151
|
return;
|
|
@@ -182,27 +180,23 @@ const Init = {
|
|
|
182
180
|
},
|
|
183
181
|
/**
|
|
184
182
|
* helper for this.initProject()
|
|
185
|
-
*
|
|
186
|
-
* @param {
|
|
187
|
-
* @param {
|
|
188
|
-
* @
|
|
183
|
+
*
|
|
184
|
+
* @param {string} bu cred/bu or cred/* or *
|
|
185
|
+
* @param {string} gitStatus signals what state the git repo is in
|
|
186
|
+
* @param {boolean | TYPE.skipInteraction} [skipInteraction] signals what to insert automatically for things usually asked via wizard
|
|
187
|
+
* @returns {Promise.<void>} -
|
|
189
188
|
*/
|
|
190
189
|
async _downloadAllBUs(bu, gitStatus, skipInteraction) {
|
|
191
190
|
let responses;
|
|
192
191
|
if (!skipInteraction) {
|
|
193
|
-
|
|
192
|
+
responses = await inquirer.prompt([
|
|
194
193
|
{
|
|
195
194
|
type: 'confirm',
|
|
196
195
|
name: 'initialRetrieveAll',
|
|
197
196
|
message: 'Do you want to start downloading all Business Units (recommended)?',
|
|
198
197
|
default: true,
|
|
199
198
|
},
|
|
200
|
-
];
|
|
201
|
-
responses = await new Promise((resolve) => {
|
|
202
|
-
inquirer.prompt(questions).then((answers) => {
|
|
203
|
-
resolve(answers);
|
|
204
|
-
});
|
|
205
|
-
});
|
|
199
|
+
]);
|
|
206
200
|
}
|
|
207
201
|
if (skipInteraction || responses.initialRetrieveAll) {
|
|
208
202
|
Util.execSync('mcdev', ['retrieve', bu]);
|
|
@@ -226,14 +220,15 @@ const Init = {
|
|
|
226
220
|
},
|
|
227
221
|
/**
|
|
228
222
|
* wrapper around npm dependency & configuration file setup
|
|
229
|
-
*
|
|
230
|
-
* @param {
|
|
231
|
-
* @param {
|
|
232
|
-
* @
|
|
223
|
+
*
|
|
224
|
+
* @param {TYPE.Mcdevrc} properties config file's json
|
|
225
|
+
* @param {boolean} [initial] print message if not part of initial setup
|
|
226
|
+
* @param {string} [repoName] if git URL was provided earlier, the repo name was extracted to use it for npm init
|
|
227
|
+
* @returns {Promise.<boolean>} success flag
|
|
233
228
|
*/
|
|
234
229
|
async upgradeProject(properties, initial, repoName) {
|
|
235
230
|
let status;
|
|
236
|
-
|
|
231
|
+
const versionBeforeUpgrade = properties?.version || '0.0.0';
|
|
237
232
|
if (!initial) {
|
|
238
233
|
Util.logger.info(
|
|
239
234
|
'Upgrading project with newest configuration, npm dependencies & other project configurations:'
|
|
@@ -244,10 +239,15 @@ const Init = {
|
|
|
244
239
|
if (!status) {
|
|
245
240
|
return false;
|
|
246
241
|
}
|
|
242
|
+
// version 4 release to simplify auth
|
|
243
|
+
status = await InitConfig.upgradeAuthFile(properties);
|
|
244
|
+
if (!status) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
// create files before installing dependencies to ensure .gitignore is properly set up
|
|
250
|
-
status = await InitConfig.createIdeConfigFiles();
|
|
250
|
+
status = await InitConfig.createIdeConfigFiles(versionBeforeUpgrade);
|
|
251
251
|
if (!status) {
|
|
252
252
|
return false;
|
|
253
253
|
}
|
|
@@ -262,17 +262,30 @@ const Init = {
|
|
|
262
262
|
},
|
|
263
263
|
/**
|
|
264
264
|
* finds credentials that are set up in config but not in auth file
|
|
265
|
-
*
|
|
266
|
-
* @
|
|
265
|
+
*
|
|
266
|
+
* @param {TYPE.Mcdevrc} properties javascript object in .mcdevrc.json
|
|
267
|
+
* @returns {string[]} list of credential names
|
|
267
268
|
*/
|
|
268
269
|
_getMissingCredentials(properties) {
|
|
269
270
|
let missingCredentials;
|
|
270
|
-
if (properties
|
|
271
|
+
if (properties?.credentials) {
|
|
272
|
+
// reload auth file because for some reason we didnt want that in our main properties object
|
|
273
|
+
let auth;
|
|
274
|
+
try {
|
|
275
|
+
auth = File.readJsonSync(Util.authFileName);
|
|
276
|
+
} catch (ex) {
|
|
277
|
+
Util.logger.error(`${ex.code}: ${ex.message}`);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
// walk through config credentials and check if the matching credential in the auth file is missing something
|
|
271
281
|
missingCredentials = Object.keys(properties.credentials).filter(
|
|
272
282
|
(cred) =>
|
|
273
|
-
!
|
|
274
|
-
!
|
|
275
|
-
|
|
283
|
+
!auth[cred] ||
|
|
284
|
+
!auth[cred].account_id ||
|
|
285
|
+
properties.credentials[cred].eid != auth[cred].account_id ||
|
|
286
|
+
!auth[cred].client_id ||
|
|
287
|
+
!auth[cred].client_secret ||
|
|
288
|
+
!auth[cred].auth_url
|
|
276
289
|
);
|
|
277
290
|
}
|
|
278
291
|
return missingCredentials || [];
|
package/lib/util/init.npm.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const File = require('./file');
|
|
4
|
-
const
|
|
5
|
-
const path = require('path');
|
|
4
|
+
const path = require('node:path');
|
|
6
5
|
const Util = require('./util');
|
|
6
|
+
const semver = require('semver');
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* CLI helper class
|
|
@@ -14,15 +14,16 @@ const Init = {
|
|
|
14
14
|
* initiates npm project and then
|
|
15
15
|
* takes care of loading the pre-configured dependency list
|
|
16
16
|
* from the boilerplate directory to them as dev-dependencies
|
|
17
|
-
*
|
|
18
|
-
* @
|
|
17
|
+
*
|
|
18
|
+
* @param {string} [repoName] if git URL was provided earlier, the repo name was extracted to use it for npm init
|
|
19
|
+
* @returns {Promise.<boolean>} install successful or error occured
|
|
19
20
|
*/
|
|
20
21
|
async installDependencies(repoName) {
|
|
21
22
|
let fileContent;
|
|
22
23
|
let projectPackageJson;
|
|
23
|
-
if (File.
|
|
24
|
+
if (await File.pathExists('package.json')) {
|
|
24
25
|
try {
|
|
25
|
-
fileContent = File.
|
|
26
|
+
fileContent = await File.readFile('package.json', 'utf8');
|
|
26
27
|
} catch (ex) {
|
|
27
28
|
Util.logger.error(
|
|
28
29
|
'Your package.json was found but seems to be corrupted: ' + ex.message
|
|
@@ -55,7 +56,7 @@ const Init = {
|
|
|
55
56
|
projectPackageJson = JSON.parse(fileContent);
|
|
56
57
|
}
|
|
57
58
|
Util.logger.info('✔️ package.json created');
|
|
58
|
-
} catch
|
|
59
|
+
} catch {
|
|
59
60
|
Util.logger.error('No package.json found. Please run "npm init" manually');
|
|
60
61
|
return false;
|
|
61
62
|
}
|
|
@@ -67,55 +68,64 @@ const Init = {
|
|
|
67
68
|
Util.boilerplateDirectory,
|
|
68
69
|
'npm-dependencies.json'
|
|
69
70
|
);
|
|
70
|
-
if (!File.
|
|
71
|
+
if (!(await File.pathExists(dependencyFile))) {
|
|
71
72
|
Util.logger.debug(`Dependency file not found in ${dependencyFile}`);
|
|
72
73
|
return false;
|
|
73
74
|
}
|
|
74
|
-
const defaultDependencies = File.
|
|
75
|
+
const defaultDependencies = await File.readJson(dependencyFile);
|
|
76
|
+
const versionsDefault = {};
|
|
77
|
+
for (const name of defaultDependencies) {
|
|
78
|
+
// check mcdev.devDependencies first
|
|
79
|
+
versionsDefault[name] = Object.keys(Util.packageJsonMcdev.dependencies).includes(name)
|
|
80
|
+
? Util.packageJsonMcdev.dependencies[name]
|
|
81
|
+
: // then check mcdev.devDependencies
|
|
82
|
+
Object.keys(Util.packageJsonMcdev.devDependencies).includes(name)
|
|
83
|
+
? Util.packageJsonMcdev.devDependencies[name]
|
|
84
|
+
: // fallback to using latest version if not found
|
|
85
|
+
'latest';
|
|
86
|
+
}
|
|
75
87
|
|
|
88
|
+
const versionsProject = {};
|
|
89
|
+
if (projectPackageJson.devDependencies) {
|
|
90
|
+
for (const name of defaultDependencies) {
|
|
91
|
+
// check project.devDependencies
|
|
92
|
+
versionsProject[name] = Object.keys(projectPackageJson.devDependencies).includes(
|
|
93
|
+
name
|
|
94
|
+
)
|
|
95
|
+
? projectPackageJson.devDependencies[name].replace(/^[\^~]/, '')
|
|
96
|
+
: // fallback to invalid version if not found
|
|
97
|
+
'0.0.0';
|
|
98
|
+
}
|
|
99
|
+
}
|
|
76
100
|
const loadDependencies = defaultDependencies.filter(
|
|
77
101
|
(name) =>
|
|
78
102
|
!projectPackageJson ||
|
|
79
103
|
!projectPackageJson.devDependencies ||
|
|
80
|
-
!projectPackageJson.devDependencies[name]
|
|
104
|
+
!projectPackageJson.devDependencies[name] ||
|
|
105
|
+
versionsDefault[name] == 'latest' ||
|
|
106
|
+
semver.gt(versionsDefault[name], versionsProject[name])
|
|
81
107
|
);
|
|
82
|
-
if (loadDependencies.length < defaultDependencies.length) {
|
|
83
|
-
Util.logger.info(
|
|
84
|
-
`✔️ ${
|
|
85
|
-
!loadDependencies.length ? 'All' : 'Some'
|
|
86
|
-
} default dependencies are already installed: ` + defaultDependencies.join(', ')
|
|
87
|
-
);
|
|
88
|
-
const questions = [
|
|
89
|
-
{
|
|
90
|
-
type: 'confirm',
|
|
91
|
-
name: 'runUpdate',
|
|
92
|
-
message: 'Would you like to attempt updating them?',
|
|
93
|
-
default: true,
|
|
94
|
-
},
|
|
95
|
-
];
|
|
96
|
-
const responses = await new Promise((resolve) => {
|
|
97
|
-
inquirer.prompt(questions).then((answers) => {
|
|
98
|
-
resolve(answers);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
if (responses.runUpdate) {
|
|
102
|
-
loadDependencies.length = 0;
|
|
103
|
-
loadDependencies.push(...defaultDependencies);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
108
|
if (loadDependencies.length) {
|
|
107
|
-
Util.logger.info('Installing Dependencies:');
|
|
108
|
-
const args = ['install', '--save-dev'].concat(
|
|
109
|
+
Util.logger.info('Installing/Updating Dependencies:');
|
|
110
|
+
const args = ['install', '--save-dev'].concat(
|
|
111
|
+
loadDependencies.map((name) => `${name}@${versionsDefault[name]}`)
|
|
112
|
+
);
|
|
109
113
|
|
|
110
114
|
Util.execSync('npm', args);
|
|
111
115
|
Util.logger.info('✔️ Dependencies installed.');
|
|
116
|
+
} else {
|
|
117
|
+
Util.logger.info(
|
|
118
|
+
`✔️ All default dependencies are already installed: ` +
|
|
119
|
+
defaultDependencies.map((name) => `${name}@${versionsProject[name]}`).join(', ')
|
|
120
|
+
);
|
|
112
121
|
}
|
|
113
122
|
return true;
|
|
114
123
|
},
|
|
115
124
|
/**
|
|
116
125
|
* ensure we have certain default values in our config
|
|
117
|
-
*
|
|
118
|
-
* @
|
|
126
|
+
*
|
|
127
|
+
* @param {object} [currentContent] what was read from existing package.json file
|
|
128
|
+
* @returns {Promise.<{script: object, author: string, license: string}>} extended currentContent
|
|
119
129
|
*/
|
|
120
130
|
async _getDefaultPackageJson(currentContent) {
|
|
121
131
|
currentContent = currentContent || {};
|
|
@@ -124,7 +134,6 @@ const Init = {
|
|
|
124
134
|
build: 'sfmc-build all',
|
|
125
135
|
'build-cp': 'sfmc-build cloudPages',
|
|
126
136
|
'build-email': 'sfmc-build emails',
|
|
127
|
-
upgrade: 'npm-check --update',
|
|
128
137
|
'eslint-check': 'eslint',
|
|
129
138
|
};
|
|
130
139
|
if (!currentContent.scripts) {
|