mcdev 3.1.3 → 4.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/.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 +28 -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 +2795 -1724
- package/jsconfig.json +1 -1
- package/lib/Builder.js +166 -75
- 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 +101 -95
- package/lib/metadataTypes/Asset.js +677 -248
- package/lib/metadataTypes/AttributeGroup.js +23 -12
- package/lib/metadataTypes/Automation.js +451 -354
- 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 +668 -454
- package/lib/metadataTypes/MobileCode.js +46 -0
- package/lib/metadataTypes/MobileKeyword.js +114 -0
- package/lib/metadataTypes/Query.js +204 -103
- package/lib/metadataTypes/Role.js +76 -61
- package/lib/metadataTypes/Script.js +145 -81
- 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 +240 -50
- package/lib/util/file.js +120 -191
- package/lib/util/init.config.js +195 -69
- 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 +44 -33
- 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/devops.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
const TYPE = require('../../types/mcdev.d');
|
|
1
2
|
const File = require('./file');
|
|
2
|
-
const path = require('path');
|
|
3
|
+
const path = require('node:path');
|
|
3
4
|
const inquirer = require('inquirer');
|
|
4
5
|
const Util = require('./util');
|
|
5
|
-
const git = require('simple-git
|
|
6
|
+
const git = require('simple-git')();
|
|
7
|
+
const Builder = require('../Builder');
|
|
6
8
|
const MetadataType = require('../MetadataTypeInfo');
|
|
7
9
|
const jsonToTable = require('json-to-table');
|
|
8
10
|
/**
|
|
@@ -13,25 +15,23 @@ const DevOps = {
|
|
|
13
15
|
/**
|
|
14
16
|
* Extracts the delta between a commit and the current state for deployment.
|
|
15
17
|
* Interactive commit selection if no commits are passed.
|
|
16
|
-
*
|
|
17
|
-
* @param {
|
|
18
|
+
*
|
|
19
|
+
* @param {TYPE.Mcdevrc} properties central properties object
|
|
20
|
+
* @param {string} [range] git commit range
|
|
18
21
|
* @param {boolean} [saveToDeployDir] if true, copy metadata changes into deploy directory
|
|
19
|
-
* @param {
|
|
20
|
-
* @returns {Promise
|
|
22
|
+
* @param {string} [filterPaths] filter file paths that start with any specified path (comma separated)
|
|
23
|
+
* @returns {Promise.<TYPE.DeltaPkgItem[]>} -
|
|
21
24
|
*/
|
|
22
|
-
async
|
|
25
|
+
async getDeltaList(properties, range, saveToDeployDir, filterPaths) {
|
|
23
26
|
const rangeUserInput = range;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} else {
|
|
33
|
-
filterPaths = [properties.directories.retrieve];
|
|
34
|
-
}
|
|
27
|
+
filterPaths = filterPaths
|
|
28
|
+
? filterPaths.split(',').map((filePath) =>
|
|
29
|
+
path
|
|
30
|
+
.normalize(properties.directories.retrieve + filePath)
|
|
31
|
+
.split('\\')
|
|
32
|
+
.join('/')
|
|
33
|
+
)
|
|
34
|
+
: [properties.directories.retrieve];
|
|
35
35
|
if (range) {
|
|
36
36
|
if (!range.includes('..')) {
|
|
37
37
|
// we limit the user here somewhat by always comparing to current branch if no range was given
|
|
@@ -54,7 +54,7 @@ const DevOps = {
|
|
|
54
54
|
}));
|
|
55
55
|
display.push(new inquirer.Separator(' ==== '));
|
|
56
56
|
|
|
57
|
-
const
|
|
57
|
+
const responses = await inquirer.prompt([
|
|
58
58
|
{
|
|
59
59
|
type: 'list',
|
|
60
60
|
message: 'Select base commit for comparison with current commit',
|
|
@@ -62,12 +62,7 @@ const DevOps = {
|
|
|
62
62
|
pageSize: 10,
|
|
63
63
|
choices: display,
|
|
64
64
|
},
|
|
65
|
-
];
|
|
66
|
-
const responses = await new Promise((resolve) => {
|
|
67
|
-
inquirer.prompt(questions).then((answers) => {
|
|
68
|
-
resolve(answers);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
65
|
+
]);
|
|
71
66
|
range = `${responses.commit}..HEAD`;
|
|
72
67
|
}
|
|
73
68
|
|
|
@@ -78,18 +73,22 @@ const DevOps = {
|
|
|
78
73
|
'add/update': 0,
|
|
79
74
|
move: 0,
|
|
80
75
|
};
|
|
76
|
+
/**
|
|
77
|
+
* @type {TYPE.DeltaPkgItem[]}
|
|
78
|
+
*/
|
|
81
79
|
const delta = (await git.diffSummary([range])).files
|
|
82
80
|
// populate additional info for all changed files
|
|
83
|
-
.map((file) => {
|
|
81
|
+
.map((/** @type {TYPE.DeltaPkgItem} */ file) => {
|
|
84
82
|
// If file was moved it's path needs to be parsed
|
|
85
83
|
file.moved = file.file.includes('=>');
|
|
86
84
|
if (file.moved) {
|
|
87
85
|
const p = file.file;
|
|
86
|
+
// TODO: rewrite remaining substring() to slice()
|
|
88
87
|
const paths = {
|
|
89
|
-
base: p.
|
|
90
|
-
before: p.substring(p.indexOf('{') + 1, p.indexOf('=>') - 1),
|
|
91
|
-
after: p.substring(p.indexOf('=>') + 3, p.indexOf('}')),
|
|
92
|
-
end: p.
|
|
88
|
+
base: p.slice(0, Math.max(0, p.indexOf('{'))),
|
|
89
|
+
before: p.substring(p.indexOf('{') + 1, p.indexOf('=>') - 1), // eslint-disable-line unicorn/prefer-string-slice
|
|
90
|
+
after: p.substring(p.indexOf('=>') + 3, p.indexOf('}')), // eslint-disable-line unicorn/prefer-string-slice
|
|
91
|
+
end: p.slice(Math.max(0, p.indexOf('}') + 1)),
|
|
93
92
|
};
|
|
94
93
|
file.fromPath = path
|
|
95
94
|
.normalize(`${paths.base}${paths.before}${paths.end}`)
|
|
@@ -113,7 +112,7 @@ const DevOps = {
|
|
|
113
112
|
// ! Filter happens after initial parse, because if file was moved its new path has to be calculated
|
|
114
113
|
.filter((file) => filterPaths.some((path) => file.file.startsWith(path)))
|
|
115
114
|
// ensure badly named files on unsupported metadata types are not in our subset
|
|
116
|
-
.filter((file) => {
|
|
115
|
+
.filter((/** @type {TYPE.DeltaPkgItem} */ file) => {
|
|
117
116
|
if (!MetadataType[file.type]) {
|
|
118
117
|
Util.logger.debug(
|
|
119
118
|
`Unknown metadata-type found for (${file.file}): ` + file.type
|
|
@@ -123,7 +122,7 @@ const DevOps = {
|
|
|
123
122
|
return true;
|
|
124
123
|
}
|
|
125
124
|
})
|
|
126
|
-
.map((file) => {
|
|
125
|
+
.map((/** @type {TYPE.DeltaPkgItem} */ file) => {
|
|
127
126
|
// Gets external key based on file name und the assumption that filename = externalKey
|
|
128
127
|
if (file.type === 'folder') {
|
|
129
128
|
file.externalKey = null;
|
|
@@ -135,7 +134,7 @@ const DevOps = {
|
|
|
135
134
|
|
|
136
135
|
// Check if file doesn't exist in reported path, that means it was a git deletion
|
|
137
136
|
// TODO: improve git action detection by switching from diffSummary to diff with --summary result parsing
|
|
138
|
-
if (!File.
|
|
137
|
+
if (!File.pathExistsSync(file.file)) {
|
|
139
138
|
file.gitAction = 'delete';
|
|
140
139
|
} else if (file.moved) {
|
|
141
140
|
file.gitAction = 'move';
|
|
@@ -189,8 +188,12 @@ const DevOps = {
|
|
|
189
188
|
return [];
|
|
190
189
|
}
|
|
191
190
|
// Write into delta.json to serve as documentation
|
|
192
|
-
File.
|
|
193
|
-
|
|
191
|
+
const directoryDeltaPkg = File.normalizePath([
|
|
192
|
+
properties.directories.docs,
|
|
193
|
+
'deltaPackage/',
|
|
194
|
+
]);
|
|
195
|
+
await File.writeJSONToFile(directoryDeltaPkg, 'delta_package', delta);
|
|
196
|
+
this.document(directoryDeltaPkg, delta);
|
|
194
197
|
Util.logger.info(
|
|
195
198
|
`- ✔️ Identified changes: Add/Update=${gitActionsCounter['add/update']}, Move=${gitActionsCounter['move']}, Delete=${gitActionsCounter['delete']}`
|
|
196
199
|
);
|
|
@@ -199,12 +202,11 @@ const DevOps = {
|
|
|
199
202
|
'Please note that deletions have to be done manually on the SFMC website.'
|
|
200
203
|
);
|
|
201
204
|
}
|
|
202
|
-
Util.logger.info(
|
|
203
|
-
`Saved report in ${path.join(properties.directories.deltaPackage, 'delta_package.md')}`
|
|
204
|
-
);
|
|
205
|
+
Util.logger.info(`Saved report in ${path.join(directoryDeltaPkg, 'delta_package.md')}`);
|
|
205
206
|
|
|
206
207
|
// Copy filtered list of files into deploy directory
|
|
207
208
|
if (saveToDeployDir) {
|
|
209
|
+
/** @type {TYPE.DeltaPkgItem} */
|
|
208
210
|
const copied = delta.map((file) =>
|
|
209
211
|
File.copyFile(
|
|
210
212
|
file.file,
|
|
@@ -229,42 +231,215 @@ const DevOps = {
|
|
|
229
231
|
} copied, ${skipped.length} skipped, ${failed.length} failed)`
|
|
230
232
|
);
|
|
231
233
|
if (skipped.length > 0) {
|
|
232
|
-
|
|
233
|
-
Util.logger.debug(`Skipped - ${file.statusMessage} - ${file.file}`)
|
|
234
|
-
|
|
234
|
+
for (const file of skipped) {
|
|
235
|
+
Util.logger.debug(`Skipped - ${file.statusMessage} - ${file.file}`);
|
|
236
|
+
}
|
|
235
237
|
}
|
|
236
238
|
if (failed.length > 0) {
|
|
237
|
-
|
|
238
|
-
Util.logger.error(`Failed - ${file.statusMessage} - ${file.file}`)
|
|
239
|
-
|
|
239
|
+
for (const file of failed) {
|
|
240
|
+
Util.logger.error(`Failed - ${file.statusMessage} - ${file.file}`);
|
|
241
|
+
}
|
|
240
242
|
}
|
|
241
243
|
}
|
|
242
244
|
return delta;
|
|
243
245
|
},
|
|
246
|
+
/**
|
|
247
|
+
* wrapper around DevOps.getDeltaList, Builder.buildTemplate and M
|
|
248
|
+
*
|
|
249
|
+
* @param {TYPE.Mcdevrc} properties project config file
|
|
250
|
+
* @param {string} range git commit range
|
|
251
|
+
* @param {TYPE.SkipInteraction} [skipInteraction] allows to skip interactive wizard
|
|
252
|
+
*/
|
|
253
|
+
async buildDeltaDefinitions(properties, range, skipInteraction) {
|
|
254
|
+
// check if sourceTargetMapping is valid
|
|
255
|
+
if (
|
|
256
|
+
!properties.options.deployment.sourceTargetMapping ||
|
|
257
|
+
!Object.keys(properties.options.deployment.sourceTargetMapping).length
|
|
258
|
+
) {
|
|
259
|
+
Util.logger.error('Bad configuration of options.deployment.sourceTargetMapping');
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const sourceMarketListArr = Object.keys(properties.options.deployment.sourceTargetMapping);
|
|
263
|
+
|
|
264
|
+
for (const sourceML of sourceMarketListArr) {
|
|
265
|
+
// check if sourceTargetMapping has valid values
|
|
266
|
+
// #1 check source marketlist
|
|
267
|
+
try {
|
|
268
|
+
Util.verifyMarketList(sourceML, properties);
|
|
269
|
+
// remove potentially existing "description"-entry
|
|
270
|
+
delete properties.marketList[sourceML].description;
|
|
271
|
+
|
|
272
|
+
const sourceMarketBuArr = Object.keys(properties.marketList[sourceML]);
|
|
273
|
+
if (sourceMarketBuArr.length !== 1) {
|
|
274
|
+
throw new Error('Only 1 BU is allowed per source marketList');
|
|
275
|
+
}
|
|
276
|
+
if ('string' !== typeof properties.marketList[sourceML][sourceMarketBuArr[0]]) {
|
|
277
|
+
throw new TypeError('Only 1 market per BU is allowed per source marketList');
|
|
278
|
+
}
|
|
279
|
+
} catch (ex) {
|
|
280
|
+
Util.logger.error('Deployment Source: ' + ex.message);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
// #2 check corresponding target marketList
|
|
284
|
+
let targetML;
|
|
285
|
+
try {
|
|
286
|
+
targetML = properties.options.deployment.sourceTargetMapping[sourceML];
|
|
287
|
+
if ('string' !== typeof targetML) {
|
|
288
|
+
throw new TypeError(
|
|
289
|
+
'Please define one target marketList per source in deployment.sourceTargetMapping (No arrays allowed)'
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
Util.verifyMarketList(targetML, properties);
|
|
293
|
+
// remove potentially existing "description"-entry
|
|
294
|
+
delete properties.marketList[targetML].description;
|
|
295
|
+
} catch (ex) {
|
|
296
|
+
Util.logger.error('Deployment Target: ' + ex.message);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// all good let's loop a second time for actual execution
|
|
300
|
+
for (const sourceMlName of sourceMarketListArr) {
|
|
301
|
+
const targetMlName = properties.options.deployment.sourceTargetMapping[sourceMlName];
|
|
302
|
+
const sourceBU = Object.keys(properties.marketList[sourceMlName])[0];
|
|
303
|
+
const sourceMarket = Object.values(properties.marketList[sourceMlName])[0];
|
|
304
|
+
|
|
305
|
+
const delta = await DevOps.getDeltaList(properties, range, false, sourceBU);
|
|
306
|
+
// If only chaing templating and buildDefinition if required
|
|
307
|
+
if (!delta || delta.length === 0) {
|
|
308
|
+
// info/error messages was printed by DevOps.createDeltaPkg() already
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
Util.logger.info('=============');
|
|
312
|
+
|
|
313
|
+
// Put files into maps. One map with BU -> type -> file (for retrieveAsTemplate)
|
|
314
|
+
// Other map only with type -> file (for buildDefinitionBulk)
|
|
315
|
+
const buTypeDelta = {}; // for bt, with BU info
|
|
316
|
+
const typeDelta = {}; // for bdb, without BU info - thats taken from the marketList
|
|
317
|
+
let deltaCounter = 0;
|
|
318
|
+
for (const file of delta
|
|
319
|
+
// Only template/build files that were added/updated/moved. no deletions
|
|
320
|
+
// ! doesn't work for folder, because their name parsing doesnt work at the moment
|
|
321
|
+
.filter((file) => file.gitAction !== 'delete' && file.name)) {
|
|
322
|
+
const buPath = `${file._credential}/${file._businessUnit}`;
|
|
323
|
+
if (!buTypeDelta[buPath]) {
|
|
324
|
+
// init object
|
|
325
|
+
buTypeDelta[buPath] = {};
|
|
326
|
+
}
|
|
327
|
+
if (!buTypeDelta[buPath][file.type]) {
|
|
328
|
+
// init array
|
|
329
|
+
buTypeDelta[buPath][file.type] = [];
|
|
330
|
+
}
|
|
331
|
+
buTypeDelta[buPath][file.type].push(file.externalKey);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Run buildTemplate for each business unit for each type
|
|
335
|
+
Util.logger.info('Retrieve template from Git delta');
|
|
336
|
+
// ! needs to be for (.. in ..) loop so that it gets executed in series
|
|
337
|
+
for (const bu in buTypeDelta) {
|
|
338
|
+
for (const type in buTypeDelta[bu]) {
|
|
339
|
+
// get unique list (original search might include more than one entry for types with docs or extracted code)
|
|
340
|
+
const keyArr = [...new Set(buTypeDelta[bu][type])];
|
|
341
|
+
Util.logger.info(
|
|
342
|
+
`⚡ mcdev bt ${bu} ${type} "${keyArr.join(',')}" ${sourceMarket}`
|
|
343
|
+
);
|
|
344
|
+
await Builder.buildTemplate(bu, type, keyArr, sourceMarket);
|
|
345
|
+
// ensure we have the right key for bd/bdb that matches the name used for rt
|
|
346
|
+
if (keyArr.length) {
|
|
347
|
+
if (!typeDelta[type]) {
|
|
348
|
+
// init array
|
|
349
|
+
typeDelta[type] = [];
|
|
350
|
+
}
|
|
351
|
+
typeDelta[type].push(...keyArr);
|
|
352
|
+
deltaCounter += keyArr.length;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (deltaCounter) {
|
|
357
|
+
Util.logger.info(`- ✔️ Templates created: ${deltaCounter}`);
|
|
358
|
+
} else {
|
|
359
|
+
Util.logger.error(
|
|
360
|
+
'- ❌ No Templates or Deploy Definitions created. Check if you expected no changes.'
|
|
361
|
+
);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Run build definitions bulk for each type
|
|
366
|
+
Util.logger.info('=============');
|
|
367
|
+
Util.logger.info('Build deploy definitions from delta templates');
|
|
368
|
+
if (
|
|
369
|
+
properties.directories.templateBuilds == properties.directories.deploy ||
|
|
370
|
+
(Array.isArray(properties.directories.templateBuilds) &&
|
|
371
|
+
properties.directories.templateBuilds.includes(properties.directories.deploy))
|
|
372
|
+
) {
|
|
373
|
+
let responses;
|
|
374
|
+
if (!skipInteraction) {
|
|
375
|
+
// deploy folder is in targets for definition creation
|
|
376
|
+
// recommend to purge their content first
|
|
377
|
+
responses = await inquirer.prompt([
|
|
378
|
+
{
|
|
379
|
+
type: 'confirm',
|
|
380
|
+
name: 'isPurgeDeployFolder',
|
|
381
|
+
message:
|
|
382
|
+
'Do you want to empty the deploy folder (ensures no files from previous deployments remain)?',
|
|
383
|
+
default: true,
|
|
384
|
+
},
|
|
385
|
+
]);
|
|
386
|
+
}
|
|
387
|
+
if (skipInteraction || responses.isPurgeDeployFolders) {
|
|
388
|
+
// Clear output folder structure for selected sub-type
|
|
389
|
+
await File.remove(File.normalizePath([properties.directories.deploy]));
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const bdPromises = [];
|
|
393
|
+
for (const type in typeDelta) {
|
|
394
|
+
Util.logger.info(
|
|
395
|
+
`⚡ mcdev bdb ${targetMlName} ${type} "${typeDelta[type].join(',')}"`
|
|
396
|
+
);
|
|
397
|
+
// omitting "await" to speed up creation
|
|
398
|
+
bdPromises.push(
|
|
399
|
+
Builder.buildDefinitionBulk(targetMlName, type, typeDelta[type].join(','))
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
await Promise.all(bdPromises);
|
|
403
|
+
Util.logger.info(`- ✔️ Deploy defintions created`);
|
|
404
|
+
if (
|
|
405
|
+
properties.directories.templateBuilds == properties.directories.deploy ||
|
|
406
|
+
(Array.isArray(properties.directories.templateBuilds) &&
|
|
407
|
+
properties.directories.templateBuilds.includes(properties.directories.deploy))
|
|
408
|
+
) {
|
|
409
|
+
Util.logger.info(`You can now run deploy on the prepared BUs`);
|
|
410
|
+
} else {
|
|
411
|
+
Util.logger.info(
|
|
412
|
+
`Your templated defintions are now ready to be copied into the deploy folder. Hint: You can have this auto-copied if you adjust directories.templateBuilds in your config.`
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
|
|
244
418
|
/**
|
|
245
419
|
* create markdown file for deployment listing
|
|
246
|
-
*
|
|
247
|
-
* @param {
|
|
420
|
+
*
|
|
421
|
+
* @param {string} directory -
|
|
422
|
+
* @param {object} jsonReport -
|
|
248
423
|
* @returns {void}
|
|
249
424
|
*/
|
|
250
425
|
document(directory, jsonReport) {
|
|
251
426
|
const tabled = jsonToTable(jsonReport);
|
|
252
427
|
let output = `# Deployment Report\n\n`;
|
|
253
428
|
let tableSeparator = '';
|
|
254
|
-
tabled[0]
|
|
429
|
+
for (const column of tabled[0]) {
|
|
255
430
|
if (column !== '') {
|
|
256
431
|
output += `| ${column} `;
|
|
257
432
|
tableSeparator += '| --- ';
|
|
258
433
|
}
|
|
259
|
-
}
|
|
434
|
+
}
|
|
260
435
|
output += `|\n${tableSeparator}|\n`;
|
|
261
436
|
for (let i = 1; i < tabled.length; i++) {
|
|
262
|
-
tabled[i]
|
|
437
|
+
for (let field of tabled[i]) {
|
|
263
438
|
if (field !== '') {
|
|
264
439
|
field = field === true ? '✓' : field === false ? '✗' : field;
|
|
265
440
|
output += `| ${field} `;
|
|
266
441
|
}
|
|
267
|
-
}
|
|
442
|
+
}
|
|
268
443
|
output += '|\n';
|
|
269
444
|
}
|
|
270
445
|
try {
|
|
@@ -274,6 +449,21 @@ const DevOps = {
|
|
|
274
449
|
Util.logger.error(`DevOps.document():: error | ` + ex.message);
|
|
275
450
|
}
|
|
276
451
|
},
|
|
452
|
+
/**
|
|
453
|
+
* should return only the json for all but asset, query and script that are saved as multiple files
|
|
454
|
+
* additionally, the documentation for dataExtension and automation should be returned
|
|
455
|
+
*
|
|
456
|
+
* @param {TYPE.Mcdevrc} properties central properties object
|
|
457
|
+
* @param {TYPE.BuObject} buObject references credentials
|
|
458
|
+
* @param {string} metadataType metadata type to build
|
|
459
|
+
* @param {string[]} keyArr customerkey of the metadata
|
|
460
|
+
* @returns {Promise.<string[]>} list of all files that need to be committed in a flat array ['path/file1.ext', 'path/file2.ext']
|
|
461
|
+
*/
|
|
462
|
+
getFilesToCommit(properties, buObject, metadataType, keyArr) {
|
|
463
|
+
MetadataType[metadataType].properties = properties;
|
|
464
|
+
MetadataType[metadataType].buObject = buObject;
|
|
465
|
+
return MetadataType[metadataType].getFilesToCommit(keyArr);
|
|
466
|
+
},
|
|
277
467
|
};
|
|
278
468
|
|
|
279
469
|
module.exports = DevOps;
|