@tangelo/tangelo-configuration-toolkit 1.20.2 → 1.21.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/index.js +1 -0
- package/package.json +1 -1
- package/src/lib/gulp-sftp.js +5 -3
- package/src/lib/remote-exec.js +44 -0
- package/src/modules/deploy/execute.js +2 -61
- package/src/modules/deploy/index.js +73 -13
- package/src/modules/info/index.js +2 -2
package/index.js
CHANGED
|
@@ -72,6 +72,7 @@ global._git = {
|
|
|
72
72
|
if (!_appdata.gitUser) _appdata._update({gitUser: execGitCommand(`config --get user.email`, _paths.repo)});
|
|
73
73
|
return _appdata.gitUser;
|
|
74
74
|
},
|
|
75
|
+
status: memoize(() => execGitCommand(`status -s`, _paths.repo)),
|
|
75
76
|
commitLocal: memoize(() => execGitCommand(`log -1 --format=%D;%H;%cd --date=iso-strict`, _paths.repo, ['branch', 'hash', 'date'])),
|
|
76
77
|
commitRemote: memoize(() => execGitCommand('log -1 --format=%cd --date=iso-strict origin/' + _git.commitLocal().branch, _paths.repo, ['date'])),
|
|
77
78
|
commitTdi: {
|
package/package.json
CHANGED
package/src/lib/gulp-sftp.js
CHANGED
|
@@ -21,13 +21,15 @@ module.exports = function(ftpConfig, remotedir) {
|
|
|
21
21
|
return cb();
|
|
22
22
|
},
|
|
23
23
|
|
|
24
|
-
function flush(cb) {
|
|
24
|
+
async function flush(cb) {
|
|
25
25
|
if (!files[0]) cb();
|
|
26
26
|
|
|
27
27
|
const paths = [...new Set(files.map(({destination}) => path.dirname(destination)))] // collect unique paths
|
|
28
28
|
.filter((p1, i, a) => !a.find(p2 => p1 != p2 && p2.includes(p1+'/'))) // remove paths being part of others
|
|
29
29
|
;
|
|
30
30
|
|
|
31
|
+
await ftpConfig.eventBeforeAll(files);
|
|
32
|
+
|
|
31
33
|
sftp.connect(ftpConfig)
|
|
32
34
|
.then(() => Promise.all(paths.map(p => parallel( // check if all directories exist
|
|
33
35
|
() => sftp.exists(p).then(r => {
|
|
@@ -48,8 +50,8 @@ module.exports = function(ftpConfig, remotedir) {
|
|
|
48
50
|
).catch(err => _warn(`File transfer failed${err ? ': '+err : ''}`));
|
|
49
51
|
}
|
|
50
52
|
))))
|
|
51
|
-
.then(() => {
|
|
52
|
-
|
|
53
|
+
.then(async () => {
|
|
54
|
+
await ftpConfig.eventAfterAll(files);
|
|
53
55
|
cb();
|
|
54
56
|
return sftp.end();
|
|
55
57
|
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const {NodeSSH} = require('node-ssh');
|
|
2
|
+
const pLimit = require('p-limit');
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
module.exports = class RemoteExec {
|
|
6
|
+
|
|
7
|
+
#ftpConfig;
|
|
8
|
+
#queue = [];
|
|
9
|
+
|
|
10
|
+
constructor(ftpConfig) {
|
|
11
|
+
this.#ftpConfig = ftpConfig;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
add(cmd, msg) {
|
|
15
|
+
if (!this.#queue.find(e => e[0]==cmd)) this.#queue.push([cmd, msg]);
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async process() {
|
|
20
|
+
if (!this.#queue[0]) return;
|
|
21
|
+
|
|
22
|
+
const sshI = new NodeSSH();
|
|
23
|
+
const limit = pLimit(this.#ftpConfig.parallel);
|
|
24
|
+
|
|
25
|
+
await sshI.connect(this.#ftpConfig) // set up connection once
|
|
26
|
+
.then(() => Promise.all(this.#queue.map(([cmd, msg]) => limit(
|
|
27
|
+
() => sshI.execCommand(cmd).then(result => {
|
|
28
|
+
if (result.stderr) _warn(result.stderr);
|
|
29
|
+
else if (msg === 'STDOUT') _write(result.stdout);
|
|
30
|
+
else if (msg) _info(msg === 'CMD' ? cmd : msg);
|
|
31
|
+
})
|
|
32
|
+
))))
|
|
33
|
+
.catch(err => {
|
|
34
|
+
if (err.code=='ECONNRESET' || err.code=='ECONNABORTED') _warn(`Server aborted connection. Retrying.`);
|
|
35
|
+
else _warn(err);
|
|
36
|
+
this.process(); // retry set up connection
|
|
37
|
+
})
|
|
38
|
+
.finally(() => {
|
|
39
|
+
sshI.dispose();
|
|
40
|
+
this.#queue = [];
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
};
|
|
@@ -3,7 +3,6 @@ const execGitCommand = require('../../lib/exec-git-command');
|
|
|
3
3
|
const fs = require('fs-extra');
|
|
4
4
|
const globby = require('globby');
|
|
5
5
|
const gulp = require('gulp');
|
|
6
|
-
const {NodeSSH} = require('node-ssh');
|
|
7
6
|
const g_tcl = require('../../lib/gulp-tcl-config');
|
|
8
7
|
const through2 = require('through2');
|
|
9
8
|
const {spawnSync} = require('child_process');
|
|
@@ -24,59 +23,6 @@ const c = require('./config');
|
|
|
24
23
|
const s = require('./srcset');
|
|
25
24
|
|
|
26
25
|
|
|
27
|
-
const remote = {
|
|
28
|
-
queue: [],
|
|
29
|
-
add(cmd, msg) {
|
|
30
|
-
if (!this.queue.find(e => e[0]==cmd)) this.queue.push([cmd, msg]);
|
|
31
|
-
return this;
|
|
32
|
-
},
|
|
33
|
-
do(sshI) {
|
|
34
|
-
let executing = Math.min(this.queue.length, c.server.ftpConfig.parallel);
|
|
35
|
-
|
|
36
|
-
if (executing===0) { // close connection and return callback after last exec command
|
|
37
|
-
sshI.dispose();
|
|
38
|
-
_write();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// execute each command from the queue in batches
|
|
42
|
-
this.queue.filter((e, i) => i < executing).forEach(()=>{
|
|
43
|
-
const command = this.queue.shift();
|
|
44
|
-
|
|
45
|
-
sshI.execCommand(command[0])
|
|
46
|
-
.then(result => {
|
|
47
|
-
if (result.stderr) _warn(result.stderr);
|
|
48
|
-
else if (command[1] === '') _write(result.stdout);
|
|
49
|
-
else _info(command[1] || command[0], true);
|
|
50
|
-
|
|
51
|
-
executing--;
|
|
52
|
-
if (executing===0) this.do(sshI);
|
|
53
|
-
})
|
|
54
|
-
.catch(() => {
|
|
55
|
-
_warn(`Server aborted connection. Retrying.`);
|
|
56
|
-
this.queue.unshift(command);
|
|
57
|
-
|
|
58
|
-
executing--;
|
|
59
|
-
if (executing===0) this.do(sshI);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
},
|
|
63
|
-
process() {
|
|
64
|
-
if (!this.queue[0]) return;
|
|
65
|
-
|
|
66
|
-
const sshI = new NodeSSH();
|
|
67
|
-
sshI.connect(c.server.ftpConfig) // set up connection once
|
|
68
|
-
.then(() => {
|
|
69
|
-
this.do(sshI);
|
|
70
|
-
})
|
|
71
|
-
.catch(err => {
|
|
72
|
-
if (err.code=='ECONNRESET' || err.code=='ECONNABORTED') _warn(`Server aborted connection. Retrying.`);
|
|
73
|
-
else _warn(err);
|
|
74
|
-
this.process(); // retry set up connection
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
|
|
80
26
|
const createDeliveryPack = () => { // create install scripts if necessary, zip output and remove temp folder
|
|
81
27
|
const dbiSql = [];
|
|
82
28
|
const dbiPs1 = [];
|
|
@@ -120,7 +66,7 @@ const createDeliveryPack = () => { // create install scripts if necessary, zip o
|
|
|
120
66
|
};
|
|
121
67
|
|
|
122
68
|
|
|
123
|
-
|
|
69
|
+
module.exports = function transfer (paths, {watch, lrServer} = {}) {
|
|
124
70
|
const jsFGlob = _settingsTdi().transpileToES5 ? ['**/*.js', '!**/hce/**/template_*', '!**/fonto/**', '!**/vendor/**', '!**/*.min.js'] : [];
|
|
125
71
|
const jsF = g_filter(jsFGlob, {restore: true});
|
|
126
72
|
const xpsF = g_filter(['**/xopus/**/*.x*'], {restore: true});
|
|
@@ -189,8 +135,6 @@ const transfer = (paths, {watch, lrServer} = {}) => {
|
|
|
189
135
|
_info('Finished transferring\n', true);
|
|
190
136
|
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 ')}`);
|
|
191
137
|
|
|
192
|
-
remote.process();
|
|
193
|
-
|
|
194
138
|
if (lrServer) { // reload specific resources if files are all image or css, else reload page
|
|
195
139
|
const reloadPage = !files.every(f => /.(?:css|jpe?g|png|gif)$/i.test(f));
|
|
196
140
|
lrServer.changed({params: {files: reloadPage ? ['page'] : files}});
|
|
@@ -199,7 +143,4 @@ const transfer = (paths, {watch, lrServer} = {}) => {
|
|
|
199
143
|
|
|
200
144
|
if (c.deliveryPack) createDeliveryPack();
|
|
201
145
|
});
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
module.exports = {remote, transfer};
|
|
146
|
+
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
const del = require('del');
|
|
2
2
|
const gulp = require('gulp');
|
|
3
3
|
const path = require('path');
|
|
4
|
+
const RemoteExec = require('../../lib/remote-exec');
|
|
4
5
|
const sftpClient = require('ssh2-sftp-client');
|
|
5
6
|
const tinylr = require('tiny-lr');
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
+
const transfer = require('./execute');
|
|
8
9
|
const c = require('./config');
|
|
9
10
|
const s = require('./srcset');
|
|
10
11
|
|
|
@@ -16,17 +17,34 @@ module.exports = function deploy (argv) {
|
|
|
16
17
|
c.setServer(argv.server);
|
|
17
18
|
c.prepareForCopy(argv.filter);
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const {ftpConfig, remotedir} = c.server;
|
|
21
|
+
|
|
22
|
+
if (ftpConfig) {
|
|
23
|
+
const logPath = path.join(remotedir, 'log/deployments.log').toFws;
|
|
24
|
+
const re = new RemoteExec(ftpConfig);
|
|
25
|
+
|
|
26
|
+
ftpConfig.eventPut = file => {
|
|
27
|
+
_write(file.destination.replace(remotedir, ''));
|
|
22
28
|
if (path.extname(file.destination)=='.sh')
|
|
23
|
-
|
|
29
|
+
re.add('chmod 755 '+file.destination, 'Permissions set: '+file.destination);
|
|
24
30
|
else if (file.destination.match(/cmscustom.*hce.*config.xml/))
|
|
25
|
-
|
|
31
|
+
re.add('touch '+c.getRemotePath('hce/hce-config.xml'), 'Touched: '+c.getRemotePath('hce/hce-config.xml'));
|
|
26
32
|
else if (file.destination.match(/cmscustom.*od[fts].*config.xml/))
|
|
27
|
-
|
|
33
|
+
re.add('touch '+c.getRemotePath('odf/odf-config.xml'), 'Touched: '+c.getRemotePath('odf/odf-config.xml'));
|
|
28
34
|
else if (file.destination.match(/txp\/site-configs/))
|
|
29
|
-
|
|
35
|
+
re.add('touch '+c.getRemotePath('txp/xmlpages-config.xml'), 'Touched: '+c.getRemotePath('txp/xmlpages-config.xml'));
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
ftpConfig.eventBeforeAll = async files => {
|
|
39
|
+
const dli = deployLogInfo(!argv.copy, c.transferPatterns[0], files, 'START');
|
|
40
|
+
await re.add(`echo -e "${dli}" >> ${logPath}`).process();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
ftpConfig.eventAfterAll = async files => {
|
|
44
|
+
_info(`${files.length} file(s) transferred`);
|
|
45
|
+
const dli = deployLogInfo(!argv.copy, c.transferPatterns[0], files, 'DONE');
|
|
46
|
+
re.add(`echo -e "${dli}" >> ${logPath}`).add(`echo "$(tail -10000 ${logPath})" > ${logPath}`);
|
|
47
|
+
await re.process();
|
|
30
48
|
};
|
|
31
49
|
}
|
|
32
50
|
|
|
@@ -64,9 +82,9 @@ module.exports = function deploy (argv) {
|
|
|
64
82
|
};
|
|
65
83
|
|
|
66
84
|
// check connection
|
|
67
|
-
if (
|
|
85
|
+
if (ftpConfig) {
|
|
68
86
|
const sftp = new sftpClient();
|
|
69
|
-
sftp.connect(
|
|
87
|
+
sftp.connect(ftpConfig).then(() => sftp.end()).catch(err => _error(`Could not connect to server${err ? ': '+err.message : ''}`));
|
|
70
88
|
}
|
|
71
89
|
|
|
72
90
|
gulp.watch(c.transferPatterns)
|
|
@@ -89,8 +107,8 @@ module.exports = function deploy (argv) {
|
|
|
89
107
|
|
|
90
108
|
if (!path.parse(filepath).base.match(/\.scss/)) {
|
|
91
109
|
const rp = c.getRemotePath(filepath);
|
|
92
|
-
const msg = 'Removed: ' + rp.replace(
|
|
93
|
-
if (
|
|
110
|
+
const msg = 'Removed: ' + rp.replace(remotedir, '').white;
|
|
111
|
+
if (ftpConfig) new RemoteExec(ftpConfig).add(`rm -rf "${rp}"`, msg).process();
|
|
94
112
|
else del([rp], {force: true}).then(() => _info(msg, true));
|
|
95
113
|
}
|
|
96
114
|
}
|
|
@@ -98,4 +116,46 @@ module.exports = function deploy (argv) {
|
|
|
98
116
|
|
|
99
117
|
}
|
|
100
118
|
|
|
101
|
-
};
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
function deployLogInfo (watch, filter, files, action) {
|
|
123
|
+
const timestamp = new Date().toISOString();
|
|
124
|
+
const user = _git.user().split('@')[0];
|
|
125
|
+
const {branch, hash} = _git.commitLocal();
|
|
126
|
+
const uncommittedChanges = _git.status().trim() ? ':~' : '';
|
|
127
|
+
|
|
128
|
+
let logline = `${timestamp} ${action.padEnd(5)} [${user}] [${branch}:${hash.substring(0, 7)}${uncommittedChanges}] [${filter}]`;
|
|
129
|
+
|
|
130
|
+
if (action == 'START') {
|
|
131
|
+
if (uncommittedChanges) {
|
|
132
|
+
const uncommittedChanges = _git.status().replace(/\?\?/g, ' U').split('\n');
|
|
133
|
+
logline += '\n Uncommitted changes:\n ' + uncommittedChanges.join('\n ');
|
|
134
|
+
}
|
|
135
|
+
const filepaths = files.map(({destination}) => destination);
|
|
136
|
+
logline += `\n Transferring ${filepaths.length} file${filepaths.length > 1 ? 's' : ''}`;
|
|
137
|
+
if (watch || filepaths.length == 1) logline += ':\n ' + filepaths.join('\n ');
|
|
138
|
+
else logline += ' in dir:\n ' + getCommonPath(filepaths);
|
|
139
|
+
}
|
|
140
|
+
return logline.replace(/"/g, '\\"');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
function getCommonPath(paths) {
|
|
145
|
+
const splitPaths = paths.map(p => p.split('/'));
|
|
146
|
+
let commonPath = splitPaths[0];
|
|
147
|
+
|
|
148
|
+
for (let i = 1; i < splitPaths.length; i++) {
|
|
149
|
+
const pathParts = splitPaths[i];
|
|
150
|
+
commonPath = commonPath.slice(0, Math.min(commonPath.length, pathParts.length));
|
|
151
|
+
|
|
152
|
+
for (let j = 0; j < commonPath.length; j++) {
|
|
153
|
+
if (commonPath[j] !== pathParts[j]) {
|
|
154
|
+
commonPath = commonPath.slice(0, j);
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return commonPath.length === 0 ? '' : commonPath.join('/');
|
|
161
|
+
}
|
|
@@ -6,7 +6,7 @@ const {Table} = require('console-table-printer');
|
|
|
6
6
|
const execGitCommand = require('../../lib/exec-git-command');
|
|
7
7
|
const getTdiBranch = require('../../lib/get-tdi-branch');
|
|
8
8
|
const c = require('../deploy/config');
|
|
9
|
-
const
|
|
9
|
+
const RemoteExec = require('../../lib/remote-exec');
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
const getGitInfo = () => {
|
|
@@ -142,7 +142,7 @@ const getServerInfo = (server) => {
|
|
|
142
142
|
|
|
143
143
|
if (!c.envDev) {
|
|
144
144
|
_info(`Remote version info for '${c.server.ftpConfig.host}':\n`);
|
|
145
|
-
|
|
145
|
+
new RemoteExec(c.server.ftpConfig).add('sudo ~root/scripts/version.sh', 'STDOUT').process();
|
|
146
146
|
}
|
|
147
147
|
else {
|
|
148
148
|
_info('For development environments no server version information is available. Check rancher / database for this information.\nAdd the --server option with a non-dev environment to see version information for that server.');
|