@tangelo/tangelo-configuration-toolkit 1.13.0 → 1.14.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.js +1 -0
- package/LICENSE.md +1 -1
- package/README.md +1 -1
- package/bitbucket-pipelines.yml +32 -0
- package/index.js +4 -1
- package/package.json +4 -1
- package/src/cli.js +10 -5
- package/src/lib/exec-git-command.js +4 -3
- package/src/lib/get-repoconfig.js +1 -1
- package/src/lib/get-tdi-branch.js +41 -0
- package/src/lib/gulp-resolve-includes.js +3 -2
- package/src/lib/package-update-check.js +15 -14
- package/src/lib/style-string-getters.js +1 -1
- package/src/modules/build/oxygen.js +4 -4
- package/src/modules/deploy/config.js +1 -1
- package/src/modules/deploy/execute.js +5 -5
- package/src/modules/deploy/index.js +1 -1
- package/src/modules/fonto/commands.js +41 -41
- package/src/modules/fonto/index.js +22 -22
- package/src/modules/git/index.js +95 -1
- package/src/modules/info/index.js +13 -14
- package/src/modules/migrate/index.js +1 -1
- package/src/modules/migrate/steps.js +1 -1
- package/src/modules/sql/index.js +2 -2
package/.eslintrc.js
CHANGED
package/LICENSE.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Copyright © 2020 Tangelo Software BV <info@tangelo-software.com>
|
|
1
|
+
Copyright © 2020 Tangelo Software BV <mailto:info@tangelo-software.com>
|
package/README.md
CHANGED
|
@@ -64,4 +64,4 @@ For a new repository, using `tct build --init` also creates the repoconfig-file.
|
|
|
64
64
|
The `build -x` commands set projects transformation scenarios and masterfiles in the oXygen project file with the following functionality:
|
|
65
65
|
- Will try to preserve manually added entries in the transformation scenarios and masterfiles
|
|
66
66
|
- Will remove non existing masterfiles or masterfiles that start with a '_'
|
|
67
|
-
- No masterfiles / scenarios will be added if their path match with oXygens hidden directory patterns
|
|
67
|
+
- No masterfiles / scenarios will be added if their path match with oXygens hidden directory patterns
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
image: atlassian/default-image:4
|
|
2
|
+
|
|
3
|
+
clone:
|
|
4
|
+
depth: full # SonarCloud scanner needs the full history to assign issues properly
|
|
5
|
+
|
|
6
|
+
definitions:
|
|
7
|
+
caches:
|
|
8
|
+
sonar: ~/.sonar/cache # Caching SonarCloud artifacts will speed up your build
|
|
9
|
+
services:
|
|
10
|
+
docker:
|
|
11
|
+
memory: 2048 # For large file line code
|
|
12
|
+
steps:
|
|
13
|
+
- step: &build-test-sonarcloud
|
|
14
|
+
name: Build, test and analyze on SonarCloud
|
|
15
|
+
caches:
|
|
16
|
+
- sonar
|
|
17
|
+
script:
|
|
18
|
+
- pipe: sonarsource/sonarcloud-scan:1.4.0
|
|
19
|
+
- step: &check-quality-gate-sonarcloud
|
|
20
|
+
name: Check the Quality Gate on SonarCloud
|
|
21
|
+
script:
|
|
22
|
+
- pipe: sonarsource/sonarcloud-quality-gate:0.1.6
|
|
23
|
+
|
|
24
|
+
pipelines:
|
|
25
|
+
branches:
|
|
26
|
+
master:
|
|
27
|
+
- step: *build-test-sonarcloud
|
|
28
|
+
- step: *check-quality-gate-sonarcloud
|
|
29
|
+
pull-requests:
|
|
30
|
+
'**':
|
|
31
|
+
- step: *build-test-sonarcloud
|
|
32
|
+
- step: *check-quality-gate-sonarcloud
|
package/index.js
CHANGED
|
@@ -24,7 +24,7 @@ global._write = (...msg) => {
|
|
|
24
24
|
global._info = (msg, time = false) => {
|
|
25
25
|
if (time) {
|
|
26
26
|
const tzDiff = new Date().getTimezoneOffset() * 60000;
|
|
27
|
-
const time = new Date(Date.now() - tzDiff).toISOString().
|
|
27
|
+
const time = new Date(Date.now() - tzDiff).toISOString().match(/\d\d:\d\d:\d\d/)[0];
|
|
28
28
|
msg = `[${time}] ${msg}`;
|
|
29
29
|
}
|
|
30
30
|
console.log(msg.lgreen);
|
|
@@ -44,6 +44,9 @@ global._perf = (t1) => {
|
|
|
44
44
|
console.log(`\nExecution time: ${t2 - t1}ms`.blue);
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
+
global._formatDate = (date) => {
|
|
48
|
+
return date.toLocaleDateString('en-gb', {day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit'});
|
|
49
|
+
};
|
|
47
50
|
|
|
48
51
|
global._paths = {
|
|
49
52
|
app: __dirname,
|
package/package.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangelo/tangelo-configuration-toolkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
|
+
"engines": {
|
|
5
|
+
"node": ">=14.0.0"
|
|
6
|
+
},
|
|
4
7
|
"description": "Tangelo Configuration Toolkit is a command-line toolkit which offers support for developing a Tangelo configuration.",
|
|
5
8
|
"bin": {
|
|
6
9
|
"tct": "bin/index.js",
|
package/src/cli.js
CHANGED
|
@@ -64,9 +64,11 @@ module.exports = function cli () {
|
|
|
64
64
|
aliases: ['g'],
|
|
65
65
|
desc: 'Git repo actions (requires global git installation)',
|
|
66
66
|
builder: {
|
|
67
|
-
init: {alias: 'i', desc: 'Initialize repository and add submodule', conflicts: ['r']},
|
|
68
|
-
reset: {alias: 'r', desc: 'Reset repository to last commit', conflicts: ['i']},
|
|
69
|
-
clone: {alias: 'c', desc: 'Clone a client repository and do basic setup'},
|
|
67
|
+
init: {alias: 'i', desc: 'Initialize repository and add submodule', conflicts: ['r', 'c', 'u']},
|
|
68
|
+
reset: {alias: 'r', desc: 'Reset repository to last commit', conflicts: ['i', 'c', 'u']},
|
|
69
|
+
clone: {alias: 'c', desc: 'Clone a client repository and do basic setup', conflicts: ['i', 'r', 'u']},
|
|
70
|
+
update: {alias: 'u', desc: 'Update repository or TDI submodule', conflicts: ['i', 'r', 'c']},
|
|
71
|
+
branch: {alias: 'b', desc: 'Pass tdi release branch, e.g. "5.4"', conflicts: ['i', 'r', 'c']}
|
|
70
72
|
},
|
|
71
73
|
handler: require('./modules/git')
|
|
72
74
|
})
|
|
@@ -111,10 +113,13 @@ module.exports = function cli () {
|
|
|
111
113
|
.example([
|
|
112
114
|
['$0 deploy --copy --server demo', 'Copy files to server "demo"'],
|
|
113
115
|
['$0 d -c -s demo', 'Same as above'],
|
|
114
|
-
['$0 f -sb && $0 d -c', 'Compile schema and build Fonto, then deploy to default server']
|
|
116
|
+
['$0 f -sb && $0 d -c', 'Compile schema and build Fonto, then deploy to default server'],
|
|
117
|
+
['$0 git --update', 'Pull git repository and submodule to latest repository commit'],
|
|
118
|
+
['$0 git --update tdi', 'Update TDI submodule to latest within current TDI branch'],
|
|
119
|
+
['$0 git --update tdi --branch 5.4', 'Update TDI submodule to latest in specified branch']
|
|
115
120
|
])
|
|
116
121
|
.check((argv, options) => {
|
|
117
|
-
const passedOpts = Object.keys(argv).filter(k => !['_', '$0', 'server', 's', 'filter', 'f'].includes(k)); // also filter options with defaults
|
|
122
|
+
const passedOpts = Object.keys(argv).filter(k => !['_', '$0', 'server', 's', 'filter', 'f', 'branch', 'b'].includes(k)); // also filter options with defaults
|
|
118
123
|
const hasOpts = Object.keys(options)[0];
|
|
119
124
|
if (hasOpts && !passedOpts[0]) yargs.showHelp();
|
|
120
125
|
return true;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
const {spawnSync} = require('child_process');
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
module.exports = function execGitCommand(args, path, returnProperties
|
|
4
|
+
module.exports = function execGitCommand(args, path, returnProperties, expectedStatus) {
|
|
5
5
|
|
|
6
6
|
const cmd = spawnSync('git', [args], {cwd: path, shell: true});
|
|
7
7
|
|
|
8
|
+
if (cmd.status !== 0) return {error: `${cmd.stderr}`};
|
|
8
9
|
if (expectedStatus!=undefined) return cmd.status == expectedStatus;
|
|
9
10
|
|
|
10
11
|
let retObj = (cmd.stdout||'').toString().trim().split(';');
|
|
11
|
-
if (returnProperties[0]) {
|
|
12
|
+
if (returnProperties?.[0]) {
|
|
12
13
|
const o = {};
|
|
13
14
|
retObj = (
|
|
14
15
|
retObj.forEach((v, i) => {
|
|
@@ -21,6 +22,6 @@ module.exports = function execGitCommand(args, path, returnProperties = [], expe
|
|
|
21
22
|
o
|
|
22
23
|
);
|
|
23
24
|
}
|
|
24
|
-
return retObj.length
|
|
25
|
+
return retObj.length === 1 ? retObj[0] : retObj;
|
|
25
26
|
|
|
26
27
|
};
|
|
@@ -16,7 +16,7 @@ module.exports = function getRepoconfig() {
|
|
|
16
16
|
const ntName = ntSqlInsert.match(/'([^']+)' name/)[1];
|
|
17
17
|
const xpiDir = ntSqlInsert.match(/'([^']+)' xsl_prep_inc/)[1];
|
|
18
18
|
|
|
19
|
-
const path_cmscustom = xpiDir.replace(/\/[^/]+$/, '').split(
|
|
19
|
+
const path_cmscustom = xpiDir.replace(/\/[^/]+$/, '').split(/\//);
|
|
20
20
|
const path_dbconfig = p.replace(/database\/config\/(.*)\/[^/]+/, '$1').split(/[/\\]/);
|
|
21
21
|
const dnRegex = [path_cmscustom[0].replace(/[_'"]/g, '.'), path_dbconfig[0].replace(/[_'"]/g, '.')].sort((a, b) => b.length - a.length).join('|');
|
|
22
22
|
const customer_name = (dtDisplayName.match(RegExp(dnRegex, 'i')) || dtDisplayName.split(/ (.+)/))[0];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const execGitCommand = require('./exec-git-command');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
module.exports = function getTdiBranch(toBranchName) {
|
|
5
|
+
const tdiBranch = {};
|
|
6
|
+
let toBranch;
|
|
7
|
+
if (toBranchName) {
|
|
8
|
+
// Check if specified branch exists; we will update to this branch
|
|
9
|
+
toBranch = String(toBranchName).replace(/(?:release\/)?(\d+(?:\.[\dx]+)*)/, `release/$1`);
|
|
10
|
+
const branchExists = execGitCommand(`branch --remote`, path.join(_paths.repo, _paths.tdi)).match(`origin/${toBranch}`);
|
|
11
|
+
if (!branchExists) _error(`TDI branch "${toBranch}" does not exist. Note that TCT can only update to a release branch.`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Get the current TDI branch:
|
|
15
|
+
|
|
16
|
+
// Get remote release branches containing TDI HEAD commit
|
|
17
|
+
const releaseBranches = execGitCommand(`branch --all --contains ${_git.commitTdi.local().hash}`, path.join(_paths.repo, _paths.tdi)).match(/remotes\/origin\/release\/[^\s]+/gsm);
|
|
18
|
+
if (!releaseBranches || releaseBranches.error) _error(`Could not retrieve TDI release branches`);
|
|
19
|
+
|
|
20
|
+
// Get the first possible branch; prefer release/5.1 over release/5.2:
|
|
21
|
+
releaseBranches.sort((a, b) => a > b ? 1 : -1);
|
|
22
|
+
|
|
23
|
+
// Set branch name of firstBranch without 'remotes/origin/'
|
|
24
|
+
tdiBranch.name = releaseBranches[0].replace(/remotes\/origin\//g, '');
|
|
25
|
+
|
|
26
|
+
// In case of branch switch set from.name to the old branch and name to the new branch
|
|
27
|
+
if (toBranch) {
|
|
28
|
+
if (tdiBranch.name > toBranch) _error(`You cannot downgrade to a lower release branch with TCT.`);
|
|
29
|
+
tdiBranch.from = {name: tdiBranch.name};
|
|
30
|
+
tdiBranch.name = toBranch;
|
|
31
|
+
|
|
32
|
+
const branchHash = execGitCommand(`rev-parse origin/${toBranch}`, path.join(_paths.repo, _paths.tdi));
|
|
33
|
+
const commonAncestorHash = execGitCommand(`merge-base ${_git.commitTdi.local().hash} ${branchHash}`, path.join(_paths.repo, _paths.tdi));
|
|
34
|
+
const commonAncestorDate = execGitCommand(`show ${commonAncestorHash} --no-patch --format=%cd --date=iso-strict `, path.join(_paths.repo, _paths.tdi), ['date']).date;
|
|
35
|
+
tdiBranch.commonAncestor = {date: new Date(commonAncestorDate)};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Get number of commits behind
|
|
39
|
+
tdiBranch.commitsBehind = execGitCommand(`rev-list HEAD...origin/${tdiBranch.name} --count`, path.join(_paths.repo, _paths.tdi));
|
|
40
|
+
return tdiBranch;
|
|
41
|
+
};
|
|
@@ -15,8 +15,8 @@ const resolveImports = (fp, file) => {
|
|
|
15
15
|
fs.readFileSync(xiPath).toString()
|
|
16
16
|
.replace(/[\s\S]*?<x:config[^>]*>([\s\S]*?)<\/x:config>/, '$1') // only return contents of x:config element
|
|
17
17
|
.replace(/<(\w+)\/>/g, '<$1></$1>') // self-closing html tags can cause problems
|
|
18
|
-
.replace(/url="(
|
|
19
|
-
.replace(/url="(
|
|
18
|
+
.replace(/url="(?:\.\.\/)+pls\//g, 'url="#{plsservlet}') // replace relative references to plsservlet by bind (4.1 compatibility)
|
|
19
|
+
.replace(/url="(?:\.\.\/)+/g, 'url="#{cmspath}') // replace relative references to cmspath by bind (4.1 compatibility)
|
|
20
20
|
.replace(/(<x:javascript\s+src=")([^#].*?)("\s*\/>)/g, (match, g1, g2, g3) => { // correct all javascript refs to paths relative to cmscustompath bind
|
|
21
21
|
const newPath = path.resolve(fp.dir, path.resolve(path.dirname(xiPath), g2))
|
|
22
22
|
.replace(/.*cmscustom./, '#{cmscustompath}')
|
|
@@ -54,6 +54,7 @@ const resolveIncludes = (fp, file) => {
|
|
|
54
54
|
}
|
|
55
55
|
catch (e) {
|
|
56
56
|
console.log('Illegal reference to path: '.red + e.path);
|
|
57
|
+
return match;
|
|
57
58
|
}
|
|
58
59
|
});
|
|
59
60
|
}
|
|
@@ -2,18 +2,19 @@ const {compare} = require('compare-versions');
|
|
|
2
2
|
const exec = require('util').promisify(require('child_process').exec);
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
const doUpdateCheck = (
|
|
6
|
-
exec(`npm view -g ${_packages[
|
|
5
|
+
const doUpdateCheck = (pkg) => (
|
|
6
|
+
exec(`npm view -g ${_packages[pkg].name} version`)
|
|
7
7
|
.then(r => {
|
|
8
8
|
const versionAvailable = r.stdout.match(/([\d/.]+)/)[1];
|
|
9
|
-
if (!_packages[
|
|
10
|
-
else if (compare(_packages[
|
|
11
|
-
_appdata._update({[`updateCheck${
|
|
9
|
+
if (!_packages[pkg].version) _warn(`${pkg} is not installed! Run ` + `npm i -g ${_packages[pkg].name}`.white);
|
|
10
|
+
else if (compare(_packages[pkg].version, versionAvailable, '<')) {
|
|
11
|
+
_appdata._update({[`updateCheck${pkg}`]: {executed: new Date(), versionAvailable}});
|
|
12
12
|
return versionAvailable;
|
|
13
13
|
}
|
|
14
|
-
else _appdata._update({[`updateCheck${
|
|
14
|
+
else _appdata._update({[`updateCheck${pkg}`]: {executed: new Date()}});
|
|
15
|
+
return;
|
|
15
16
|
})
|
|
16
|
-
.catch(
|
|
17
|
+
.catch(() => _warn(`Failed checking latest version of ${pkg}.`))
|
|
17
18
|
);
|
|
18
19
|
|
|
19
20
|
let checkUpdatesDone = false;
|
|
@@ -23,16 +24,16 @@ module.exports = function packageUpdateCheck () {
|
|
|
23
24
|
if (!checkUpdatesDone) { // check if updatecheck has ran before because async calls below trigger beforeExit again
|
|
24
25
|
checkUpdatesDone = true;
|
|
25
26
|
|
|
26
|
-
['TCT'].forEach(
|
|
27
|
-
const updateMsg = (va) => `| Update ${
|
|
28
|
-
const {versionAvailable} = _appdata[`updateCheck${
|
|
27
|
+
['TCT'].forEach(pkg => {
|
|
28
|
+
const updateMsg = (va) => `| Update ${pkg} to ${va} | ` + `npm i -g ${_packages[pkg].name}`.white;
|
|
29
|
+
const {versionAvailable} = _appdata[`updateCheck${pkg}`] || {};
|
|
29
30
|
|
|
30
|
-
if (new Date() - new Date(_appdata[`updateCheck${
|
|
31
|
-
doUpdateCheck(
|
|
31
|
+
if (new Date() - new Date(_appdata[`updateCheck${pkg}`]?.executed || 0) > 1000*3600*24*7) { // check every week
|
|
32
|
+
doUpdateCheck(pkg).then(r => r && _warn(updateMsg(r)));
|
|
32
33
|
}
|
|
33
34
|
else if (versionAvailable) {
|
|
34
|
-
if (compare(_packages[
|
|
35
|
-
else _appdata._update({[`updateCheck${
|
|
35
|
+
if (compare(_packages[pkg].version, versionAvailable, '<')) _warn(updateMsg(versionAvailable));
|
|
36
|
+
else _appdata._update({[`updateCheck${pkg}`]: {executed: new Date()}});
|
|
36
37
|
}
|
|
37
38
|
});
|
|
38
39
|
}
|
|
@@ -9,5 +9,5 @@ for (const name in styles ) {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
String.prototype.__defineGetter__('nostyle', function () {
|
|
12
|
-
return this.replace(/[\u001b\u009b][[()#;?]*(
|
|
12
|
+
return this.replace(/[\u001b\u009b][[()#;?]*(?:\d{1,4}(?:;\d{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
|
|
13
13
|
});
|
|
@@ -59,7 +59,7 @@ const getTransformations = (config) => {
|
|
|
59
59
|
// extract baseStrings from file
|
|
60
60
|
const fileString = fs.readFileSync(f).toString();
|
|
61
61
|
const baseStrings = fileString.match(RegExp(config.extracts.base, 'gm'));
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
if (fileString.replace(/\s|^prompt\s.*$/gm,'')!=='') {
|
|
64
64
|
if (baseStrings) {
|
|
65
65
|
baseStrings.forEach(s => {
|
|
@@ -156,9 +156,9 @@ const transformXprFile = (xprFiles) => {
|
|
|
156
156
|
|
|
157
157
|
|
|
158
158
|
module.exports = function oxygen () {
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
//
|
|
159
|
+
// Set projects transformation scenarios and masterfiles in oXygen project file
|
|
160
|
+
// - Will try to preserve manually added entries in the transformation scenarios and masterfiles
|
|
161
|
+
// - Will remove non existing masterfiles or masterfiles that start with a '_'
|
|
162
162
|
|
|
163
163
|
if (!fs.existsSync(spConfigPath) || !fs.existsSync(sefFilePath)) {
|
|
164
164
|
_error(`Cannot find required files in TDI submodule. Try updating TDI submodule.`);
|
|
@@ -91,7 +91,7 @@ module.exports = {
|
|
|
91
91
|
let transferPattern = path.join(_paths.apply, filter).toFws();
|
|
92
92
|
|
|
93
93
|
// test if 'cmscustom/tdi' would be included, then add specifically, because following symlinks doesnt work with glob stars
|
|
94
|
-
const tdiIncRegex = /^(config\/)?(({\w*,?)?cmscustom(,?\w
|
|
94
|
+
const tdiIncRegex = /^(config\/)?((\{\w*,?)?cmscustom(,?\w*\})?\/|\*\/)?\*\*/;
|
|
95
95
|
const tdiPattern = tdiIncRegex.test(transferPattern) ? transferPattern.replace(tdiIncRegex, `${this.deliveryPack ? 'config/' : ''}cmscustom/tdi/**`) : 'nomatch';
|
|
96
96
|
|
|
97
97
|
if (!transferPattern)
|
|
@@ -32,7 +32,7 @@ const remote = {
|
|
|
32
32
|
do(sshI) {
|
|
33
33
|
let executing = Math.min(this.queue.length, c.server.ftpConfig.parallel);
|
|
34
34
|
|
|
35
|
-
if (executing
|
|
35
|
+
if (executing===0) { // close connection and return callback after last exec command
|
|
36
36
|
sshI.dispose();
|
|
37
37
|
_write();
|
|
38
38
|
}
|
|
@@ -48,14 +48,14 @@ const remote = {
|
|
|
48
48
|
else _info(command[1] || command[0], true);
|
|
49
49
|
|
|
50
50
|
executing--;
|
|
51
|
-
if (executing
|
|
51
|
+
if (executing===0) this.do(sshI);
|
|
52
52
|
})
|
|
53
|
-
.catch(
|
|
53
|
+
.catch(() => {
|
|
54
54
|
_warn(`Server aborted connection. Retrying.`);
|
|
55
55
|
this.queue.unshift(command);
|
|
56
56
|
|
|
57
57
|
executing--;
|
|
58
|
-
if (executing
|
|
58
|
+
if (executing===0) this.do(sshI);
|
|
59
59
|
});
|
|
60
60
|
});
|
|
61
61
|
},
|
|
@@ -189,7 +189,7 @@ const transfer = (paths, lrServer) => {
|
|
|
189
189
|
remote.process();
|
|
190
190
|
|
|
191
191
|
if (lrServer) { // reload specific resources if files are all image or css, else reload page
|
|
192
|
-
const reloadPage = !files.every(f => /.(css|jpe?g|png|gif)$/i.test(f));
|
|
192
|
+
const reloadPage = !files.every(f => /.(?:css|jpe?g|png|gif)$/i.test(f));
|
|
193
193
|
lrServer.changed({params: {files: reloadPage ? ['page'] : files}});
|
|
194
194
|
_info(`${reloadPage ? 'Page' : 'Resources'} reloaded\n`);
|
|
195
195
|
}
|
|
@@ -90,7 +90,7 @@ module.exports = function deploy (argv) {
|
|
|
90
90
|
if (!path.parse(filepath).base.match(/\.scss/)) {
|
|
91
91
|
const rp = c.getRemotePath(filepath);
|
|
92
92
|
const msg = 'Removed: ' + rp.replace(c.server.remotedir, '').white;
|
|
93
|
-
if (c.localTransfer) del([rp], {force: true}).then(
|
|
93
|
+
if (c.localTransfer) del([rp], {force: true}).then(() => _info(msg, true));
|
|
94
94
|
else remote.add(`rm -rf "${rp}"`, msg).process();
|
|
95
95
|
}
|
|
96
96
|
}
|
|
@@ -38,7 +38,7 @@ const cmdExec = command => new Promise((resolve) => {
|
|
|
38
38
|
});
|
|
39
39
|
cp.stderr.setEncoding('utf8');
|
|
40
40
|
cp.stderr.on('data', data => log(data.replace(/Browserslist: caniuse-lite .*/s, '')));
|
|
41
|
-
cp.on('close',
|
|
41
|
+
cp.on('close', () => {
|
|
42
42
|
_write();
|
|
43
43
|
resolve();
|
|
44
44
|
});
|
|
@@ -49,15 +49,15 @@ module.exports = {
|
|
|
49
49
|
|
|
50
50
|
init ([fdt, fontoVersion]) {
|
|
51
51
|
return new Promise((resolve, reject) => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
_info('Ensure symlink exists:');
|
|
53
|
+
fs.symlink(path.join('../../../tdi/fonto/packages-shared'), 'packages-shared', 'dir', err => {
|
|
54
|
+
if (err && err.code!='EEXIST') reject(err.code=='EPERM' ? 'Start your console as admin for symlink creation!' : err);
|
|
55
|
+
else _write('Done.\n');
|
|
56
|
+
resolve();
|
|
57
|
+
});
|
|
58
|
+
})
|
|
59
|
+
.then(() => cmdExec(`${fdt} editor upgrade --version ${fontoVersion} --non-interactive`))
|
|
60
|
+
.then(() => [fdt])
|
|
61
61
|
;
|
|
62
62
|
},
|
|
63
63
|
|
|
@@ -77,7 +77,7 @@ module.exports = {
|
|
|
77
77
|
|
|
78
78
|
// add dependency for sx-module to each package fonto-manifest.json
|
|
79
79
|
// add schema packages in fonto.json to config/fonto-manifest.json
|
|
80
|
-
Object.entries(rootSchemas).forEach(([
|
|
80
|
+
Object.entries(rootSchemas).forEach(([, obj]) => {
|
|
81
81
|
const pckPath = 'packages/' + obj.packageName;
|
|
82
82
|
const pckFontoManifest = fs.readJsonSync(pckPath + '/fonto-manifest.json', {throws: false}) || {dependencies: {}};
|
|
83
83
|
pckFontoManifest.dependencies['sx-module'] = 'packages/sx-module';
|
|
@@ -100,8 +100,8 @@ module.exports = {
|
|
|
100
100
|
_info('Copying schema json files to build folders:');
|
|
101
101
|
// deploy-watch copies schema's to server directly, so update local builds to keep integrity
|
|
102
102
|
jsonPaths.forEach(jsonPath => {
|
|
103
|
-
fs.copySync(jsonPath, 'dev/assets/schemas/' + jsonPath.match(/[
|
|
104
|
-
fs.copySync(jsonPath, 'dist/assets/schemas/' + jsonPath.match(/[
|
|
103
|
+
fs.copySync(jsonPath, 'dev/assets/schemas/' + jsonPath.match(/[^/]*$/)[0]);
|
|
104
|
+
fs.copySync(jsonPath, 'dist/assets/schemas/' + jsonPath.match(/[^/]*$/)[0]);
|
|
105
105
|
});
|
|
106
106
|
_write('Done.\n');
|
|
107
107
|
})
|
|
@@ -125,7 +125,7 @@ module.exports = {
|
|
|
125
125
|
|
|
126
126
|
// execute "fdt elements" for each schema package, ignore default elements, and combine results
|
|
127
127
|
const schemasPerElement = {};
|
|
128
|
-
Object.entries(rootSchemas).forEach(([
|
|
128
|
+
Object.entries(rootSchemas).forEach(([, obj]) => {
|
|
129
129
|
const data = execSync(`${fdt} elements --schema packages/${obj.packageName}/src/assets/schemas/${obj.packageName}.json -C name`, {encoding: 'UTF-8'});
|
|
130
130
|
const elements = data.replace(/(^.*?Name\*)|(Printed name\*.*$)/gs, '').split(/\s+/);
|
|
131
131
|
const customElements = [...new Set(elements)].filter(e => e && !ignoreElements.includes(e));
|
|
@@ -142,19 +142,19 @@ module.exports = {
|
|
|
142
142
|
};
|
|
143
143
|
|
|
144
144
|
return wws(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
145
|
+
'searching',
|
|
146
|
+
findElements,
|
|
147
|
+
{
|
|
148
|
+
fdt,
|
|
149
|
+
rootSchemas: fs.readJsonSync('../schema/fonto.json').rootSchemas,
|
|
150
|
+
tdiXsdPath: path.join(_paths.repo, _paths.tdi, 'src/config/cmscustom/[customer]/[project]/schema/xsd/')
|
|
151
|
+
},
|
|
152
|
+
result => {
|
|
153
|
+
if (Array.isArray(result)) result.forEach(([e, s]) => _write(e, s, '\n'));
|
|
154
|
+
else _write(result, '\n');
|
|
155
|
+
}
|
|
156
|
+
)
|
|
157
|
+
.then(() => [fdt])
|
|
158
158
|
;
|
|
159
159
|
},
|
|
160
160
|
|
|
@@ -174,7 +174,7 @@ module.exports = {
|
|
|
174
174
|
|
|
175
175
|
// execute "fdt attributes" for each schema package, ignore default attributes, and combine results
|
|
176
176
|
const schemasPerAttribute = {};
|
|
177
|
-
Object.entries(rootSchemas).forEach(([
|
|
177
|
+
Object.entries(rootSchemas).forEach(([, obj]) => {
|
|
178
178
|
const data = execSync(`${fdt} attributes --schema packages/${obj.packageName}/src/assets/schemas/${obj.packageName}.json`, {encoding: 'UTF-8'});
|
|
179
179
|
const attributes = data.replace(/(^.*?Default value\s+)|(\s+Printed name\*.*$)/gs, '').split(/\n\s+/).map(a => a.split(/\s+/)).map(a =>
|
|
180
180
|
a[0] + (a[2]=='required' ? ' (required)' : '') + (a[3]=='-' ? ' (no default)' : '')
|
|
@@ -193,19 +193,19 @@ module.exports = {
|
|
|
193
193
|
};
|
|
194
194
|
|
|
195
195
|
return wws(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
196
|
+
'searching',
|
|
197
|
+
findAttributes,
|
|
198
|
+
{
|
|
199
|
+
fdt,
|
|
200
|
+
rootSchemas: fs.readJsonSync('../schema/fonto.json').rootSchemas,
|
|
201
|
+
tdiXsdPath: path.join(_paths.repo, _paths.tdi, 'src/config/cmscustom/[customer]/[project]/schema/xsd/')
|
|
202
|
+
},
|
|
203
|
+
result => {
|
|
204
|
+
if (Array.isArray(result)) result.forEach(([e, s]) => _write(e, s, '\n'));
|
|
205
|
+
else _write(result, '\n');
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
.then(() => [fdt])
|
|
209
209
|
;
|
|
210
210
|
},
|
|
211
211
|
|
|
@@ -33,7 +33,7 @@ module.exports = function fonto (argv) {
|
|
|
33
33
|
.map(p => ([p.replace('manifest.json', ''), fs.readJsonSync(p).sdkVersion.replace(/Nightlies.*/, 'nightly')])
|
|
34
34
|
);
|
|
35
35
|
|
|
36
|
-
if (fontoPaths.length
|
|
36
|
+
if (fontoPaths.length===0) _error('No Fonto instance found.');
|
|
37
37
|
|
|
38
38
|
let promiseChain = Promise.resolve(); // for sequentially executing commands for each fonto instance
|
|
39
39
|
|
|
@@ -47,27 +47,27 @@ module.exports = function fonto (argv) {
|
|
|
47
47
|
|
|
48
48
|
// execute commands sequentially and in correct order
|
|
49
49
|
return new Promise((resolve) => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
50
|
+
_info('Determining Fonto version:');
|
|
51
|
+
let fontoVersionNew = typeof argv.init == 'string' ? argv.init : fontoVersionCurrent;
|
|
52
|
+
|
|
53
|
+
if (fontoVersionNew == 'latest') {
|
|
54
|
+
const data = execSync(`${fdtCommand(fontoVersionCurrent)} editor versions`, {encoding: 'UTF-8'});
|
|
55
|
+
fontoVersionNew = data.match(/\d+\.\d+\.\d+/g).filter(v => allowedFontoVersionRegex.test(v))[0];
|
|
56
|
+
}
|
|
57
|
+
else if (!allowedFontoVersionRegex.test(fontoVersionNew)) {
|
|
58
|
+
_error(`Fonto version ${fontoVersionNew} is not compatible with the current TDI submodule commit!\nExecute: tct fonto --init latest`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
_write(fontoVersionNew+'\n');
|
|
62
|
+
return resolve([fdtCommand(fontoVersionNew), fontoVersionNew]);
|
|
63
|
+
})
|
|
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
|
@@ -2,7 +2,10 @@ const {execSync, spawn} = require('child_process');
|
|
|
2
2
|
const {chdir} = require('process');
|
|
3
3
|
const inquirer = require('inquirer');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const fs = require('fs-extra');
|
|
5
6
|
|
|
7
|
+
const execGitCommand = require('../../lib/exec-git-command');
|
|
8
|
+
const getTdiBranch = require('../../lib/get-tdi-branch');
|
|
6
9
|
|
|
7
10
|
const cmdExec = commands => new Promise(resolve => {
|
|
8
11
|
let promise = new Promise(resolve => resolve());
|
|
@@ -21,7 +24,7 @@ const cmdExec = commands => new Promise(resolve => {
|
|
|
21
24
|
cp.stdout.on('data', log);
|
|
22
25
|
cp.stderr.setEncoding('utf8');
|
|
23
26
|
cp.stderr.on('data', log);
|
|
24
|
-
cp.on('close',
|
|
27
|
+
cp.on('close', () => {
|
|
25
28
|
if (!a[i+1] || a[i+1][1]) _write();
|
|
26
29
|
resolve();
|
|
27
30
|
});
|
|
@@ -33,6 +36,8 @@ const cmdExec = commands => new Promise(resolve => {
|
|
|
33
36
|
});
|
|
34
37
|
});
|
|
35
38
|
|
|
39
|
+
const tdiMigrationFilePath = path.join(_paths.repo, _paths.tdi, 'tct/git/tdiCommitsRequiringMigration.js');
|
|
40
|
+
|
|
36
41
|
module.exports = function git (argv) {
|
|
37
42
|
|
|
38
43
|
if (argv.init) {
|
|
@@ -112,4 +117,93 @@ module.exports = function git (argv) {
|
|
|
112
117
|
throw e;
|
|
113
118
|
});
|
|
114
119
|
}
|
|
120
|
+
|
|
121
|
+
if (argv.update) {
|
|
122
|
+
const [update, tdiFromDateCustom, tdiToDateCustom] = `${argv.update}`.split(/\s/);
|
|
123
|
+
|
|
124
|
+
if (update === `tdi`) {
|
|
125
|
+
|
|
126
|
+
const tdiBranch = getTdiBranch(argv.branch);
|
|
127
|
+
|
|
128
|
+
if (tdiBranch.from) _info(`Current branch '${tdiBranch.from.name}' will be updated to '${tdiBranch.name}'\nCommon ancestor will be used in selecting TDI commits requiring migration: ${_formatDate(tdiBranch.commonAncestor.date)}`);
|
|
129
|
+
_info(`Branch ${tdiBranch.name} is ${tdiBranch.commitsBehind} commits behind.`);
|
|
130
|
+
|
|
131
|
+
// Set branch in .gitmodules file; This ensures submodule update will follow this branch to the latest commit
|
|
132
|
+
const setBranch = execGitCommand(`submodule set-branch -b ${tdiBranch.name} tangelo-default-implementation`, _paths.repo);
|
|
133
|
+
if (setBranch.error) _error(`Set branch failed: ${setBranch.error}`);
|
|
134
|
+
|
|
135
|
+
if (tdiBranch.commitsBehind > 0 || tdiFromDateCustom) {
|
|
136
|
+
// update submodule
|
|
137
|
+
const updateSubmoduleMsg = execGitCommand(`submodule update --remote`, _paths.repo);
|
|
138
|
+
if (updateSubmoduleMsg.error && !tdiFromDateCustom) _error(`Update submodule failed\n${updateSubmoduleMsg.error}`);
|
|
139
|
+
|
|
140
|
+
if (!tdiFromDateCustom) _info(`TDI submodule updated:\n${updateSubmoduleMsg}`);
|
|
141
|
+
|
|
142
|
+
// tdiMigrationFilePath should exist in latest commits of releases 5.3+; As we updated to the latest version this should work
|
|
143
|
+
if (fs.existsSync(tdiMigrationFilePath)) {
|
|
144
|
+
const migrations = require(tdiMigrationFilePath);
|
|
145
|
+
const fromTdiDate = tdiFromDateCustom ? new Date(tdiFromDateCustom) : (tdiBranch.commonAncestor) ? tdiBranch.commonAncestor.date: _git.commitTdi.local().date;
|
|
146
|
+
const toTdiDate = tdiToDateCustom ? new Date(tdiToDateCustom) : new Date(execGitCommand(`log -1 --format=%cd --date=iso-strict`, path.join(_paths.repo, _paths.tdi), ['date']).date);
|
|
147
|
+
|
|
148
|
+
_info(`TDI commits requiring migration between ${_formatDate(fromTdiDate)} and ${_formatDate(toTdiDate)}`);
|
|
149
|
+
// Filter the migrations that should be applied/considered; Also display older releases migrations
|
|
150
|
+
const migrationsFiltered = migrations
|
|
151
|
+
.filter((m) => m.releases.find((r) => {
|
|
152
|
+
const time = new Date(r.date).getTime();
|
|
153
|
+
return ((tdiBranch.from)
|
|
154
|
+
? `release/${r.release}` == tdiBranch.name || (tdiBranch.from.name <= `release/${r.release}` && `release/${r.release}` < tdiBranch.name && (fromTdiDate.getTime() < time && time < toTdiDate.getTime()))
|
|
155
|
+
: `release/${r.release}` == tdiBranch.name && (fromTdiDate.getTime() < time && time < toTdiDate.getTime())
|
|
156
|
+
);
|
|
157
|
+
})
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
let relevantMigrationCount = 0;
|
|
161
|
+
// Apply callback for migrations
|
|
162
|
+
migrationsFiltered
|
|
163
|
+
.forEach((m) => {
|
|
164
|
+
const date = _formatDate(new Date(m.releases.filter((r) => tdiBranch.name >= `release/${r.release}`)[0].date));
|
|
165
|
+
relevantMigrationCount += (m.callback(date, relevantMigrationCount+1)) === 1 ? 1 : 0;
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Output message based on whether or not migrations are required
|
|
169
|
+
if (migrationsFiltered.length === 0) {
|
|
170
|
+
_write(`-- No commits require migrations --`);
|
|
171
|
+
} else {
|
|
172
|
+
_info(`\n${relevantMigrationCount} relevant migration(s) out of ${migrationsFiltered.length} migration(s) are shown.`);
|
|
173
|
+
_info(`See confluence page 'TDI commits requiring migration' for more detailed information`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
} else {
|
|
177
|
+
_error(`Cannot find required files in TDI submodule.`);
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
_info(`Your TDI submodule is already up to date`);
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
if (argv.branch) _warn(`Branch argument '${argv.branch}' is ignored for a repository update.`);
|
|
184
|
+
|
|
185
|
+
// Fetch all
|
|
186
|
+
_info(`Fetch all`);
|
|
187
|
+
const cmdFetch = execGitCommand('fetch -pf --all', path.join(_paths.repo));
|
|
188
|
+
if (cmdFetch.error) _warn(`Fetch failed\n${cmdFetch.error}`);
|
|
189
|
+
|
|
190
|
+
// pull repo
|
|
191
|
+
_info(`Pull repository`);
|
|
192
|
+
const cmdPull = execGitCommand(`pull`, _paths.repo);
|
|
193
|
+
if (cmdPull.error) _warn(`Pull failed\n${cmdPull.error}`);
|
|
194
|
+
|
|
195
|
+
_info(`Checked out at commit:`);
|
|
196
|
+
const repoLog = execGitCommand(`log -1 --format=%D;%H;%cd --date=iso-strict`, _paths.repo, ['tags', 'hash', 'date']);
|
|
197
|
+
_write(`${_formatDate(repoLog.date)} - ${repoLog.tags} - ${repoLog.hash}`);
|
|
198
|
+
|
|
199
|
+
_info(`Retrieve submodules that belong to this repo-commit`); // update submodule recursively (set to version that belongs to repo)
|
|
200
|
+
_write(execGitCommand(`submodule update --recursive`, path.join(_paths.repo)));
|
|
201
|
+
_info(`Submodule checked out at commit:`);
|
|
202
|
+
const tdiLog = execGitCommand(`log -1 --format=%D;%H;%cd --date=iso-strict`, path.join(_paths.repo, _paths.tdi), ['tags', 'hash', 'date']);
|
|
203
|
+
_write(`${_formatDate(tdiLog.date)} - ${tdiLog.tags} - ${tdiLog.hash}`);
|
|
204
|
+
const tdiBranch = getTdiBranch();
|
|
205
|
+
|
|
206
|
+
_info(`\nTDI branch ${tdiBranch.name} is ${tdiBranch.commitsBehind} commits behind.\nUpdate TDI to latest version with 'tct g -u tdi'`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
115
209
|
};
|
|
@@ -4,6 +4,7 @@ const path = require('path');
|
|
|
4
4
|
const {Table} = require('console-table-printer');
|
|
5
5
|
|
|
6
6
|
const execGitCommand = require('../../lib/exec-git-command');
|
|
7
|
+
const getTdiBranch = require('../../lib/get-tdi-branch');
|
|
7
8
|
const c = require('../deploy/config');
|
|
8
9
|
const {remote} = require('../deploy/execute');
|
|
9
10
|
|
|
@@ -18,30 +19,28 @@ const getGitInfo = () => {
|
|
|
18
19
|
});
|
|
19
20
|
|
|
20
21
|
// Fetch all
|
|
21
|
-
execGitCommand('fetch -pf --all', path.join(_paths.repo, _paths.tdi));
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
releaseBranches.sort((a, b) => a.name > b.name ? 1 : -1);
|
|
28
|
-
const firstBranch = {name: releaseBranches[0]};
|
|
22
|
+
const cmdFetch = execGitCommand('fetch -pf --all', path.join(_paths.repo, _paths.tdi));
|
|
23
|
+
if (cmdFetch.error) _warn(`Fetch failed\n${cmdFetch.error}`);
|
|
24
|
+
|
|
25
|
+
// Set branch name of firstBranch without 'remotes/origin/'
|
|
26
|
+
const tdiBranch = getTdiBranch();
|
|
27
|
+
|
|
29
28
|
// Get number of commits behind
|
|
30
|
-
|
|
29
|
+
tdiBranch.commitsBehind = execGitCommand(`rev-list HEAD...origin/${tdiBranch.name} --count`, path.join(_paths.repo, _paths.tdi));
|
|
31
30
|
|
|
32
31
|
// Create table rows for TDI submodule info
|
|
33
32
|
gitSubmoduleInfo.addRow({
|
|
34
33
|
property: 'Commit date',
|
|
35
|
-
value: _git.commitTdi.local().date
|
|
34
|
+
value: _formatDate(_git.commitTdi.local().date)
|
|
36
35
|
});
|
|
37
|
-
if (
|
|
36
|
+
if (tdiBranch) {
|
|
38
37
|
gitSubmoduleInfo.addRow({
|
|
39
38
|
property: 'Branch',
|
|
40
|
-
value:
|
|
39
|
+
value: tdiBranch.name
|
|
41
40
|
});
|
|
42
41
|
gitSubmoduleInfo.addRow({
|
|
43
42
|
property: 'Commits behind',
|
|
44
|
-
value:
|
|
43
|
+
value: tdiBranch.commitsBehind
|
|
45
44
|
});
|
|
46
45
|
} else {
|
|
47
46
|
gitSubmoduleInfo.addRow({
|
|
@@ -174,7 +173,7 @@ module.exports = function info (argv) {
|
|
|
174
173
|
const id = dtRow.match(/(\d+) id/)?.[1];
|
|
175
174
|
const name = dtRow.match(/'([^']+)' display_name/)?.[1];
|
|
176
175
|
const dbPath = p.match(/(database\/config\/(:?.*)\/)txd_document_types.sql/i)?.[1];
|
|
177
|
-
const prPath = ntSqlInsert.match(/'([^']+)' xsl_prep_inc/)[1].replace('prepare_xincludes.xsl','');
|
|
176
|
+
const prPath = ntSqlInsert.match(/'([^']+)' xsl_prep_inc/)[1].replace('prepare_xincludes.xsl', '');
|
|
178
177
|
|
|
179
178
|
doctypesInfo.addRows([
|
|
180
179
|
{id, name, paths: 'config/cmscustom/'+ prPath},
|
|
@@ -62,7 +62,7 @@ module.exports = function migrate (argv) {
|
|
|
62
62
|
if (p.path_dbconfig.join('/') != p.path_cmscustom.join('/')) projectFolders.push(p.path_cmscustom.join('/'));
|
|
63
63
|
});
|
|
64
64
|
// Set filter to chosen active documents
|
|
65
|
-
filter = projectFolders.length
|
|
65
|
+
filter = projectFolders.length === 1 ? `**/${projectFolders}/**` : `**/{${projectFolders.join(',')}}/**`;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
_info(`Filter applied: ${filter}`);
|
|
@@ -54,7 +54,7 @@ module.exports = function steps (step, dry, filter) {
|
|
|
54
54
|
let filesModCount = 0;
|
|
55
55
|
for (let i=0; i<20 && r.files[0]; i++) { // execute repeatedly for modified files only (with safety limit of 20)
|
|
56
56
|
r.files = rif.sync(r).filter(f => f.hasChanged).map(f => f.file);
|
|
57
|
-
if (i
|
|
57
|
+
if (i===0) filesModCount = r.files.length; // save count only after first run (after this only subsets are processed)
|
|
58
58
|
if (dry) break;
|
|
59
59
|
}
|
|
60
60
|
|
package/src/modules/sql/index.js
CHANGED
|
@@ -18,9 +18,9 @@ const checkLog = (logFilePath, remove=true) => {
|
|
|
18
18
|
if (fs.existsSync(logFilePath)) {
|
|
19
19
|
const logFile = fs.readFileSync(logFilePath).toString();
|
|
20
20
|
// Check for ORA- error messages
|
|
21
|
-
if (logFile.match(
|
|
21
|
+
if (logFile.match(/ORA-/g)) {
|
|
22
22
|
// Select each ORA- error message and up to 6 lines before (the lines before may not contain ORA- themselves)
|
|
23
|
-
const errors = logFile.match(
|
|
23
|
+
const errors = logFile.match(/(?:(?![^\n]*ORA-)[^\n]*\n){0,6}ORA-[^\n\r]*/gms);
|
|
24
24
|
_info(`${errors.length} error(s) during SQL script:\n`);
|
|
25
25
|
// Print each error + lines before:
|
|
26
26
|
errors.forEach((e, i) => {
|