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/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,225 @@ 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
|
+
* @returns {Promise.<TYPE.DeltaPkgItem[]>} -
|
|
253
|
+
*/
|
|
254
|
+
async buildDeltaDefinitions(properties, range, skipInteraction) {
|
|
255
|
+
// check if sourceTargetMapping is valid
|
|
256
|
+
if (
|
|
257
|
+
!properties.options.deployment.sourceTargetMapping ||
|
|
258
|
+
!Object.keys(properties.options.deployment.sourceTargetMapping).length
|
|
259
|
+
) {
|
|
260
|
+
Util.logger.error('Bad configuration of options.deployment.sourceTargetMapping');
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const sourceMarketListArr = Object.keys(properties.options.deployment.sourceTargetMapping);
|
|
264
|
+
/** @type {TYPE.DeltaPkgItem[]} */
|
|
265
|
+
const deltaDeployAll = [];
|
|
266
|
+
for (const sourceML of sourceMarketListArr) {
|
|
267
|
+
// check if sourceTargetMapping has valid values
|
|
268
|
+
// #1 check source marketlist
|
|
269
|
+
try {
|
|
270
|
+
Util.verifyMarketList(sourceML, properties);
|
|
271
|
+
// remove potentially existing "description"-entry
|
|
272
|
+
delete properties.marketList[sourceML].description;
|
|
273
|
+
|
|
274
|
+
const sourceMarketBuArr = Object.keys(properties.marketList[sourceML]);
|
|
275
|
+
if (sourceMarketBuArr.length !== 1) {
|
|
276
|
+
throw new Error('Only 1 BU is allowed per source marketList');
|
|
277
|
+
}
|
|
278
|
+
if ('string' !== typeof properties.marketList[sourceML][sourceMarketBuArr[0]]) {
|
|
279
|
+
throw new TypeError('Only 1 market per BU is allowed per source marketList');
|
|
280
|
+
}
|
|
281
|
+
} catch (ex) {
|
|
282
|
+
Util.logger.error('Deployment Source: ' + ex.message);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
// #2 check corresponding target marketList
|
|
286
|
+
let targetML;
|
|
287
|
+
try {
|
|
288
|
+
targetML = properties.options.deployment.sourceTargetMapping[sourceML];
|
|
289
|
+
if ('string' !== typeof targetML) {
|
|
290
|
+
throw new TypeError(
|
|
291
|
+
'Please define one target marketList per source in deployment.sourceTargetMapping (No arrays allowed)'
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
Util.verifyMarketList(targetML, properties);
|
|
295
|
+
// remove potentially existing "description"-entry
|
|
296
|
+
delete properties.marketList[targetML].description;
|
|
297
|
+
} catch (ex) {
|
|
298
|
+
Util.logger.error('Deployment Target: ' + ex.message);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// all good let's loop a second time for actual execution
|
|
302
|
+
for (const sourceMlName of sourceMarketListArr) {
|
|
303
|
+
const targetMlName = properties.options.deployment.sourceTargetMapping[sourceMlName];
|
|
304
|
+
const sourceBU = Object.keys(properties.marketList[sourceMlName])[0];
|
|
305
|
+
const sourceMarket = Object.values(properties.marketList[sourceMlName])[0];
|
|
306
|
+
|
|
307
|
+
const delta = await DevOps.getDeltaList(properties, range, false, sourceBU);
|
|
308
|
+
// If only chaing templating and buildDefinition if required
|
|
309
|
+
if (!delta || delta.length === 0) {
|
|
310
|
+
// info/error messages was printed by DevOps.createDeltaPkg() already
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
Util.logger.info('=============');
|
|
314
|
+
|
|
315
|
+
// Put files into maps. One map with BU -> type -> file (for retrieveAsTemplate)
|
|
316
|
+
// Other map only with type -> file (for buildDefinitionBulk)
|
|
317
|
+
const buTypeDelta = {}; // for bt, with BU info
|
|
318
|
+
const typeDelta = {}; // for bdb, without BU info - thats taken from the marketList
|
|
319
|
+
let deltaCounter = 0;
|
|
320
|
+
const deltaDeploy = delta
|
|
321
|
+
// Only template/build files that were added/updated/moved. no deletions
|
|
322
|
+
// ! doesn't work for folder, because their name parsing doesnt work at the moment
|
|
323
|
+
.filter((file) => file.gitAction !== 'delete' && file.name);
|
|
324
|
+
deltaDeployAll.push(...deltaDeploy);
|
|
325
|
+
for (const file of deltaDeploy) {
|
|
326
|
+
const buPath = `${file._credential}/${file._businessUnit}`;
|
|
327
|
+
if (!buTypeDelta[buPath]) {
|
|
328
|
+
// init object
|
|
329
|
+
buTypeDelta[buPath] = {};
|
|
330
|
+
}
|
|
331
|
+
if (!buTypeDelta[buPath][file.type]) {
|
|
332
|
+
// init array
|
|
333
|
+
buTypeDelta[buPath][file.type] = [];
|
|
334
|
+
}
|
|
335
|
+
buTypeDelta[buPath][file.type].push(file.externalKey);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Run buildTemplate for each business unit for each type
|
|
339
|
+
Util.logger.info('Retrieve template from Git delta');
|
|
340
|
+
// ! needs to be for (.. in ..) loop so that it gets executed in series
|
|
341
|
+
for (const bu in buTypeDelta) {
|
|
342
|
+
for (const type in buTypeDelta[bu]) {
|
|
343
|
+
// get unique list (original search might include more than one entry for types with docs or extracted code)
|
|
344
|
+
const keyArr = [...new Set(buTypeDelta[bu][type])];
|
|
345
|
+
Util.logger.info(
|
|
346
|
+
`⚡ mcdev bt ${bu} ${type} "${keyArr.join(',')}" ${sourceMarket}`
|
|
347
|
+
);
|
|
348
|
+
await Builder.buildTemplate(bu, type, keyArr, sourceMarket);
|
|
349
|
+
// ensure we have the right key for bd/bdb that matches the name used for rt
|
|
350
|
+
if (keyArr.length) {
|
|
351
|
+
if (!typeDelta[type]) {
|
|
352
|
+
// init array
|
|
353
|
+
typeDelta[type] = [];
|
|
354
|
+
}
|
|
355
|
+
typeDelta[type].push(...keyArr);
|
|
356
|
+
deltaCounter += keyArr.length;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (deltaCounter) {
|
|
361
|
+
Util.logger.info(`- ✔️ Templates created: ${deltaCounter}`);
|
|
362
|
+
} else {
|
|
363
|
+
Util.logger.warn(
|
|
364
|
+
`- No Templates or Deploy Definitions created for ${sourceMlName}`
|
|
365
|
+
);
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Run build definitions bulk for each type
|
|
370
|
+
Util.logger.info('=============');
|
|
371
|
+
Util.logger.info('Build deploy definitions from delta templates');
|
|
372
|
+
if (
|
|
373
|
+
properties.directories.templateBuilds == properties.directories.deploy ||
|
|
374
|
+
(Array.isArray(properties.directories.templateBuilds) &&
|
|
375
|
+
properties.directories.templateBuilds.includes(properties.directories.deploy))
|
|
376
|
+
) {
|
|
377
|
+
let responses;
|
|
378
|
+
if (!skipInteraction) {
|
|
379
|
+
// deploy folder is in targets for definition creation
|
|
380
|
+
// recommend to purge their content first
|
|
381
|
+
responses = await inquirer.prompt([
|
|
382
|
+
{
|
|
383
|
+
type: 'confirm',
|
|
384
|
+
name: 'isPurgeDeployFolder',
|
|
385
|
+
message:
|
|
386
|
+
'Do you want to empty the deploy folder (ensures no files from previous deployments remain)?',
|
|
387
|
+
default: true,
|
|
388
|
+
},
|
|
389
|
+
]);
|
|
390
|
+
}
|
|
391
|
+
if (skipInteraction || responses.isPurgeDeployFolders) {
|
|
392
|
+
// Clear output folder structure for selected sub-type
|
|
393
|
+
await File.remove(File.normalizePath([properties.directories.deploy]));
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
const bdPromises = [];
|
|
397
|
+
for (const type in typeDelta) {
|
|
398
|
+
Util.logger.info(
|
|
399
|
+
`⚡ mcdev bdb ${targetMlName} ${type} "${typeDelta[type].join(',')}"`
|
|
400
|
+
);
|
|
401
|
+
// omitting "await" to speed up creation
|
|
402
|
+
bdPromises.push(
|
|
403
|
+
Builder.buildDefinitionBulk(targetMlName, type, typeDelta[type].join(','))
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
await Promise.all(bdPromises);
|
|
407
|
+
Util.logger.info(`- ✔️ Deploy defintions created`);
|
|
408
|
+
if (
|
|
409
|
+
properties.directories.templateBuilds == properties.directories.deploy ||
|
|
410
|
+
(Array.isArray(properties.directories.templateBuilds) &&
|
|
411
|
+
properties.directories.templateBuilds.includes(properties.directories.deploy))
|
|
412
|
+
) {
|
|
413
|
+
Util.logger.info(`You can now run deploy on the prepared BUs`);
|
|
414
|
+
} else {
|
|
415
|
+
Util.logger.info(
|
|
416
|
+
`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.`
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
if (!deltaDeployAll.length) {
|
|
421
|
+
Util.logger.error(
|
|
422
|
+
'- ❌ No Templates or Deploy Definitions created. Check if you expected no changes.'
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
return deltaDeployAll;
|
|
426
|
+
},
|
|
427
|
+
|
|
244
428
|
/**
|
|
245
429
|
* create markdown file for deployment listing
|
|
246
|
-
*
|
|
247
|
-
* @param {
|
|
430
|
+
*
|
|
431
|
+
* @param {string} directory -
|
|
432
|
+
* @param {object} jsonReport -
|
|
248
433
|
* @returns {void}
|
|
249
434
|
*/
|
|
250
435
|
document(directory, jsonReport) {
|
|
251
436
|
const tabled = jsonToTable(jsonReport);
|
|
252
437
|
let output = `# Deployment Report\n\n`;
|
|
253
438
|
let tableSeparator = '';
|
|
254
|
-
tabled[0]
|
|
439
|
+
for (const column of tabled[0]) {
|
|
255
440
|
if (column !== '') {
|
|
256
441
|
output += `| ${column} `;
|
|
257
442
|
tableSeparator += '| --- ';
|
|
258
443
|
}
|
|
259
|
-
}
|
|
444
|
+
}
|
|
260
445
|
output += `|\n${tableSeparator}|\n`;
|
|
261
446
|
for (let i = 1; i < tabled.length; i++) {
|
|
262
|
-
tabled[i]
|
|
447
|
+
for (let field of tabled[i]) {
|
|
263
448
|
if (field !== '') {
|
|
264
449
|
field = field === true ? '✓' : field === false ? '✗' : field;
|
|
265
450
|
output += `| ${field} `;
|
|
266
451
|
}
|
|
267
|
-
}
|
|
452
|
+
}
|
|
268
453
|
output += '|\n';
|
|
269
454
|
}
|
|
270
455
|
try {
|
|
@@ -274,6 +459,21 @@ const DevOps = {
|
|
|
274
459
|
Util.logger.error(`DevOps.document():: error | ` + ex.message);
|
|
275
460
|
}
|
|
276
461
|
},
|
|
462
|
+
/**
|
|
463
|
+
* should return only the json for all but asset, query and script that are saved as multiple files
|
|
464
|
+
* additionally, the documentation for dataExtension and automation should be returned
|
|
465
|
+
*
|
|
466
|
+
* @param {TYPE.Mcdevrc} properties central properties object
|
|
467
|
+
* @param {TYPE.BuObject} buObject references credentials
|
|
468
|
+
* @param {string} metadataType metadata type to build
|
|
469
|
+
* @param {string[]} keyArr customerkey of the metadata
|
|
470
|
+
* @returns {Promise.<string[]>} list of all files that need to be committed in a flat array ['path/file1.ext', 'path/file2.ext']
|
|
471
|
+
*/
|
|
472
|
+
getFilesToCommit(properties, buObject, metadataType, keyArr) {
|
|
473
|
+
MetadataType[metadataType].properties = properties;
|
|
474
|
+
MetadataType[metadataType].buObject = buObject;
|
|
475
|
+
return MetadataType[metadataType].getFilesToCommit(keyArr);
|
|
476
|
+
},
|
|
277
477
|
};
|
|
278
478
|
|
|
279
479
|
module.exports = DevOps;
|