@tangelo/tangelo-configuration-toolkit 1.14.3 → 1.15.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/README.md +67 -67
- package/bin/index.js +2 -2
- package/index.js +36 -11
- package/package.json +6 -6
- package/src/cli.js +3 -4
- package/src/lib/get-repoconfig.js +3 -3
- package/src/lib/get-tdi-branch.js +43 -43
- package/src/lib/gulp-batch-replace-with-filter.js +19 -19
- package/src/lib/gulp-simple-rename.js +11 -11
- package/src/lib/style-string-getters.js +2 -0
- package/src/modules/build/index.js +5 -5
- package/src/modules/build/oxygen.js +177 -174
- package/src/modules/fonto/commands.js +4 -4
- package/src/modules/fonto/index.js +16 -16
- package/src/modules/git/index.js +3 -4
- package/src/modules/info/index.js +200 -201
- package/src/modules/migrate/index.js +3 -3
- package/src/modules/sql/index.js +3 -3
- package/.eslintrc.js +0 -47
- package/.pre-commit-config.yaml +0 -36
- package/bitbucket-pipelines.yml +0 -32
|
@@ -1,175 +1,178 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const globby = require('globby');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const SaxonJS = require('saxon-js');
|
|
5
|
-
|
|
6
|
-
const spConfigPath =
|
|
7
|
-
const sefFilePath =
|
|
8
|
-
const cmscustomPath = 'config/cmscustom/';
|
|
9
|
-
const siteStylesheetsPath = 'config/txp/site-stylesheets/';
|
|
10
|
-
|
|
11
|
-
const masterFiles = new Set;
|
|
12
|
-
const transformationScenarios = [];
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
'
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
//
|
|
168
|
-
// - Will
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const globby = require('globby');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const SaxonJS = require('saxon-js');
|
|
5
|
+
|
|
6
|
+
const spConfigPath = 'build/oxygen/stylesheetPaths.json';
|
|
7
|
+
const sefFilePath = 'build/oxygen/createProjectFile.sef.json';
|
|
8
|
+
const cmscustomPath = 'config/cmscustom/';
|
|
9
|
+
const siteStylesheetsPath = 'config/txp/site-stylesheets/';
|
|
10
|
+
|
|
11
|
+
const masterFiles = new Set;
|
|
12
|
+
const transformationScenarios = [];
|
|
13
|
+
|
|
14
|
+
let spConfig, sefFile;
|
|
15
|
+
|
|
16
|
+
const convertToValidFilename = string => string.replace(/[/|\\:*?"<>]/g, ' ');
|
|
17
|
+
|
|
18
|
+
const createProjectFile = (config, newXprFile) => {
|
|
19
|
+
_info('Initializing xpr file(s):');
|
|
20
|
+
const xprFiles = [...globby.sync(`*.xpr`)];
|
|
21
|
+
// Add newXprFile at the start of xprFiles if it does not already exists:
|
|
22
|
+
if (newXprFile && xprFiles.indexOf(newXprFile)===-1) xprFiles.unshift(newXprFile);
|
|
23
|
+
|
|
24
|
+
// Copy xpr file from TDI if it does not exists yet;
|
|
25
|
+
if (xprFiles[0] && !newXprFile) _write(`Found: ${xprFiles.join(', ')}`);
|
|
26
|
+
else {
|
|
27
|
+
if (!newXprFile) {
|
|
28
|
+
// Set xpr filename to customer name (assumes correct upsert scripts structure)
|
|
29
|
+
const customers = new Set;
|
|
30
|
+
_repoconfig.forEach(p => customers.add(p.customer_name));
|
|
31
|
+
xprFiles.push(convertToValidFilename([...customers].join(' - ')) + '.xpr');
|
|
32
|
+
}
|
|
33
|
+
// Copy new xpr file
|
|
34
|
+
fs.copySync(path.join(_paths.repo, 'tangelo-default-implementation/src/[customer].xpr'), path.join(_paths.repo, xprFiles[0]));
|
|
35
|
+
_write(`Created: '${xprFiles[0]}'`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Search for transformationScenarios/masterfiles based on TDI submodule oxygenProjectFile config
|
|
39
|
+
_info('\nSearching for transformationScenarios/masterfiles');
|
|
40
|
+
config.oxygenProjectFile.forEach(
|
|
41
|
+
pf => {
|
|
42
|
+
// Collect transformation scenarios and add them to the transformationScenarios array; add individual stylesheets to the masterFiles set.
|
|
43
|
+
if (pf.transformation) getTransformations(pf.transformation);
|
|
44
|
+
// Add files to the masterfiles set.
|
|
45
|
+
else if (pf.masterfile) getMasterfiles(pf.masterfile);
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// update all .xpr files with collected transformation scenarios and masterfiles.
|
|
50
|
+
transformXprFile(xprFiles);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const getTransformations = config => {
|
|
54
|
+
_repoconfig.forEach(rc => {
|
|
55
|
+
// get pathname of customer/project
|
|
56
|
+
const [customerPath, projectPath] = config.location === 'database' ? rc.path_dbconfig : rc.path_cmscustom;
|
|
57
|
+
|
|
58
|
+
// set pathname of customer/project in location glob-expression
|
|
59
|
+
const location = path.join(
|
|
60
|
+
_paths.repo,
|
|
61
|
+
config.files.replace(/\[customer\]/, customerPath).replace(/\[project\]/, projectPath)
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
globby
|
|
65
|
+
.sync(`${location}`)
|
|
66
|
+
.forEach(f => {
|
|
67
|
+
// extract baseStrings from file
|
|
68
|
+
const fileString = fs.readFileSync(f).toString();
|
|
69
|
+
const baseStrings = fileString.match(RegExp(config.extracts.base, 'gm'));
|
|
70
|
+
|
|
71
|
+
if (fileString.replace(/\s|^prompt\s.*$/gm, '') !== '') {
|
|
72
|
+
if (baseStrings) {
|
|
73
|
+
baseStrings.forEach(s => {
|
|
74
|
+
// extract type, name, files info from baseString
|
|
75
|
+
const type = config.extracts.type ? s.match(RegExp(config.extracts.type))[1] : config.values.type;
|
|
76
|
+
const name = config.extracts.name ? s.match(RegExp(config.extracts.name))[1] : config.values.name;
|
|
77
|
+
const files = s.match(RegExp(config.extracts.files))[1];
|
|
78
|
+
|
|
79
|
+
// Add transformation scenario to the transformationScenario array
|
|
80
|
+
transformationScenarios.push({
|
|
81
|
+
name: `${type}: ${name} (${rc.customer_name}, ${rc.project_name})`, // note that in createProjectFile.xsl a regex is added that matches scenarios based on this name. This to preserve manually added scenarios.
|
|
82
|
+
transformationScenario: files,
|
|
83
|
+
location: config.location === 'txp' ? siteStylesheetsPath : cmscustomPath
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Add each non-tdi stylesheet in transformation scenario to the masterFiles set
|
|
87
|
+
files.split(',').forEach(f => {
|
|
88
|
+
const filePath = `${config.location === 'txp' ? siteStylesheetsPath : cmscustomPath}${f}`;
|
|
89
|
+
if (!f.startsWith('tdi')) masterFiles.add(filePath);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
_write(`No transformation scenarios found in ${f} for '${config.extracts.base}'`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
const getMasterfiles = config => {
|
|
103
|
+
globby
|
|
104
|
+
.sync(`${path.join(_paths.repo, config.files)}`)
|
|
105
|
+
.forEach(cf => {
|
|
106
|
+
// Check if masterfile should be extracted from file
|
|
107
|
+
const fileString = fs.readFileSync(cf).toString();
|
|
108
|
+
if (fileString.replace(/\s|^prompt\s.*$/gm, '')!=='') {
|
|
109
|
+
if (config.extracts) {
|
|
110
|
+
// extract baseStrings from file
|
|
111
|
+
const baseStrings = fileString.match(RegExp(config.extracts.base, 'gm'));
|
|
112
|
+
|
|
113
|
+
if (baseStrings) {
|
|
114
|
+
baseStrings.forEach(s => {
|
|
115
|
+
// extract (comma-separated) list of masterfiles
|
|
116
|
+
const filesString = s.match(RegExp(config.extracts.files))[1];
|
|
117
|
+
if (!filesString) _error(`No masterfiles found in '${s}' for '${config.extracts.files}'`);
|
|
118
|
+
|
|
119
|
+
// Add each non-tdi masterfile to the masterFiles set
|
|
120
|
+
filesString.split(',').forEach(f => {
|
|
121
|
+
if (!f.startsWith('tdi')){
|
|
122
|
+
const filePath = `${config.location === 'txp' ? siteStylesheetsPath : cmscustomPath}${f}`;
|
|
123
|
+
masterFiles.add(filePath);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
} else {
|
|
128
|
+
_write(`No masterfiles found in ${cf} for '${config.extracts.base}'`);
|
|
129
|
+
}
|
|
130
|
+
} else { // Add synced file to masterfiles; strip path from c:/... hence it starts with config/cmscustom or config/txp/site-stylesheets
|
|
131
|
+
const filePath = config.location === 'txp' ? `${siteStylesheetsPath}${cf.split(siteStylesheetsPath)[1]}` : `${cmscustomPath}${cf.split(cmscustomPath)[1]}`;
|
|
132
|
+
masterFiles.add(filePath);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const transformXprFile = xprFiles => {
|
|
140
|
+
_info('\nUpdating xpr file(s):');
|
|
141
|
+
// create with: xslt3 -t -xsl:createProjectFile.xsl -export:createProjectFile.sef.json v -nogo'
|
|
142
|
+
|
|
143
|
+
xprFiles
|
|
144
|
+
.forEach(xprFile => {
|
|
145
|
+
// Transform xpr; add masterfiles and transformationScenarios as parameters of the stylesheet
|
|
146
|
+
_write(`${xprFile}\n`);
|
|
147
|
+
SaxonJS.transform({
|
|
148
|
+
stylesheetText: JSON.stringify(sefFile),
|
|
149
|
+
stylesheetBaseURI: 'createProjectFile.sef.json',
|
|
150
|
+
sourceFileName: path.join(_paths.repo, xprFile),
|
|
151
|
+
destination: 'serialized',
|
|
152
|
+
stylesheetParams: {
|
|
153
|
+
'masterfiles': [...masterFiles],
|
|
154
|
+
'Q{}transformationScenarios': transformationScenarios
|
|
155
|
+
}
|
|
156
|
+
}, 'async')
|
|
157
|
+
.then(output => {
|
|
158
|
+
// Write result of transformation to xpr file
|
|
159
|
+
fs.writeFileSync(path.join(_paths.repo, xprFile), output.principalResult);
|
|
160
|
+
})
|
|
161
|
+
.catch(e => _warn(`Failed to update: ${xprFile}\n ${e}`));
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
module.exports = function oxygen (arg) {
|
|
167
|
+
// Set projects transformation scenarios and masterfiles in oXygen project file
|
|
168
|
+
// - Will try to preserve manually added entries in the transformation scenarios and masterfiles
|
|
169
|
+
// - Will remove non existing masterfiles or masterfiles that start with a '_'
|
|
170
|
+
|
|
171
|
+
spConfig = _modulesTdi.require(spConfigPath);
|
|
172
|
+
sefFile = _modulesTdi.require(sefFilePath);
|
|
173
|
+
|
|
174
|
+
if (!spConfig || !sefFile) _error(`Cannot find required files in TDI submodule. Try updating TDI submodule.`);
|
|
175
|
+
|
|
176
|
+
const newXprFile = (typeof arg === 'string') ? convertToValidFilename(arg) + '.xpr' : null;
|
|
177
|
+
createProjectFile(spConfig, newXprFile);
|
|
175
178
|
};
|
|
@@ -56,8 +56,8 @@ module.exports = {
|
|
|
56
56
|
resolve();
|
|
57
57
|
});
|
|
58
58
|
})
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
.then(() => cmdExec(`${fdt} editor upgrade --version ${fontoVersion} --non-interactive`))
|
|
60
|
+
.then(() => [fdt])
|
|
61
61
|
;
|
|
62
62
|
},
|
|
63
63
|
|
|
@@ -154,7 +154,7 @@ module.exports = {
|
|
|
154
154
|
else _write(result, '\n');
|
|
155
155
|
}
|
|
156
156
|
)
|
|
157
|
-
|
|
157
|
+
.then(() => [fdt])
|
|
158
158
|
;
|
|
159
159
|
},
|
|
160
160
|
|
|
@@ -205,7 +205,7 @@ module.exports = {
|
|
|
205
205
|
else _write(result, '\n');
|
|
206
206
|
}
|
|
207
207
|
)
|
|
208
|
-
|
|
208
|
+
.then(() => [fdt])
|
|
209
209
|
;
|
|
210
210
|
},
|
|
211
211
|
|
|
@@ -5,13 +5,13 @@ const globby = require('globby');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
const fdtCommand =
|
|
8
|
+
const fdtCommand = fv => `npx -y ${_packages.FDT.name}@${fv.replace(/^(7\.|8\.[012]\.).*/, '3.12.0')}`;
|
|
9
9
|
|
|
10
10
|
module.exports = function fonto (argv) {
|
|
11
11
|
|
|
12
12
|
const allowedFontoVersionRegex = (() => {
|
|
13
|
-
const
|
|
14
|
-
if (
|
|
13
|
+
const cvRegex = _modulesTdi.require('fonto/compatibleVersions.json')?.regex;
|
|
14
|
+
if (cvRegex) return RegExp(cvRegex);
|
|
15
15
|
|
|
16
16
|
// old way: a regex for each basecommit is stored in global._git.commitTdi
|
|
17
17
|
for (const fv of _git.commitTdi.fontoVersions) {
|
|
@@ -19,7 +19,7 @@ module.exports = function fonto (argv) {
|
|
|
19
19
|
}
|
|
20
20
|
})();
|
|
21
21
|
|
|
22
|
-
if (!_tdiSubmoduleExists) _error('TDI submodule folder is missing.');
|
|
22
|
+
if (!_tdiSubmoduleExists()) _error('TDI submodule folder is missing.');
|
|
23
23
|
|
|
24
24
|
// check if FDT is not installed globally, because then it won't be possible to use specific versions with npx
|
|
25
25
|
if (fs.pathExistsSync(path.join(_appdata.npmPath, 'node_modules', _packages.FDT.name))) {
|
|
@@ -30,8 +30,8 @@ module.exports = function fonto (argv) {
|
|
|
30
30
|
|
|
31
31
|
// find fonto instances by searching for fonto/manifest.json files
|
|
32
32
|
const fontoPaths = globby.sync(['**/fonto/manifest.json', 'manifest.json', `!${_paths.tdi}/**`])
|
|
33
|
-
.map(p => ([p.replace('manifest.json', ''), fs.readJsonSync(p).sdkVersion.replace(/Nightlies.*/, 'nightly')])
|
|
34
|
-
|
|
33
|
+
.map(p => ([p.replace('manifest.json', ''), fs.readJsonSync(p).sdkVersion.replace(/Nightlies.*/, 'nightly')]))
|
|
34
|
+
;
|
|
35
35
|
|
|
36
36
|
if (fontoPaths.length===0) _error('No Fonto instance found.');
|
|
37
37
|
|
|
@@ -43,14 +43,14 @@ module.exports = function fonto (argv) {
|
|
|
43
43
|
.then(() => {
|
|
44
44
|
process.chdir(path.join(_paths.repo, _paths.apply, fontoPath));
|
|
45
45
|
|
|
46
|
-
if (fontoPath
|
|
46
|
+
if (fontoPath !== '.') _info(`${i>0 ? '\n' : ''}Fonto instance #${i+1}: ${path.join(_paths.apply, fontoPath)}\n`);
|
|
47
47
|
|
|
48
48
|
// execute commands sequentially and in correct order
|
|
49
|
-
return new Promise(
|
|
49
|
+
return new Promise(resolve => {
|
|
50
50
|
_info('Determining Fonto version:');
|
|
51
51
|
let fontoVersionNew = typeof argv.init == 'string' ? argv.init : fontoVersionCurrent;
|
|
52
52
|
|
|
53
|
-
if (fontoVersionNew
|
|
53
|
+
if (fontoVersionNew === 'latest') {
|
|
54
54
|
const data = execSync(`${fdtCommand(fontoVersionCurrent)} editor versions`, {encoding: 'UTF-8'});
|
|
55
55
|
fontoVersionNew = data.match(/\d+\.\d+\.\d+/g).filter(v => allowedFontoVersionRegex.test(v))[0];
|
|
56
56
|
}
|
|
@@ -61,13 +61,13 @@ module.exports = function fonto (argv) {
|
|
|
61
61
|
_write(fontoVersionNew+'\n');
|
|
62
62
|
return resolve([fdtCommand(fontoVersionNew), fontoVersionNew]);
|
|
63
63
|
})
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
.then(argv.init && commands.init)
|
|
65
|
+
.then(argv.schema && commands.schema)
|
|
66
|
+
.then(argv.elements && commands.elements)
|
|
67
|
+
.then(argv.attributes && commands.attributes)
|
|
68
|
+
.then(argv.localize && commands.localize)
|
|
69
|
+
.then(argv.build && commands.build)
|
|
70
|
+
.then(argv.run && commands.run)
|
|
71
71
|
;
|
|
72
72
|
});
|
|
73
73
|
});
|
package/src/modules/git/index.js
CHANGED
|
@@ -36,7 +36,6 @@ const cmdExec = commands => new Promise(resolve => {
|
|
|
36
36
|
});
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
const tdiMigrationFilePath = path.join(_paths.repo, _paths.tdi, 'tct/git/tdiCommitsRequiringMigration.js');
|
|
40
39
|
|
|
41
40
|
module.exports = function git (argv) {
|
|
42
41
|
|
|
@@ -143,10 +142,10 @@ module.exports = function git (argv) {
|
|
|
143
142
|
if (!tdiFromDateCustom) _info(`TDI submodule updated:\n${updateSubmoduleMsg}`);
|
|
144
143
|
|
|
145
144
|
// tdiMigrationFilePath should exist in latest commits of releases 5.3+; As we updated to the latest version this should work
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
const migrations = _modulesTdi.require('git/tdiCommitsRequiringMigration.js');
|
|
146
|
+
if (migrations) {
|
|
148
147
|
const fromTdiDate = tdiFromDateCustom ? new Date(tdiFromDateCustom) : (tdiBranch.commonAncestor) ? tdiBranch.commonAncestor.date: _git.commitTdi.local().date;
|
|
149
|
-
const toTdiDate = tdiToDateCustom ? new Date(tdiToDateCustom) : new Date(
|
|
148
|
+
const toTdiDate = tdiToDateCustom ? new Date(tdiToDateCustom) : new Date();
|
|
150
149
|
|
|
151
150
|
_info(`TDI commits requiring migration between ${_formatDate(fromTdiDate)} and ${_formatDate(toTdiDate)}`);
|
|
152
151
|
// Filter the migrations that should be applied/considered; Also display older releases migrations
|