@tangelo/tangelo-configuration-toolkit 1.17.1 → 1.18.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/README.md +32 -0
- package/index.js +18 -45
- package/package.json +1 -1
- package/src/cli.js +7 -7
- package/src/lib/get-repoconfig.js +4 -8
- package/src/lib/get-tdi-branch.js +9 -7
- package/src/lib/gulp-batch-replace-with-filter.js +3 -3
- package/src/lib/memoize.js +28 -0
- package/src/lib/style-string-getters.js +9 -6
- package/src/modules/build/index.js +0 -38
- package/src/modules/deploy/config.js +128 -133
- package/src/modules/deploy/execute.js +2 -2
- package/src/modules/deploy/index.js +6 -6
- package/src/modules/fonto/commands.js +1 -1
- package/src/modules/fonto/index.js +1 -1
- package/src/modules/git/index.js +181 -162
package/README.md
CHANGED
|
@@ -60,3 +60,35 @@ The `build -x` commands set projects transformation scenarios and masterfiles in
|
|
|
60
60
|
- Will try to preserve manually added entries in the transformation scenarios and masterfiles
|
|
61
61
|
- Will remove non existing masterfiles or masterfiles that start with a '_'
|
|
62
62
|
- No masterfiles / scenarios will be added if their path match with oXygens hidden directory patterns
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
## Debug
|
|
66
|
+
|
|
67
|
+
Save this in the root of your local clone as `tct-dev.cmd`:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
@ECHO off
|
|
71
|
+
SETLOCAL
|
|
72
|
+
CALL :find_dp0
|
|
73
|
+
|
|
74
|
+
IF EXIST "%dp0%\node.exe" (
|
|
75
|
+
SET "_prog=%dp0%\node.exe"
|
|
76
|
+
) ELSE (
|
|
77
|
+
SET "_prog=node"
|
|
78
|
+
SET PATHEXT=%PATHEXT:;.JS;=;%
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
"%_prog%" "%dp0%\index.js" %*
|
|
82
|
+
ENDLOCAL
|
|
83
|
+
EXIT /b %errorlevel%
|
|
84
|
+
:find_dp0
|
|
85
|
+
SET dp0=%~dp0
|
|
86
|
+
EXIT
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Then:
|
|
90
|
+
|
|
91
|
+
- open a 'Javascript Debug Terminal' in VSCode
|
|
92
|
+
- jump to a customer folder ( you can use `tan-cust` for this)
|
|
93
|
+
- set a breakpoint
|
|
94
|
+
- type (for instance): `tct-dev d -c`
|
package/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
global._appStartTime = new Date();
|
|
2
|
+
|
|
1
3
|
require('./src/lib/style-string-getters');
|
|
2
4
|
|
|
3
5
|
String.prototype.toFws = function(){
|
|
@@ -12,6 +14,7 @@ const homedir = require('os').homedir();
|
|
|
12
14
|
const path = require('path');
|
|
13
15
|
|
|
14
16
|
const execGitCommand = require('./src/lib/exec-git-command');
|
|
17
|
+
const memoize = require('./src/lib/memoize');
|
|
15
18
|
|
|
16
19
|
|
|
17
20
|
const appname = 'tangelo-configuration-toolkit';
|
|
@@ -50,6 +53,9 @@ global._formatDate = date =>
|
|
|
50
53
|
;
|
|
51
54
|
|
|
52
55
|
|
|
56
|
+
global._devmode = !__dirname.includes('node_modules');
|
|
57
|
+
|
|
58
|
+
|
|
53
59
|
global._packages = {
|
|
54
60
|
TCT: {name: '@tangelo/tangelo-configuration-toolkit', version: require('./package.json')?.version},
|
|
55
61
|
FDT: {name: '@fontoxml/fontoxml-development-tools'}
|
|
@@ -76,15 +82,6 @@ if (!_appdata.npmPath) {
|
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
|
|
79
|
-
if (!fs.existsSync(_paths.appconfig)) { // try to find old appconfig (TCT-162 => remove code block in next version)
|
|
80
|
-
fs.ensureDirSync(_paths.apphome);
|
|
81
|
-
const oldPathAppconfig = findUp.sync(appname+'-appconfig.json');
|
|
82
|
-
if (oldPathAppconfig) {
|
|
83
|
-
fs.renameSync(oldPathAppconfig, _paths.appconfig);
|
|
84
|
-
_info(`${appname}-appconfig.json moved to ${_paths.apphome}`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
85
|
global._appconfig = { // default appconfig if file not found
|
|
89
86
|
'sharedConfigPath': path.join(homedir, 'Dropbox (Tangelo Software)/Product Distribution/tooling'),
|
|
90
87
|
'servers': [],
|
|
@@ -105,6 +102,9 @@ catch({code, message}) {
|
|
|
105
102
|
}
|
|
106
103
|
|
|
107
104
|
|
|
105
|
+
global._repoconfig = require('./src/lib/get-repoconfig')(_paths.repo);
|
|
106
|
+
|
|
107
|
+
|
|
108
108
|
_appconfig.sharedConfigPath = path.resolve(_paths.appconfig, '..', _appconfig.sharedConfigPath || '', appname+'-appconfig.json');
|
|
109
109
|
_appconfig.shared = fs.readJsonSync(_appconfig.sharedConfigPath, {throws: false}) || {};
|
|
110
110
|
|
|
@@ -114,49 +114,20 @@ global._git = {
|
|
|
114
114
|
if (!_appdata.gitUser) _appdata._update({gitUser: execGitCommand(`config --get user.email`, _paths.repo)});
|
|
115
115
|
return _appdata.gitUser;
|
|
116
116
|
},
|
|
117
|
-
commitLocal (
|
|
118
|
-
|
|
119
|
-
this.cache ??= execGitCommand(`log -1 --format=%D;%H;%cd --date=iso-strict`, _paths.repo, ['branch', 'hash', 'date']);
|
|
120
|
-
return this.cache;
|
|
121
|
-
},
|
|
122
|
-
commitRemote () {
|
|
123
|
-
this.cache ??= execGitCommand('log -1 --format=%cd --date=iso-strict origin/'+_git.commitLocal().branch, _paths.repo, ['date']);
|
|
124
|
-
return this.cache;
|
|
125
|
-
},
|
|
117
|
+
commitLocal: memoize(() => execGitCommand(`log -1 --format=%D;%H;%cd --date=iso-strict`, _paths.repo, ['branch', 'hash', 'date'])),
|
|
118
|
+
commitRemote: memoize(() => execGitCommand('log -1 --format=%cd --date=iso-strict origin/' + _git.commitLocal().branch, _paths.repo, ['date'])),
|
|
126
119
|
commitTdi: {
|
|
127
|
-
local (
|
|
128
|
-
|
|
129
|
-
this.cache ??= execGitCommand(`log -1 --format=%D;%H;%cd --date=iso-strict`, path.join(_paths.repo, _paths.tdi), ['tags', 'hash', 'date']);
|
|
130
|
-
return this.cache;
|
|
131
|
-
},
|
|
132
|
-
after (commitHash) {
|
|
133
|
-
this['cache-'+commitHash] ??= execGitCommand(`merge-base --is-ancestor ${commitHash} ${_git.commitTdi.local().hash}`, path.join(_paths.repo, _paths.tdi), null, 0);
|
|
134
|
-
return this['cache-'+commitHash];
|
|
135
|
-
},
|
|
120
|
+
local: memoize(() => execGitCommand(`log -1 --format=%D;%H;%cd --date=iso-strict`, path.join(_paths.repo, _paths.tdi), ['tags', 'hash', 'date'])),
|
|
121
|
+
after: memoize(commitHash => execGitCommand(`merge-base --is-ancestor ${commitHash} ${_git.commitTdi.local().hash}`, path.join(_paths.repo, _paths.tdi), null, 0)),
|
|
136
122
|
fontoVersions: [ // latest commits first
|
|
137
123
|
{commitHash: 'e599766', regex: /^7\.1[45]\./},
|
|
138
124
|
{commitHash: '77b8ea9', regex: /^7\.14\./}, // 7.14.x
|
|
139
125
|
{commitHash: '8066c44', regex: /^7\.13\./}, // 7.13.x
|
|
140
126
|
{commitHash: 'a2b2d4e', regex: /^7\.12\./} // 7.12.x
|
|
141
|
-
]
|
|
142
|
-
stopUsingRepoconfigFile: '1f12bff' // release/5.1 10-05-2021
|
|
127
|
+
]
|
|
143
128
|
}
|
|
144
129
|
};
|
|
145
130
|
|
|
146
|
-
|
|
147
|
-
if (_git.commitTdi.after(_git.commitTdi.stopUsingRepoconfigFile)) {
|
|
148
|
-
global._repoconfig = require('./src/lib/get-repoconfig')(_paths.repo);
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
try { global._repoconfig = fs.existsSync(_paths.repoconfig) && fs.readJsonSync(_paths.repoconfig) || {}; }
|
|
152
|
-
catch ({message}) { _error('Error in '+message); }
|
|
153
|
-
global._repoconfig.update = obj => {
|
|
154
|
-
global._repoconfig = Object.assign(_repoconfig, obj);
|
|
155
|
-
fs.writeJsonSync(_paths.repoconfig, _repoconfig, {spaces: 2});
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
|
|
160
131
|
global._tdiSubmoduleExists = () => fs.existsSync(path.join(_paths.repo, _paths.tdi));
|
|
161
132
|
global._isPre42 = () => fs.existsSync(path.join(_paths.repo, _paths.tdi, 'create_new_project')); // folder changed in 4.2
|
|
162
133
|
global._isPre51 = () => !fs.existsSync(path.join(_paths.repo, _paths.tdi, 'src')); // folder changed in 5.1 (check new folder because old one could still exist after branch switch)
|
|
@@ -175,7 +146,7 @@ global._modulesTdi = {
|
|
|
175
146
|
this.depsUpToDate = true;
|
|
176
147
|
},
|
|
177
148
|
require(module) {
|
|
178
|
-
this.ensureDepsUpToDate();
|
|
149
|
+
if (path.extname(module) !== '.json') this.ensureDepsUpToDate();
|
|
179
150
|
_info(`Loading ${path.join(_paths.tdi, 'tct', module)}\n`);
|
|
180
151
|
try {
|
|
181
152
|
return require(path.join(this.absolutePathTdi, 'tct', module));
|
|
@@ -198,7 +169,9 @@ process.on('beforeExit', () => {
|
|
|
198
169
|
fs.writeJsonSync(_paths.appdata, _appdata, {spaces: 2});
|
|
199
170
|
}
|
|
200
171
|
|
|
172
|
+
if (_devmode) _perf(_appStartTime); // output execution time in dev mode
|
|
173
|
+
|
|
201
174
|
});
|
|
202
175
|
|
|
203
176
|
|
|
204
|
-
require('./src/cli.js')();
|
|
177
|
+
require('./src/cli.js')();
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -34,7 +34,6 @@ module.exports = function cli () {
|
|
|
34
34
|
init: {alias: 'i', desc: 'Create repository content structure', conflicts: ['p', 's', 'c']},
|
|
35
35
|
project: {alias: 'p', desc: 'Create project configuration', conflicts: ['i', 's', 'c']},
|
|
36
36
|
symlinks: {alias: 's', desc: 'Recreate symlinks to TDI', conflicts: ['p', 'i', 'c']},
|
|
37
|
-
config: {alias: 'c', desc: 'Create repo-config file', conflicts: ['p', 'i', 's'], hidden: true},
|
|
38
37
|
oxygen: {alias: 'x', desc: 'Create or update oXygen project file (.xpr)', conflicts: ['p', 'i', 'c']}
|
|
39
38
|
},
|
|
40
39
|
handler: require('./modules/build')
|
|
@@ -76,8 +75,9 @@ module.exports = function cli () {
|
|
|
76
75
|
init: {alias: 'i', desc: 'Initialize repository and add submodule', conflicts: ['r', 'c', 'u']},
|
|
77
76
|
reset: {alias: 'r', desc: 'Reset repository to last commit', conflicts: ['i', 'c', 'u']},
|
|
78
77
|
clone: {alias: 'c', desc: 'Clone a client repository and do basic setup', conflicts: ['i', 'r', 'u']},
|
|
79
|
-
update: {alias: '
|
|
80
|
-
|
|
78
|
+
'update-repo': {alias: 'ur', desc: 'Update repository', conflicts: ['i', 'r', 'c']},
|
|
79
|
+
'update-submodule': {alias: 'us', desc: 'Update TDI submodule, optionally pass release branch (without "release/")', conflicts: ['i', 'r', 'c']},
|
|
80
|
+
dates: {alias: 'd', desc: '[hidden] Use i.c.w. update-submodule, pass 2 dates in format dd-mm-yyyy', conflicts: ['i', 'r', 'c'], implies: 'update-submodule', requiresArg: true, array: true, hidden: !_devmode}
|
|
81
81
|
},
|
|
82
82
|
handler: require('./modules/git')
|
|
83
83
|
})
|
|
@@ -123,9 +123,9 @@ module.exports = function cli () {
|
|
|
123
123
|
['$0 deploy --copy --server demo', 'Copy files to server "demo"'],
|
|
124
124
|
['$0 d -c -s demo', 'Same as above'],
|
|
125
125
|
['$0 f -sb && $0 d -c', 'Compile schema and build Fonto, then deploy to default server'],
|
|
126
|
-
['$0 git --update', 'Pull git repository and submodule to latest repository commit'],
|
|
127
|
-
['$0 git --update
|
|
128
|
-
['$0 git --update
|
|
126
|
+
['$0 git --update-repo', 'Pull git repository and submodule to latest repository commit'],
|
|
127
|
+
['$0 git --update-submodule', 'Update TDI submodule to latest within current TDI branch'],
|
|
128
|
+
['$0 git --update-submodule 5.4', 'Update TDI submodule to latest in specified branch']
|
|
129
129
|
])
|
|
130
130
|
.check((argv, options) => {
|
|
131
131
|
const nonDefaultOptions = Object.keys(options.key).filter(o => !Object.keys(options.default).includes(o));
|
|
@@ -133,7 +133,7 @@ module.exports = function cli () {
|
|
|
133
133
|
else throw new Error('Pass a non-default option');
|
|
134
134
|
})
|
|
135
135
|
.strict()
|
|
136
|
-
.wrap(
|
|
136
|
+
.wrap(120)
|
|
137
137
|
.parse()
|
|
138
138
|
;
|
|
139
139
|
|
|
@@ -4,11 +4,9 @@ const globby = require('globby');
|
|
|
4
4
|
|
|
5
5
|
module.exports = function getRepoconfig(repoPath) {
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
globby
|
|
7
|
+
return globby
|
|
10
8
|
.sync('database/config/**/txd_document_types.sql', {cwd: repoPath, absolute: true})
|
|
11
|
-
.
|
|
9
|
+
.map(p => {
|
|
12
10
|
const dtSqlInsert = fs.readFileSync(p).toString().match(/select(.*?)from/s)[1];
|
|
13
11
|
const ntSqlInsert = fs.readFileSync(p.replace('txd_document_types', 'txd_node_types')).toString().match(/select(.*?)from/s)[1];
|
|
14
12
|
|
|
@@ -21,7 +19,7 @@ module.exports = function getRepoconfig(repoPath) {
|
|
|
21
19
|
const dnRegex = [path_cmscustom[0].replace(/[_'"]/g, '.'), path_dbconfig[0].replace(/[_'"]/g, '.')].sort((a, b) => b.length - a.length).join('|');
|
|
22
20
|
const customer_name = (dtDisplayName.match(RegExp(dnRegex, 'i')) || dtDisplayName.split(/ (.+)/))[0];
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
return {
|
|
25
23
|
customer_name,
|
|
26
24
|
customer_abbr: ntName.split('_')[0],
|
|
27
25
|
project_name: dtDisplayName.split(RegExp(customer_name.replace(/(\W)/g, '\\$1')))[1].trim() || '',
|
|
@@ -29,9 +27,7 @@ module.exports = function getRepoconfig(repoPath) {
|
|
|
29
27
|
path_cmscustom,
|
|
30
28
|
path_dbconfig,
|
|
31
29
|
id_range: dtSqlInsert.match(/(\d+)\d{3} id/)[1]
|
|
32
|
-
}
|
|
30
|
+
};
|
|
33
31
|
});
|
|
34
32
|
|
|
35
|
-
return repoconfig;
|
|
36
|
-
|
|
37
33
|
};
|
|
@@ -2,22 +2,24 @@ const execGitCommand = require('./exec-git-command');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
|
|
4
4
|
module.exports = function getTdiBranch(toBranchName) {
|
|
5
|
+
const tdiPath = path.join(_paths.repo, _paths.tdi);
|
|
5
6
|
const tdiBranch = {};
|
|
7
|
+
|
|
6
8
|
// Fetch all
|
|
7
9
|
_info(`Fetch TDI submodule`);
|
|
8
|
-
const cmdFetch = execGitCommand('fetch -pf --all',
|
|
10
|
+
const cmdFetch = execGitCommand('fetch -pf --all', tdiPath);
|
|
9
11
|
if (cmdFetch.error) _warn(`Fetch failed\n${cmdFetch.error}`);
|
|
10
12
|
|
|
11
13
|
let toBranch;
|
|
12
14
|
if (toBranchName) {
|
|
13
15
|
// Check if specified branch exists; we will update to this branch
|
|
14
16
|
toBranch = String(toBranchName).replace(/(?:release\/)?(\d+(?:\.[\dxt]+)*)/, `release/$1`);
|
|
15
|
-
const branchExists = execGitCommand(`branch --remote`,
|
|
17
|
+
const branchExists = execGitCommand(`branch --remote`, tdiPath).match(`origin/${toBranch}`);
|
|
16
18
|
if (!branchExists) _error(`TDI branch "${toBranch}" does not exist. Note that TCT can only update to a release branch.`);
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
// Get remote release branches containing TDI HEAD commit
|
|
20
|
-
const releaseBranches = execGitCommand(`branch --all --contains ${_git.commitTdi.local().hash}`,
|
|
22
|
+
const releaseBranches = execGitCommand(`branch --all --contains ${_git.commitTdi.local().hash}`, tdiPath).match(/remotes\/origin\/release\/[^\s]+/gsm);
|
|
21
23
|
if (!releaseBranches || releaseBranches.error) _error(`Could not retrieve TDI release branches`);
|
|
22
24
|
|
|
23
25
|
// Get the first possible branch; prefer release/5.1 over release/5.2:
|
|
@@ -32,13 +34,13 @@ module.exports = function getTdiBranch(toBranchName) {
|
|
|
32
34
|
tdiBranch.from = {name: tdiBranch.name};
|
|
33
35
|
tdiBranch.name = toBranch;
|
|
34
36
|
|
|
35
|
-
const branchHash = execGitCommand(`rev-parse origin/${toBranch}`,
|
|
36
|
-
const commonAncestorHash = execGitCommand(`merge-base ${_git.commitTdi.local().hash} ${branchHash}`,
|
|
37
|
-
const commonAncestorDate = execGitCommand(`show ${commonAncestorHash} --no-patch --format=%cd --date=iso-strict `,
|
|
37
|
+
const branchHash = execGitCommand(`rev-parse origin/${toBranch}`, tdiPath);
|
|
38
|
+
const commonAncestorHash = execGitCommand(`merge-base ${_git.commitTdi.local().hash} ${branchHash}`, tdiPath);
|
|
39
|
+
const commonAncestorDate = execGitCommand(`show ${commonAncestorHash} --no-patch --format=%cd --date=iso-strict `, tdiPath, ['date']).date;
|
|
38
40
|
tdiBranch.commonAncestor = {date: new Date(commonAncestorDate)};
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
// Get number of commits behind
|
|
42
|
-
tdiBranch.commitsBehind = execGitCommand(`rev-list HEAD...origin/${tdiBranch.name} --count`,
|
|
44
|
+
tdiBranch.commitsBehind = execGitCommand(`rev-list HEAD...origin/${tdiBranch.name} --count`, tdiPath);
|
|
43
45
|
return tdiBranch;
|
|
44
46
|
};
|
|
@@ -4,15 +4,15 @@ const es = require('event-stream'), minimatch = require('minimatch'), istextorbi
|
|
|
4
4
|
|
|
5
5
|
const execReplace = (c, s, r) => Buffer.from(s instanceof RegExp ? String(c).replace(s, r) : String(c).split(s).join(r));
|
|
6
6
|
|
|
7
|
-
module.exports =
|
|
7
|
+
module.exports = arr => {
|
|
8
8
|
return es.map((file, callback) => {
|
|
9
9
|
if(file.contents instanceof Buffer) {
|
|
10
|
-
|
|
10
|
+
for (const e of arr) {
|
|
11
11
|
// exec if no glob is passed or if glob matches, and it's a text file
|
|
12
12
|
if ((!e[2] || minimatch(file.path, e[2])) && istextorbinary.isText(file.path, file)) {
|
|
13
13
|
file.contents = execReplace(file.contents, e[0], e[1]);
|
|
14
14
|
}
|
|
15
|
-
}
|
|
15
|
+
}
|
|
16
16
|
}
|
|
17
17
|
callback(null, file);
|
|
18
18
|
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memoizes a function by caching its results based on the arguments.
|
|
3
|
+
* @param {Function} fn - The function to be memoized.
|
|
4
|
+
* @returns {Function} - The memoized function.
|
|
5
|
+
*/
|
|
6
|
+
module.exports = function memoize(fn) {
|
|
7
|
+
let cache = {};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The memoized function that caches the results of the original function based on its arguments.
|
|
11
|
+
* @param {...*} args - The arguments to be passed to the original function.
|
|
12
|
+
* @returns {*} - The result of the original function.
|
|
13
|
+
*/
|
|
14
|
+
const cachedfn = (...args) => {
|
|
15
|
+
const key = JSON.stringify(args);
|
|
16
|
+
cache[key] ??= fn(...args);
|
|
17
|
+
return cache[key];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Clears the cache of the memoized function.
|
|
22
|
+
*/
|
|
23
|
+
cachedfn.clear = () => {
|
|
24
|
+
cache = {};
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return cachedfn;
|
|
28
|
+
};
|
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
const styles = {bold: 1, underline: 4, black: 30, red: 31, green: 32, yellow: 33, blue: 34, magenta: 35, cyan: 36, white: 37, lblack: 90, lred: 91, lgreen: 92, lyellow: 93, lblue: 94, lmagenta: 95, lcyan: 96, lwhite: 97};
|
|
4
4
|
|
|
5
5
|
for (const name in styles ) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
Object.defineProperty(String.prototype, name, {
|
|
7
|
+
get () {
|
|
8
|
+
const _this = this.replace(/\x1b\[0m/g, `\x1b[${styles[name]}m`); // replace reset control with color for supporting multiple colors in string
|
|
9
|
+
return `\x1b[${styles[name]}m${_this}\x1b[0m`;
|
|
10
|
+
}
|
|
10
11
|
});
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
String.prototype
|
|
14
|
-
|
|
14
|
+
Object.defineProperty(String.prototype, 'nostyle', {
|
|
15
|
+
get () {
|
|
16
|
+
return this.replace(/[\u001b\u009b][[()#;?]*(?:\d{1,4}(?:;\d{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
|
|
17
|
+
}
|
|
15
18
|
});
|
|
@@ -70,43 +70,5 @@ module.exports = function build (argv) {
|
|
|
70
70
|
createSymlinks();
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
if (argv.config) {
|
|
74
|
-
if (_git.commitTdi.after(_git.commitTdi.stopUsingRepoconfigFile)) _error('This option only works for older repo\'s using a repoconfig file.');
|
|
75
|
-
|
|
76
|
-
inquirer
|
|
77
|
-
.prompt([{message: 'Be sure paths for projects are [customer]/[project]. Continue?', name: 'confirm', type: 'confirm'}])
|
|
78
|
-
.then(({confirm}) => {
|
|
79
|
-
if (!confirm) return;
|
|
80
|
-
|
|
81
|
-
const customer = {}, projects = [];
|
|
82
|
-
|
|
83
|
-
globby
|
|
84
|
-
.sync('database/config/**/txd_document_types.sql')
|
|
85
|
-
.forEach(p => {
|
|
86
|
-
const dtSqlInsert = fs.readFileSync(p).toString().match(/select(.*?)from/s)[1];
|
|
87
|
-
const ntSqlInsert = fs.readFileSync(p.replace('txd_document_types', 'txd_node_types')).toString().match(/select(.*?)from/s)[1];
|
|
88
|
-
|
|
89
|
-
const dtDisplayName = dtSqlInsert.match(/'([^']+)' display_name/)[1];
|
|
90
|
-
const ntName = ntSqlInsert.match(/'([^']+)' name/)[1];
|
|
91
|
-
const xpiDir = ntSqlInsert.match(/'([^']+)' xsl_prep_inc/)[1];
|
|
92
|
-
|
|
93
|
-
customer.name = dtDisplayName.split(/ (.+)/)[0];
|
|
94
|
-
customer.abbr = ntName.split('_')[0];
|
|
95
|
-
customer.dirname = xpiDir.split('/')[0];
|
|
96
|
-
|
|
97
|
-
projects.push({
|
|
98
|
-
name: dtDisplayName.split(/ (.+)/)[1],
|
|
99
|
-
abbr: ntName.split('_')[1],
|
|
100
|
-
dirname: xpiDir.split('/')[1],
|
|
101
|
-
idrange: dtSqlInsert.match(/(\d+)\d{3} id/)[1]
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
_repoconfig.update({customer, projects, applink: 'None'});
|
|
106
|
-
|
|
107
|
-
_write(`\nFile '${_paths.repoconfig}' has been created.`);
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
73
|
if (argv.oxygen) oxygen(argv.oxygen);
|
|
112
74
|
};
|
|
@@ -3,141 +3,136 @@ const objMerge = require('object-assign-deep');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
_appconfig.serverDefaults, obj, // override with local defaults
|
|
36
|
-
{domains: (obj.domains||[]).filter(d => obj.name == server || d.startsWith(server))} // make sure key is present and filter for domain (if not matched for name)
|
|
37
|
-
)
|
|
38
|
-
);
|
|
6
|
+
function getRemotePath (...arr) {
|
|
7
|
+
const fp = path.join(this.server.remotedir, ...arr);
|
|
8
|
+
return this.server.remotedir.includes(':') ? fp : fp.toFws();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
function setServer (searchString) {
|
|
13
|
+
if (!searchString) _error('No server was passed.');
|
|
14
|
+
|
|
15
|
+
const stringsToArrays = stringOrArray => [stringOrArray].flat();
|
|
16
|
+
const filterDomains = domain => domain[0].startsWith(searchString) || domain.slice(1).includes(searchString);
|
|
17
|
+
const filterServerConfigs = (arr, cfg) => {
|
|
18
|
+
if (cfg.domains) cfg.domains = cfg.domains.map(stringsToArrays).filter(filterDomains);
|
|
19
|
+
if (cfg.name === searchString || cfg.domains?.[0]) arr.push(cfg);
|
|
20
|
+
return arr;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// find server in local config and filter domains
|
|
24
|
+
let serverConfigs = _appconfig.servers?.reduce(filterServerConfigs, []);
|
|
25
|
+
// if nothing found, find server in shared config and filter domains
|
|
26
|
+
if (!serverConfigs?.[0]) serverConfigs = _appconfig.shared.servers?.reduce(filterServerConfigs, []);
|
|
27
|
+
|
|
28
|
+
// abort if no config is found, or if multiple configs or domains are found for server
|
|
29
|
+
if (!serverConfigs?.[0]) _error(`No config found for "${searchString}".`);
|
|
30
|
+
if (serverConfigs.length > 1 || serverConfigs[0].domains?.length > 1) {
|
|
31
|
+
const matches = serverConfigs.map(obj => obj.name === searchString ? obj.name : obj.domains);
|
|
32
|
+
const matchesFlatten = matches.flat().map(stringsToArrays).map(m => m[0] + (m[1] ? ` [${m.slice(1).join(', ')}]` : '')).join(', ');
|
|
33
|
+
_error(`Passed server name "${searchString}" gives multiple results:\n${matchesFlatten}`);
|
|
34
|
+
}
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
36
|
+
// complement serverconfig with defaults
|
|
37
|
+
const serverConfig = objMerge(
|
|
38
|
+
{config: {}, domains: [[]]}, // make sure keys are present
|
|
39
|
+
_appconfig.shared.serverDefaults, // override with shared defaults
|
|
40
|
+
_appconfig.serverDefaults, // override with local defaults
|
|
41
|
+
serverConfigs[0]
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// compose final server config in usable format
|
|
45
|
+
const {config, domains: [[host]], mode} = serverConfig;
|
|
46
|
+
|
|
47
|
+
this.server = {remotedir: config.remotedir};
|
|
48
|
+
|
|
49
|
+
if (host) {
|
|
50
|
+
const {parallel, port, username} = config;
|
|
51
|
+
if (!(port && parallel && username)) _error('Config is incomplete!');
|
|
52
|
+
this.server.ftpConfig = {
|
|
53
|
+
host, port, parallel, username,
|
|
54
|
+
agent: process.platform === 'win32' ? 'pageant' : process.env.SSH_AUTH_SOCK,
|
|
55
|
+
agentForward: process.platform !== 'win32',
|
|
56
|
+
readyTimeout: 15000,
|
|
57
|
+
retries: 1
|
|
61
58
|
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (mode === 'delivery-pack') {
|
|
62
|
+
const dateStr = new Date().toISOString().replace(/(.*)T(\d+):(\d+):(\d+).*/g, '$1_$2$3$4');
|
|
63
|
+
this.deliveryPackName = `tangelo-config-delivery-pack_${_paths.repo.split(/[\\/]/).pop()}_${dateStr}`;
|
|
64
|
+
this.server.remotedir += '/' + this.deliveryPackName; // output to temp folder
|
|
65
|
+
this.deliveryPack = true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.envDev = !host && !this.deliveryPack || /\.dev\.tangelo\.nl$/.test(host);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
function prepareForCopy (filter) {
|
|
73
|
+
const {remotedir, ftpConfig} = this.server;
|
|
74
|
+
if (!path.isAbsolute(remotedir)) _error('"remotedir" must contain an absolute path!');
|
|
75
|
+
|
|
76
|
+
this.giPatterns = !fs.existsSync('.gitignore') ? [] :
|
|
77
|
+
fs.readFileSync('.gitignore').toString()
|
|
78
|
+
.trim()
|
|
79
|
+
.replace(/^#.+/gm, '') // remove comments
|
|
80
|
+
.split(/\s+/) // split on lines
|
|
81
|
+
.filter(p => !p.match(/(^database\/)|(\/tdi)|(\/fonto)/)) // filter paths that should be copied
|
|
82
|
+
.map(p => '!' + (p.match(/^config/) ? p : '**/' + p.split('**/').pop()) + (p.match(/\/$/) ? '**' : '')) // negate expressions, remove base dir, suffix folders with **
|
|
83
|
+
;
|
|
84
|
+
|
|
85
|
+
let transferPattern = path.join(_paths.apply, filter).toFws();
|
|
86
|
+
|
|
87
|
+
// test if 'cmscustom/tdi' would be included, then add specifically, because following symlinks doesnt work with glob stars
|
|
88
|
+
const tdiIncRegex = /^(config\/)?((\{\w*,?)?cmscustom(,?\w*\})?\/|\*\/)?\*\*/;
|
|
89
|
+
const tdiPattern = tdiIncRegex.test(transferPattern) ? transferPattern.replace(tdiIncRegex, `${this.deliveryPack ? 'config/' : ''}cmscustom/tdi/**`) : 'nomatch';
|
|
90
|
+
|
|
91
|
+
if (!transferPattern)
|
|
92
|
+
_error('Invalid glob expression passed!');
|
|
93
|
+
else if (this.deliveryPack && transferPattern.startsWith('*'))
|
|
94
|
+
transferPattern = `{config,database,${_paths.tdi}/src/database/tdi}/${transferPattern}`;
|
|
95
|
+
else if (!this.deliveryPack) {
|
|
96
|
+
// for normal deployment to server, set config-dir so no other files in repo are unintentionally transferred
|
|
97
|
+
transferPattern = transferPattern.replace(/^config\/|^\//, '');
|
|
98
|
+
try { process.chdir('config'); }
|
|
99
|
+
catch(e) { _error('No config dir present!'); }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
this.transferPatterns = [transferPattern, tdiPattern, ...this.giPatterns, '!**/*.crdownload']; // ignore patterns must come last
|
|
103
|
+
|
|
104
|
+
if (this.deliveryPack && transferPattern.includes(_paths.tdi)) this.transferPatterns.push(`${_paths.tdi}/src/database/create*.sql`); // create(_exists).sql is called by tdi install
|
|
105
|
+
|
|
106
|
+
// add time parameters to all xopus requests to overcome cache
|
|
107
|
+
const ts = new Date().toISOString().replace(/[-:]/g, '').substring(0, 15);
|
|
108
|
+
this.replaceStrings = [
|
|
109
|
+
[/((xsd|xsl|src|iconsrc)="[^"?]+)"/g, `$1?_=${ts}"`, '**/xopus/xml/*'],
|
|
110
|
+
[/(schemaLocation="[^"?]+)"/g, `$1?_=${ts}"`, '**/xopus/xsd/*'],
|
|
111
|
+
[/(href="[^"?]+.xsl)"/g, `$1?_=${ts}"`, '**/xopus/xsl/*']
|
|
112
|
+
];
|
|
113
|
+
// depending on env, enable debug mode and change track functionality
|
|
114
|
+
if (this.envDev) {
|
|
115
|
+
this.transferPatterns.push('!ldap.xml');
|
|
116
|
+
this.replaceStrings.push(
|
|
117
|
+
['debug.on=false', 'debug.on=true', '**/util.js'],
|
|
118
|
+
['debugMode>false', 'debugMode>true', '**/xopus/xml/*'],
|
|
119
|
+
['allowAccept>false', 'allowAccept>true', '**/xopus/xml/*'],
|
|
120
|
+
['allowReject>false', 'allowReject>true', '**/xopus/xml/*'],
|
|
121
|
+
[/(id="generateSampleText" available=")false/, '$1true', '**/xopus/xml/*']
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
else this.replaceStrings.push(['debugMode>true', 'debugMode>false', '**/xopus/xml/*']);
|
|
125
|
+
|
|
126
|
+
_info(`Branch: ${_git.commitLocal().branch}`);
|
|
127
|
+
_info(`Source: ${path.join(process.cwd(), transferPattern)}`);
|
|
128
|
+
_info(`Server: ${ftpConfig ? `${ftpConfig.host}:${ftpConfig.port}` : 'local'}`);
|
|
129
|
+
_info(`Target: ${this.getRemotePath(transferPattern)}`);
|
|
130
|
+
_write();
|
|
62
131
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (ftpConfig.host && !(ftpConfig.port && ftpConfig.parallel && ftpConfig.username)) _error('Config is incomplete!');
|
|
66
|
-
|
|
67
|
-
this.localTransfer = !ftpConfig.host;
|
|
68
|
-
this.deliveryPack = serverConfig.mode == 'delivery-pack';
|
|
69
|
-
this.envDev = this.localTransfer && !this.deliveryPack || /\.dev\.tangelo\.nl$/.test(ftpConfig.host);
|
|
70
|
-
|
|
71
|
-
if (this.deliveryPack) {
|
|
72
|
-
const dateStr = new Date().toISOString().replace(/(.*)T(\d+):(\d+):(\d+).*/g, '$1_$2$3$4');
|
|
73
|
-
this.deliveryPackName = `tangelo-config-delivery-pack_${_paths.repo.split(/[\\/]/).pop()}_${dateStr}`;
|
|
74
|
-
this.server.remotedir += '/' + this.deliveryPackName; // output to temp folder
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
prepareForCopy (filter) {
|
|
79
|
-
const {remotedir, ftpConfig} = this.server;
|
|
80
|
-
if (!path.isAbsolute(remotedir)) _error('"remotedir" must contain an absolute path!');
|
|
81
|
-
|
|
82
|
-
this.giPatterns = !fs.existsSync('.gitignore') ? [] :
|
|
83
|
-
fs.readFileSync('.gitignore').toString()
|
|
84
|
-
.trim()
|
|
85
|
-
.replace(/^#.+/gm, '') // remove comments
|
|
86
|
-
.split(/\s+/) // split on lines
|
|
87
|
-
.filter(p => !p.match(/(^database\/)|(\/tdi)|(\/fonto)/)) // filter paths that should be copied
|
|
88
|
-
.map(p => '!' + (p.match(/^config/) ? p : '**/' + p.split('**/').pop()) + (p.match(/\/$/) ? '**' : '')) // negate expressions, remove base dir, suffix folders with **
|
|
89
|
-
;
|
|
90
|
-
|
|
91
|
-
let transferPattern = path.join(_paths.apply, filter).toFws();
|
|
92
|
-
|
|
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*\})?\/|\*\/)?\*\*/;
|
|
95
|
-
const tdiPattern = tdiIncRegex.test(transferPattern) ? transferPattern.replace(tdiIncRegex, `${this.deliveryPack ? 'config/' : ''}cmscustom/tdi/**`) : 'nomatch';
|
|
96
|
-
|
|
97
|
-
if (!transferPattern)
|
|
98
|
-
_error('Invalid glob expression passed!');
|
|
99
|
-
else if (this.deliveryPack && transferPattern.startsWith('*'))
|
|
100
|
-
transferPattern = `{config,database,${_paths.tdi}/src/database/tdi}/${transferPattern}`;
|
|
101
|
-
else if (!this.deliveryPack) {
|
|
102
|
-
// for normal deployment to server, set config-dir so no other files in repo are unintentionally transferred
|
|
103
|
-
transferPattern = transferPattern.replace(/^config\/|^\//, '');
|
|
104
|
-
try { process.chdir('config'); }
|
|
105
|
-
catch(e) { _error('No config dir present!'); }
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
this.transferPatterns = [transferPattern, tdiPattern, ...this.giPatterns, '!**/*.crdownload']; // ignore patterns must come last
|
|
109
|
-
|
|
110
|
-
if (this.deliveryPack && transferPattern.includes(_paths.tdi)) this.transferPatterns.push(`${_paths.tdi}/src/database/create*.sql`); // create(_exists).sql is called by tdi install
|
|
111
|
-
|
|
112
|
-
// add time parameters to all xopus requests to overcome cache
|
|
113
|
-
const ts = new Date().toISOString().replace(/[-:]/g, '').substring(0, 15);
|
|
114
|
-
this.replaceStrings = [
|
|
115
|
-
[/((xsd|xsl|src|iconsrc)="[^"?]+)"/g, `$1?_=${ts}"`, '**/xopus/xml/*'],
|
|
116
|
-
[/(schemaLocation="[^"?]+)"/g, `$1?_=${ts}"`, '**/xopus/xsd/*'],
|
|
117
|
-
[/(href="[^"?]+.xsl)"/g, `$1?_=${ts}"`, '**/xopus/xsl/*']
|
|
118
|
-
];
|
|
119
|
-
// depending on env, enable debug mode and change track functionality
|
|
120
|
-
if (this.envDev) {
|
|
121
|
-
this.transferPatterns.push('!ldap.xml');
|
|
122
|
-
this.replaceStrings.push(
|
|
123
|
-
['debug.on=false', 'debug.on=true', '**/util.js'],
|
|
124
|
-
['debugMode>false', 'debugMode>true', '**/xopus/xml/*'],
|
|
125
|
-
['allowAccept>false', 'allowAccept>true', '**/xopus/xml/*'],
|
|
126
|
-
['allowReject>false', 'allowReject>true', '**/xopus/xml/*'],
|
|
127
|
-
[/(id="generateSampleText" available=")false/, '$1true', '**/xopus/xml/*']
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
else this.replaceStrings.push(['debugMode>true', 'debugMode>false', '**/xopus/xml/*']);
|
|
131
|
-
|
|
132
|
-
_info(`Branch: ${_git.commitLocal().branch}`);
|
|
133
|
-
_info(`Source: ${path.join(process.cwd(), transferPattern)}`);
|
|
134
|
-
_info(`Server: ${ftpConfig.host ? `${ftpConfig.host}:${ftpConfig.port}` : 'local'}`);
|
|
135
|
-
_info(`Target: ${this.getRemotePath(transferPattern)}`);
|
|
136
|
-
_write();
|
|
137
|
-
|
|
138
|
-
if (_git.commitLocal().date < _git.commitRemote().date) {
|
|
139
|
-
_warn(`You're not deploying from the most recent git commit!\n`);
|
|
140
|
-
}
|
|
132
|
+
if (_git.commitLocal().date < _git.commitRemote().date) {
|
|
133
|
+
_warn(`You're not deploying from the most recent git commit!\n`);
|
|
141
134
|
}
|
|
135
|
+
}
|
|
136
|
+
|
|
142
137
|
|
|
143
|
-
};
|
|
138
|
+
module.exports = {getRemotePath, setServer, prepareForCopy};
|
|
@@ -149,7 +149,7 @@ const transfer = (paths, lrServer) => {
|
|
|
149
149
|
});
|
|
150
150
|
|
|
151
151
|
// disable file logging for ftp (gulp-sftp does it already)
|
|
152
|
-
g_print.setLogFunction(
|
|
152
|
+
g_print.setLogFunction(filepath => c.server.ftpConfig ? null : _write(filepath.nostyle));
|
|
153
153
|
|
|
154
154
|
_info('Start transferring', true);
|
|
155
155
|
|
|
@@ -181,7 +181,7 @@ const transfer = (paths, lrServer) => {
|
|
|
181
181
|
cb(null, file);
|
|
182
182
|
}))
|
|
183
183
|
.pipe(g_print.default())
|
|
184
|
-
.pipe(c.
|
|
184
|
+
.pipe(c.server.ftpConfig ? g_sftp(c.server.ftpConfig, c.server.remotedir) : gulp.dest(c.server.remotedir))
|
|
185
185
|
.on('end', () => {
|
|
186
186
|
_info('Finished transferring\n', true);
|
|
187
187
|
if (fontoPaths.outdated[0]) _warn(`Fonto build files in the following folders were outdated and therefore skipped:\n ${fontoPaths.outdated.map(v => v.slice(0, -1)).join('\n ')}`);
|
|
@@ -16,8 +16,8 @@ module.exports = function deploy (argv) {
|
|
|
16
16
|
c.setServer(argv.server);
|
|
17
17
|
c.prepareForCopy(argv.filter);
|
|
18
18
|
|
|
19
|
-
if (
|
|
20
|
-
c.server.ftpConfig.eventPut =
|
|
19
|
+
if (c.server.ftpConfig) {
|
|
20
|
+
c.server.ftpConfig.eventPut = file => {
|
|
21
21
|
_write(file.destination.replace(c.server.remotedir, ''));
|
|
22
22
|
if (path.extname(file.destination)=='.sh')
|
|
23
23
|
remote.add('chmod 755 '+file.destination, 'Permissions set: '+file.destination);
|
|
@@ -64,7 +64,7 @@ module.exports = function deploy (argv) {
|
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
// check connection
|
|
67
|
-
if (
|
|
67
|
+
if (c.server.ftpConfig) {
|
|
68
68
|
const sftp = new sftpClient();
|
|
69
69
|
sftp.connect(c.server.ftpConfig).then(() => sftp.end()).catch(err => _error(`Could not connect to server${err ? ': '+err.message : ''}`));
|
|
70
70
|
}
|
|
@@ -77,7 +77,7 @@ module.exports = function deploy (argv) {
|
|
|
77
77
|
// the "dist" folder isn't watched properly: it does not detect "assets" anymore after building once
|
|
78
78
|
!/cmscustom.+fonto[\\/]/.test(filepath) ||
|
|
79
79
|
/fonto[\\/]dist[\\/]/.test(filepath) ||
|
|
80
|
-
(/fonto[\\/]dev[\\/]/.test(filepath)
|
|
80
|
+
(/fonto[\\/]dev[\\/]/.test(filepath)) ||
|
|
81
81
|
/fonto[\\/]packages[\\/]sx-shell-.*?[\\/]assets[\\/]schemas[\\/]/.test(filepath)
|
|
82
82
|
)
|
|
83
83
|
) {
|
|
@@ -90,8 +90,8 @@ 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.
|
|
94
|
-
else
|
|
93
|
+
if (c.server.ftpConfig) remote.add(`rm -rf "${rp}"`, msg).process();
|
|
94
|
+
else del([rp], {force: true}).then(() => _info(msg, true));
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
});
|
|
@@ -53,7 +53,7 @@ module.exports = function fonto (argv) {
|
|
|
53
53
|
fontoVersionNew = data.match(/\d+\.\d+\.\d+/g).filter(v => allowedFontoVersionRegex.test(v))[0];
|
|
54
54
|
}
|
|
55
55
|
else if (!allowedFontoVersionRegex.test(fontoVersionNew)) {
|
|
56
|
-
_error(`Fonto version ${fontoVersionNew} is not compatible with the current TDI submodule commit!\nExecute: tct fonto --init latest`);
|
|
56
|
+
_error(`Fonto version ${fontoVersionNew} is not compatible with the current TDI submodule commit!\nExecute: ${'tct fonto --init latest'.cyan}`);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
_write(fontoVersionNew+'\n');
|
package/src/modules/git/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const {execSync, spawn} = require('child_process');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const globby = require('globby');
|
|
2
4
|
const inquirer = require('inquirer');
|
|
3
5
|
const path = require('path');
|
|
4
6
|
|
|
@@ -36,172 +38,189 @@ const cmdExec = commands => new Promise(resolve => {
|
|
|
36
38
|
});
|
|
37
39
|
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
function init () {
|
|
42
|
+
const remoteTdiUrl = `${_paths.gitremote}/${_paths.tdi}.git`;
|
|
43
|
+
let branches;
|
|
44
|
+
|
|
45
|
+
try { branches = execSync('git ls-remote --heads ' + remoteTdiUrl, {encoding: 'UTF-8'}); }
|
|
46
|
+
catch (e) { _error(' '); } // execSync already prints an error to the console
|
|
47
|
+
|
|
48
|
+
branches = branches
|
|
49
|
+
.match(/refs\/heads\/\S+/g)
|
|
50
|
+
.map(s => s.replace('refs/heads/', ''))
|
|
51
|
+
.sort((a, b) => {
|
|
52
|
+
const aRelease = /^release\//.test(a), bRelease = /^release\//.test(b);
|
|
53
|
+
if (aRelease && bRelease) return a > b ? -1 : 1;
|
|
54
|
+
if (aRelease) return -1;
|
|
55
|
+
if (bRelease) return 1;
|
|
56
|
+
return a > b ? 1 : -1;
|
|
57
|
+
})
|
|
58
|
+
;
|
|
59
|
+
inquirer
|
|
60
|
+
.prompt([{
|
|
61
|
+
message: 'Choose a branch for TDI submodule: ', name: 'branch', type: 'list',
|
|
62
|
+
choices: branches, pageSize: 3, loop: false
|
|
63
|
+
}])
|
|
64
|
+
.then(a => {
|
|
65
|
+
_write();
|
|
66
|
+
cmdExec([
|
|
67
|
+
['git init', 'Initialize repo:'],
|
|
68
|
+
[`git remote add origin ${_paths.gitremote.replace(/\//, ':')}/${path.basename(process.cwd()).replace(/\W/, '-')}.git`],
|
|
69
|
+
[`git submodule add -b ${a.branch} ${remoteTdiUrl}`, 'Add TDI submodule:'],
|
|
70
|
+
['git commit -m "added tdi submodule"', 'Commit:']
|
|
71
|
+
])
|
|
72
|
+
.then(() => {
|
|
73
|
+
_info(`Next steps:
|
|
74
|
+
1. Go to https://bitbucket.org/repo/create?workspace=tangelosoftware and create a repository named "${path.basename(process.cwd()).replace(/\W/, '-')}", with options for including README/.gitignore disabled.
|
|
75
|
+
2. Open this folder in Sourcetree (ctrl+O), and push master branch to origin.
|
|
76
|
+
`.replace(/^\s{8}/gm, ''));
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
function reset () {
|
|
83
|
+
_info('Reset and clean repo:');
|
|
84
|
+
cmdExec([
|
|
85
|
+
['git reset --hard'],
|
|
86
|
+
['git clean -f -d']
|
|
87
|
+
]);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
function clone ({clone: repoName}) {
|
|
92
|
+
|
|
93
|
+
if (typeof repoName !== 'string') _error('Pass a repository name!');
|
|
94
|
+
|
|
95
|
+
_info('Clone a client repository and do basic project initialization');
|
|
96
|
+
cmdExec([
|
|
97
|
+
// Clone repository
|
|
98
|
+
['git clone git@bitbucket.org:tangelosoftware/' + repoName + '.git']
|
|
99
|
+
])
|
|
100
|
+
.then(() => {
|
|
101
|
+
// Change directory for the next commands, it should have the same name as the clone argument from commandline at this point
|
|
102
|
+
process.chdir('./' + repoName);
|
|
103
|
+
cmdExec([
|
|
104
|
+
// Retrieve TDI submodule for this client
|
|
105
|
+
['git submodule update --init', 'Fetch submodules such as TDI'],
|
|
106
|
+
// Create symlinks for TDI
|
|
107
|
+
['tct b -s', 'Create TDI symlinks'],
|
|
108
|
+
// Create Oxygen project file and set name equal to repo name
|
|
109
|
+
[`tct b -x ${repoName}`, 'Create Oxygen project file'],
|
|
110
|
+
]);
|
|
111
|
+
})
|
|
112
|
+
.catch(e => {throw e;})
|
|
113
|
+
;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Updates the repository to the latest commit.
|
|
119
|
+
* Checks the version of the Fonto instances and warns if there are changes.
|
|
120
|
+
*/
|
|
121
|
+
function updateRepo () {
|
|
122
|
+
const readFontoVersionFromFile = relPath => fs.readJSONSync(path.join(_paths.repo, 'config/cmscustom', relPath)).sdkVersion;
|
|
123
|
+
const fontoLocations = globby.sync('*/*/fonto/manifest.json', {cwd: path.join(_paths.repo, 'config/cmscustom')});
|
|
124
|
+
const fontoVersions = {};
|
|
125
|
+
fontoLocations.forEach(p => {fontoVersions[p] = readFontoVersionFromFile(p);});
|
|
126
|
+
|
|
127
|
+
_info(`Fetch all`);
|
|
128
|
+
const cmdFetch = execGitCommand('fetch -pf --all', _paths.repo);
|
|
129
|
+
if (cmdFetch.error) _warn(`Fetch failed\n${cmdFetch.error}`);
|
|
130
|
+
|
|
131
|
+
_info(`Pull repository`);
|
|
132
|
+
const cmdPull = execGitCommand(`pull`, _paths.repo);
|
|
133
|
+
if (cmdPull.error) _warn(`Pull failed\n${cmdPull.error}`);
|
|
134
|
+
|
|
135
|
+
_info(`Checked out at commit:`);
|
|
136
|
+
const repoLog = _git.commitLocal();
|
|
137
|
+
_write([_formatDate(repoLog.date), repoLog.tags, repoLog.hash].filter(v=>v).join(' - '));
|
|
138
|
+
|
|
139
|
+
if (fontoLocations.find(p => fontoVersions[p] !== readFontoVersionFromFile(p))) _warn(`\nChanges in Fonto instance versions have been pulled. Therefore you must execute ${'tct fonto --init'.cyan} before building and deploying Fonto.`);
|
|
140
|
+
|
|
141
|
+
_info(`\nRetrieve submodules that belong to this repo-commit`); // update submodule recursively (set to version that belongs to repo)
|
|
142
|
+
_write(execGitCommand(`submodule update --recursive`, _paths.repo));
|
|
143
|
+
_info(`Submodule checked out at commit:`);
|
|
144
|
+
const tdiLog = _git.commitTdi.local();
|
|
145
|
+
_write([_formatDate(tdiLog.date), tdiLog.tags, tdiLog.hash].filter(v=>v).join(' - '));
|
|
146
|
+
|
|
147
|
+
const tdiBranch = getTdiBranch();
|
|
148
|
+
_info(`\nTDI branch ${tdiBranch.name} is ${tdiBranch.commitsBehind} commits behind.\nUpdate TDI submodule to latest version with ${'tct git --update-submodule'.cyan}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Updates a submodule in a Git repository. Allows for custom date ranges (for testing) and branch upgrades, and applies migrations to the repository if necessary.
|
|
154
|
+
*
|
|
155
|
+
* @param {string[]} options.dates - An array of two strings representing the start and end dates for the custom date range.
|
|
156
|
+
* @param {string} options.updateSubmodule - The target branch name for the submodule update.
|
|
157
|
+
*/
|
|
158
|
+
function updateSubmodule({dates = [], updateSubmodule: toBranchName}) {
|
|
159
|
+
const [dateCustomFrom, dateCustomTo] = dates;
|
|
160
|
+
const branch = getTdiBranch(typeof toBranchName !== 'boolean' && toBranchName);
|
|
161
|
+
const branchUpgrade = branch.from ? branch.from.name < branch.name : false;
|
|
162
|
+
|
|
163
|
+
if (branchUpgrade) _info(`Current branch '${branch.from.name}' will be updated to '${branch.name}'\nCommon ancestor will be used in selecting TDI commits requiring migration: ${_formatDate(branch.commonAncestor.date)}`);
|
|
164
|
+
_info(`Branch ${branch.name} is ${branch.commitsBehind} commits behind.\n`);
|
|
165
|
+
|
|
166
|
+
// Set branch in .gitmodules file; This ensures submodule update will follow this branch to the latest commit
|
|
167
|
+
const setBranch = execGitCommand(`submodule set-branch -b ${branch.name} tangelo-default-implementation`, _paths.repo);
|
|
168
|
+
if (setBranch.error) _error(`Set branch failed: ${setBranch.error}`);
|
|
169
|
+
|
|
170
|
+
if (branch.commitsBehind > 0 || dateCustomFrom) {
|
|
171
|
+
const dateBeforeUpdate = _git.commitTdi.local().date;
|
|
172
|
+
|
|
173
|
+
// update submodule
|
|
174
|
+
const updateSubmoduleMsg = execGitCommand(`submodule update --remote`, _paths.repo);
|
|
175
|
+
if (updateSubmoduleMsg.error) _error(`Update submodule failed\n${updateSubmoduleMsg.error}`);
|
|
176
|
+
if (updateSubmoduleMsg) _info(`TDI submodule updated:\n${updateSubmoduleMsg}\n`);
|
|
177
|
+
|
|
178
|
+
// tdiMigrationFilePath should exist in latest commits of releases 5.3+; As we updated to the latest version this should work
|
|
179
|
+
const migrations = _modulesTdi.require('git/tdiCommitsRequiringMigration.js');
|
|
180
|
+
const fromDate = dateCustomFrom ? new Date(dateCustomFrom) : branch.commonAncestor?.date ?? dateBeforeUpdate;
|
|
181
|
+
const toDate = dateCustomTo ? new Date(dateCustomTo) : new Date();
|
|
182
|
+
|
|
183
|
+
_info(`TDI commits requiring migration between ${_formatDate(fromDate)} and ${_formatDate(toDate)}`);
|
|
184
|
+
// Filter the migrations that should be applied/considered; Also display older releases migrations
|
|
185
|
+
// For a branch upgrade, all the migrations need to be executed from the beginning of the target branch, not just within the given date range
|
|
186
|
+
const migrationsFiltered = migrations.filter(m => m.releases.find(r => {
|
|
187
|
+
const withinDateRange = fromDate < new Date(r.date) && new Date(r.date) < toDate;
|
|
188
|
+
const isTargetBranch = `release/${r.release}` === branch.name;
|
|
189
|
+
return branchUpgrade ?
|
|
190
|
+
isTargetBranch || (branch.from.name <= `release/${r.release}` && `release/${r.release}` < branch.name && withinDateRange) :
|
|
191
|
+
isTargetBranch && withinDateRange
|
|
192
|
+
;
|
|
193
|
+
}));
|
|
40
194
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
branches = branches
|
|
49
|
-
.match(/refs\/heads\/\S+/g)
|
|
50
|
-
.map(s => s.replace('refs/heads/', ''))
|
|
51
|
-
.sort((a, b) => {
|
|
52
|
-
const aRelease = /^release\//.test(a), bRelease = /^release\//.test(b);
|
|
53
|
-
if (aRelease && bRelease) return a > b ? -1 : 1;
|
|
54
|
-
if (aRelease) return -1;
|
|
55
|
-
if (bRelease) return 1;
|
|
56
|
-
return a > b ? 1 : -1;
|
|
57
|
-
})
|
|
58
|
-
;
|
|
59
|
-
inquirer
|
|
60
|
-
.prompt([{
|
|
61
|
-
message: 'Choose a branch for TDI submodule: ', name: 'branch', type: 'list',
|
|
62
|
-
choices: branches, pageSize: 3, loop: false
|
|
63
|
-
}])
|
|
64
|
-
.then(a => {
|
|
65
|
-
_write();
|
|
66
|
-
cmdExec([
|
|
67
|
-
['git init', 'Initialize repo:'],
|
|
68
|
-
[`git remote add origin ${_paths.gitremote.replace(/\//, ':')}/${path.basename(process.cwd()).replace(/\W/, '-')}.git`],
|
|
69
|
-
[`git submodule add -b ${a.branch} ${remoteTdiUrl}`, 'Add TDI submodule:'],
|
|
70
|
-
['git commit -m "added tdi submodule"', 'Commit:']
|
|
71
|
-
])
|
|
72
|
-
.then(() => {
|
|
73
|
-
_info(`Next steps:
|
|
74
|
-
1. Go to https://bitbucket.org/repo/create?workspace=tangelosoftware and create a repository named "${path.basename(process.cwd()).replace(/\W/, '-')}", with options for including README/.gitignore disabled.
|
|
75
|
-
2. Open this folder in Sourcetree (ctrl+O), and push master branch to origin.
|
|
76
|
-
`.replace(/^\s{8}/gm, ''));
|
|
77
|
-
});
|
|
195
|
+
let relevantMigrationCount = 0;
|
|
196
|
+
// Apply callback for migrations
|
|
197
|
+
migrationsFiltered
|
|
198
|
+
.sort((a, b) => Date(a.releases[0].date) > Date(b.releases[0].date) ? 1 : -1)
|
|
199
|
+
.forEach(m => {
|
|
200
|
+
const date = _formatDate(new Date(m.releases.filter(r => branch.name >= `release/${r.release}`)[0].date));
|
|
201
|
+
relevantMigrationCount += m.callback(date, relevantMigrationCount+1) === 1 ? 1 : 0;
|
|
78
202
|
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (argv.reset) {
|
|
82
|
-
_info('Reset and clean repo:');
|
|
83
|
-
cmdExec([
|
|
84
|
-
['git reset --hard'],
|
|
85
|
-
['git clean -f -d']
|
|
86
|
-
]);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (argv.clone) {
|
|
90
|
-
if (typeof argv.clone !== 'string') _error('Pass a repository name!');
|
|
91
|
-
|
|
92
|
-
_info('Clone a client repository and do basic project initialization');
|
|
93
|
-
cmdExec([
|
|
94
|
-
// Clone repository
|
|
95
|
-
['git clone git@bitbucket.org:tangelosoftware/' + argv.clone + '.git']
|
|
96
|
-
])
|
|
97
|
-
.then(() => {
|
|
98
|
-
// Change directory for the next commands, it should have the same name as the clone argument from commandline at this point
|
|
99
|
-
process.chdir('./' + argv.clone);
|
|
100
|
-
cmdExec([
|
|
101
|
-
// Retrieve TDI submodule for this client
|
|
102
|
-
['git submodule update --init', 'Fetch submodules such as TDI'],
|
|
103
|
-
// Create symlinks for TDI
|
|
104
|
-
['tct b -s', 'Create TDI symlinks'],
|
|
105
|
-
// Create Oxygen project file and set name equal to repo name
|
|
106
|
-
[`tct b -x ${argv.clone}`, 'Create Oxygen project file'],
|
|
107
|
-
]);
|
|
108
|
-
})
|
|
109
|
-
.catch(e => {throw e;})
|
|
110
|
-
;
|
|
111
|
-
}
|
|
112
203
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (update === `tdi`) {
|
|
117
|
-
|
|
118
|
-
const tdiBranch = getTdiBranch(argv.branch);
|
|
119
|
-
const tdiBranchUpgrade = tdiBranch.from ? (tdiBranch.from.name < tdiBranch.name) : false;
|
|
120
|
-
|
|
121
|
-
if (tdiBranchUpgrade) _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)}`);
|
|
122
|
-
_info(`Branch ${tdiBranch.name} is ${tdiBranch.commitsBehind} commits behind.`);
|
|
123
|
-
|
|
124
|
-
// Set branch in .gitmodules file; This ensures submodule update will follow this branch to the latest commit
|
|
125
|
-
const setBranch = execGitCommand(`submodule set-branch -b ${tdiBranch.name} tangelo-default-implementation`, _paths.repo);
|
|
126
|
-
if (setBranch.error) _error(`Set branch failed: ${setBranch.error}`);
|
|
127
|
-
|
|
128
|
-
if (tdiBranch.commitsBehind > 0 || tdiFromDateCustom) {
|
|
129
|
-
// update submodule
|
|
130
|
-
const updateSubmoduleMsg = execGitCommand(`submodule update --remote`, _paths.repo);
|
|
131
|
-
if (updateSubmoduleMsg.error && !tdiFromDateCustom) _error(`Update submodule failed\n${updateSubmoduleMsg.error}`);
|
|
132
|
-
|
|
133
|
-
const tdiDateBeforeUpdate = _git.commitTdi.local().date;
|
|
134
|
-
// update local TDI commit data
|
|
135
|
-
_git.commitTdi.local(true);
|
|
136
|
-
|
|
137
|
-
if (!tdiFromDateCustom) _info(`TDI submodule updated:\n${updateSubmoduleMsg}`);
|
|
138
|
-
|
|
139
|
-
// tdiMigrationFilePath should exist in latest commits of releases 5.3+; As we updated to the latest version this should work
|
|
140
|
-
const migrations = _modulesTdi.require('git/tdiCommitsRequiringMigration.js');
|
|
141
|
-
if (migrations) {
|
|
142
|
-
const fromTdiDate = tdiFromDateCustom ? new Date(tdiFromDateCustom) : (tdiBranch.commonAncestor) ? tdiBranch.commonAncestor.date: tdiDateBeforeUpdate;
|
|
143
|
-
const toTdiDate = tdiToDateCustom ? new Date(tdiToDateCustom) : new Date();
|
|
144
|
-
|
|
145
|
-
_info(`TDI commits requiring migration between ${_formatDate(fromTdiDate)} and ${_formatDate(toTdiDate)}`);
|
|
146
|
-
// Filter the migrations that should be applied/considered; Also display older releases migrations
|
|
147
|
-
const migrationsFiltered = migrations
|
|
148
|
-
.filter((m) => m.releases.find((r) => {
|
|
149
|
-
const time = new Date(r.date).getTime();
|
|
150
|
-
return ((tdiBranchUpgrade)
|
|
151
|
-
? `release/${r.release}` == tdiBranch.name || (tdiBranch.from.name <= `release/${r.release}` && `release/${r.release}` < tdiBranch.name && (fromTdiDate.getTime() < time && time < toTdiDate.getTime()))
|
|
152
|
-
: `release/${r.release}` == tdiBranch.name && (fromTdiDate.getTime() < time && time < toTdiDate.getTime())
|
|
153
|
-
);
|
|
154
|
-
})
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
let relevantMigrationCount = 0;
|
|
158
|
-
// Apply callback for migrations
|
|
159
|
-
migrationsFiltered
|
|
160
|
-
.sort((a, b) => Date(a.releases[0].date)>Date(b.releases[0].date)?1:-1)
|
|
161
|
-
.forEach((m) => {
|
|
162
|
-
const date = _formatDate(new Date(m.releases.filter((r) => tdiBranch.name >= `release/${r.release}`)[0].date));
|
|
163
|
-
relevantMigrationCount += (m.callback(date, relevantMigrationCount+1)) === 1 ? 1 : 0;
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// Output message based on whether or not migrations are required
|
|
167
|
-
if (migrationsFiltered.length === 0) {
|
|
168
|
-
_write(`-- No commits require migrations --`);
|
|
169
|
-
} else {
|
|
170
|
-
_info(`\n${relevantMigrationCount} relevant migration(s) out of ${migrationsFiltered.length} migration(s) are shown.`);
|
|
171
|
-
_info(`See confluence page 'TDI commits requiring migration' for more detailed information`);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
} else {
|
|
175
|
-
_error(`Cannot find required files in TDI submodule.`);
|
|
176
|
-
}
|
|
177
|
-
} else {
|
|
178
|
-
_info(`Your TDI submodule is already up to date`);
|
|
179
|
-
}
|
|
204
|
+
// Output message based on whether or not migrations are required
|
|
205
|
+
if (migrationsFiltered.length === 0) {
|
|
206
|
+
_write(`-- No commits require migrations --`);
|
|
180
207
|
} else {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
// Fetch all
|
|
184
|
-
_info(`Fetch all`);
|
|
185
|
-
const cmdFetch = execGitCommand('fetch -pf --all', path.join(_paths.repo));
|
|
186
|
-
if (cmdFetch.error) _warn(`Fetch failed\n${cmdFetch.error}`);
|
|
187
|
-
|
|
188
|
-
// pull repo
|
|
189
|
-
_info(`Pull repository`);
|
|
190
|
-
const cmdPull = execGitCommand(`pull`, _paths.repo);
|
|
191
|
-
if (cmdPull.error) _warn(`Pull failed\n${cmdPull.error}`);
|
|
192
|
-
|
|
193
|
-
_info(`Checked out at commit:`);
|
|
194
|
-
const repoLog = _git.commitLocal(true);
|
|
195
|
-
_write(`${_formatDate(repoLog.date)} - ${repoLog.tags} - ${repoLog.hash}`);
|
|
196
|
-
|
|
197
|
-
_info(`Retrieve submodules that belong to this repo-commit`); // update submodule recursively (set to version that belongs to repo)
|
|
198
|
-
_write(execGitCommand(`submodule update --recursive`, path.join(_paths.repo)));
|
|
199
|
-
_info(`Submodule checked out at commit:`);
|
|
200
|
-
const tdiLog = _git.commitTdi.local(true);
|
|
201
|
-
_write(`${_formatDate(tdiLog.date)} - ${tdiLog.tags} - ${tdiLog.hash}`);
|
|
202
|
-
const tdiBranch = getTdiBranch();
|
|
203
|
-
|
|
204
|
-
_info(`\nTDI branch ${tdiBranch.name} is ${tdiBranch.commitsBehind} commits behind.\nUpdate TDI to latest version with 'tct g -u tdi'`);
|
|
208
|
+
_info(`\n${relevantMigrationCount} relevant migration(s) out of ${migrationsFiltered.length} migration(s) are shown.`);
|
|
209
|
+
_info(`See confluence page 'TDI commits requiring migration' for more detailed information (https://tangelo-software.atlassian.net/wiki/x/qQnE)`);
|
|
205
210
|
}
|
|
211
|
+
|
|
212
|
+
} else {
|
|
213
|
+
_info(`Your TDI submodule is already up to date`);
|
|
206
214
|
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
module.exports = function git (argv) {
|
|
219
|
+
|
|
220
|
+
if (argv.init) init();
|
|
221
|
+
if (argv.reset) reset();
|
|
222
|
+
if (argv.clone) clone(argv);
|
|
223
|
+
if (argv.updateRepo) updateRepo();
|
|
224
|
+
if (argv.updateSubmodule) updateSubmodule(argv);
|
|
225
|
+
|
|
207
226
|
};
|