cob-cli 2.51.0 → 2.52.0-beta-2
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/bin/cob-cli.js +31 -0
- package/lib/commands/cleanup.js +116 -0
- package/lib/commands/deploy.js +33 -26
- package/lib/commands/package.js +20 -0
- package/lib/commands/tagDefs.js +67 -0
- package/lib/commands/test.js +29 -9
- package/lib/commands/updateFromServer.js +149 -13
- package/lib/task_lists/common_definitions.js +26 -0
- package/lib/task_lists/common_enviromentHandler.js +14 -8
- package/lib/task_lists/common_helpers.js +95 -17
- package/lib/task_lists/common_operationState.js +28 -0
- package/lib/task_lists/common_releaseManager.js +10 -8
- package/lib/task_lists/common_syncFiles.js +9 -4
- package/lib/task_lists/package_execute.js +45 -0
- package/lib/task_lists/tagDefs_execute.js +121 -0
- package/lib/task_lists/test_customUIsContinuosReload.js +36 -7
- package/lib/task_lists/test_otherFilesContiousReload.js +44 -22
- package/lib/task_lists/test_syncFile.js +5 -4
- package/lib/task_lists/test_validate.js +3 -2
- package/lib/task_lists/updateFromServer_definitions.js +135 -0
- package/lib/task_lists/updateFromServer_domains.js +50 -0
- package/lib/task_lists/updateFromServer_groups.js +111 -0
- package/lib/task_lists/updateFromServer_instances.js +76 -0
- package/lib/task_lists/updateFromServer_kibana.js +88 -0
- package/lib/task_lists/updateFromServer_validate.js +3 -3
- package/lib/webpack/webpack.config.js +1 -1
- package/package.json +5 -1
|
@@ -5,6 +5,7 @@ const git = require('simple-git');
|
|
|
5
5
|
const fs = require('fs-extra');
|
|
6
6
|
const { getCurrentBranch } = require("./common_releaseManager");
|
|
7
7
|
const { SERVER_COB_CLI_DIRECTORY } = require("./common_helpers")
|
|
8
|
+
const { getSshArgs } = require("./common_helpers")
|
|
8
9
|
|
|
9
10
|
const SERVER_LAST_ENV_FILE = SERVER_COB_CLI_DIRECTORY + "lastDeployEnv";
|
|
10
11
|
|
|
@@ -66,7 +67,7 @@ exports.getCurrentCommandEnviroment = getCurrentCommandEnviroment;
|
|
|
66
67
|
|
|
67
68
|
/* ************************************ */
|
|
68
69
|
async function _applySpecific(environmentName) {
|
|
69
|
-
const envSpecificFiles = await fg(['**/*.ENV__'+ environmentName +'__.*'], { onlyFiles: false, dot: true });
|
|
70
|
+
const envSpecificFiles = await fg(['**/*.ENV__'+ environmentName +'__.*'], { onlyFiles: false, dot: true });
|
|
70
71
|
|
|
71
72
|
for( let envSpecificFile of envSpecificFiles ) {
|
|
72
73
|
let prodFile = envSpecificFile.replace(/\.ENV__.*__/,"")
|
|
@@ -74,7 +75,7 @@ async function _applySpecific(environmentName) {
|
|
|
74
75
|
if(!fs.existsSync(deleteFile)) {
|
|
75
76
|
if(fs.existsSync(prodFile)) {
|
|
76
77
|
let backupFile = envSpecificFile.replace(/\.ENV__.*__/,".ENV__ORIGINAL_BACKUP__")
|
|
77
|
-
await git().raw(["update-index","--assume-unchanged", prodFile])
|
|
78
|
+
await git().raw(["update-index","--assume-unchanged", prodFile]).catch(() => {})
|
|
78
79
|
fs.renameSync(prodFile, backupFile)
|
|
79
80
|
} else {
|
|
80
81
|
fs.appendFileSync('.git/info/exclude', "\n" + prodFile)
|
|
@@ -82,7 +83,7 @@ async function _applySpecific(environmentName) {
|
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
85
|
fs.renameSync(envSpecificFile, prodFile)
|
|
85
|
-
await git().raw(["update-index","--assume-unchanged", envSpecificFile])
|
|
86
|
+
await git().raw(["update-index","--assume-unchanged", envSpecificFile]).catch(() => {})
|
|
86
87
|
}
|
|
87
88
|
}
|
|
88
89
|
|
|
@@ -94,11 +95,11 @@ async function _undoSpecific(environmentName) {
|
|
|
94
95
|
let prodFile = changedFile.replace(/\.ENV__.*__/,"")
|
|
95
96
|
let originalEnvSpecificFile = changedFile.replace(/\.ENV__.*__/,'.ENV__' + environmentName + '__')
|
|
96
97
|
fs.renameSync(prodFile, originalEnvSpecificFile)
|
|
97
|
-
await git().raw(["update-index","--no-assume-unchanged", originalEnvSpecificFile])
|
|
98
|
-
|
|
98
|
+
await git().raw(["update-index","--no-assume-unchanged", originalEnvSpecificFile]).catch(() => {})
|
|
99
|
+
|
|
99
100
|
if( changedFile.indexOf("\.ENV__ORIGINAL_BACKUP__") > 0 ) {
|
|
100
101
|
fs.renameSync(changedFile, prodFile)
|
|
101
|
-
await git().raw(["update-index","--no-assume-unchanged", prodFile])
|
|
102
|
+
await git().raw(["update-index","--no-assume-unchanged", prodFile]).catch(() => {})
|
|
102
103
|
}
|
|
103
104
|
if( changedFile.indexOf("\.ENV__DELETE__") > 0 ) {
|
|
104
105
|
flagDeleteExclude = true
|
|
@@ -120,6 +121,8 @@ function _getDefaultServerForEnvironment(environmentName) {
|
|
|
120
121
|
|
|
121
122
|
/* ************************************ */
|
|
122
123
|
function _getServerSSHFQDN(servername) {
|
|
124
|
+
// return IPs as-is
|
|
125
|
+
if (servername.includes(".")) return servername;
|
|
123
126
|
return servername + ".cultofbits.pt";
|
|
124
127
|
}
|
|
125
128
|
|
|
@@ -131,16 +134,19 @@ function _getServerHTTPsFQDN(servername) {
|
|
|
131
134
|
/* ************************************ */
|
|
132
135
|
async function _getLastEnvironmentDeployed(server) {
|
|
133
136
|
try {
|
|
134
|
-
let result = await execa('ssh', [server, "cat " + SERVER_LAST_ENV_FILE ]);
|
|
137
|
+
let result = await execa('ssh', [...getSshArgs(server), "cat " + SERVER_LAST_ENV_FILE ]);
|
|
135
138
|
return result.stdout;
|
|
136
139
|
} catch (error) {
|
|
140
|
+
if (error.exitCode === 255) {
|
|
141
|
+
throw new Error(`SSH connection to '${server}' failed: ${error.shortMessage}`);
|
|
142
|
+
}
|
|
137
143
|
return "prod"
|
|
138
144
|
}
|
|
139
145
|
}
|
|
140
146
|
|
|
141
147
|
/* ************************************ */
|
|
142
148
|
async function _setLastEnvironmentDeployed(server, environmentName) {
|
|
143
|
-
await execa('ssh', [server, "echo '" + environmentName + "' > " + SERVER_LAST_ENV_FILE ]);
|
|
149
|
+
await execa('ssh', [...getSshArgs(server), "echo '" + environmentName + "' > " + SERVER_LAST_ENV_FILE ]);
|
|
144
150
|
}
|
|
145
151
|
|
|
146
152
|
/* ************************************ */
|
|
@@ -1,5 +1,42 @@
|
|
|
1
|
+
const inquirer = require("inquirer")
|
|
2
|
+
const readline = require('readline');
|
|
3
|
+
const yaml = require('js-yaml');
|
|
4
|
+
const fs = require("fs/promises");
|
|
5
|
+
|
|
1
6
|
const SERVER_COB_CLI_DIRECTORY = "/opt/cob-cli/";
|
|
2
7
|
|
|
8
|
+
/* ************************************ */
|
|
9
|
+
function getSshArgs(server) {
|
|
10
|
+
const args = [
|
|
11
|
+
"-o", "StrictHostKeyChecking=no",
|
|
12
|
+
"-o", "PreferredAuthentications=publickey",
|
|
13
|
+
"-o", "BatchMode=yes",
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
if (process.env.COB_SSH_PORT) args.push("-p", process.env.COB_SSH_PORT);
|
|
17
|
+
if (process.env.COB_SSH_IDENTITY) args.push("-i", process.env.COB_SSH_IDENTITY);
|
|
18
|
+
|
|
19
|
+
const target = process.env.COB_SSH_USER ? `${process.env.COB_SSH_USER}@${server}` : server;
|
|
20
|
+
args.push(target);
|
|
21
|
+
|
|
22
|
+
return args;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* ************************************ */
|
|
26
|
+
function getServerAddress(server) {
|
|
27
|
+
return process.env.COB_SSH_USER ? `${process.env.COB_SSH_USER}@${server}` : server;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* ************************************ */
|
|
31
|
+
function getRsyncSsh() {
|
|
32
|
+
let cmd = "ssh -o ConnectTimeout=30 -o ServerAliveInterval=30 -o ServerAliveCountMax=30 -o StrictHostKeyChecking=no";
|
|
33
|
+
|
|
34
|
+
if (process.env.COB_SSH_IDENTITY) cmd += ` -i '${process.env.COB_SSH_IDENTITY}'`;
|
|
35
|
+
if (process.env.COB_SSH_PORT) cmd += ` -p ${process.env.COB_SSH_PORT}`;
|
|
36
|
+
|
|
37
|
+
return cmd;
|
|
38
|
+
}
|
|
39
|
+
|
|
3
40
|
/* **************************************************************************************** */
|
|
4
41
|
async function checkWorkingCopyCleanliness(testNotDetached=true) {
|
|
5
42
|
const git = require('simple-git');
|
|
@@ -32,40 +69,81 @@ async function checkWorkingCopyCleanliness(testNotDetached=true) {
|
|
|
32
69
|
function checkConnectivity(server) {
|
|
33
70
|
const execa = require('execa');
|
|
34
71
|
return execa('ssh', [
|
|
35
|
-
"-o", "StrictHostKeyChecking=no",
|
|
36
|
-
"-o", "PreferredAuthentications=publickey",
|
|
37
|
-
"-o", "BatchMode=yes",
|
|
38
72
|
"-o", "ConnectTimeout=15",
|
|
39
|
-
server,
|
|
73
|
+
...getSshArgs(server),
|
|
40
74
|
"test -w /etc/recordm/services/com.cultofbits.web.integration.properties && exit || exit 1"
|
|
41
75
|
])
|
|
42
76
|
}
|
|
43
77
|
|
|
78
|
+
|
|
44
79
|
/* ************************************ */
|
|
80
|
+
let readlineSetup = false
|
|
45
81
|
function getKeypress() {
|
|
46
|
-
|
|
47
|
-
|
|
82
|
+
if (!readlineSetup) {
|
|
83
|
+
readline.emitKeypressEvents(process.stdin);
|
|
84
|
+
readlineSetup = true
|
|
85
|
+
}
|
|
48
86
|
process.stdin.setRawMode(true);
|
|
49
|
-
readline.emitKeypressEvents(process.stdin);
|
|
50
87
|
const rl = readline.createInterface({
|
|
51
88
|
input: process.stdin
|
|
52
89
|
});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
90
|
+
|
|
91
|
+
return new Promise(resolve => {
|
|
92
|
+
const listener = function(letter,key){
|
|
93
|
+
if(key.name == "return") {
|
|
94
|
+
console.log("")
|
|
95
|
+
|
|
96
|
+
} else {
|
|
97
|
+
if(key.ctrl && key.name == 'c') letter = "ctrl+c"
|
|
98
|
+
rl.input.removeListener('keypress', listener)
|
|
99
|
+
rl.close();
|
|
100
|
+
process.stdin.setRawMode(false);
|
|
101
|
+
resolve(letter && letter.toLowerCase() || key.name);
|
|
102
|
+
}
|
|
61
103
|
}
|
|
62
|
-
|
|
104
|
+
rl.input.on("keypress", listener)
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* ************************************ */
|
|
109
|
+
async function promptCredentials() {
|
|
110
|
+
const answers = await inquirer.prompt([
|
|
111
|
+
{
|
|
112
|
+
type: 'text',
|
|
113
|
+
message: 'Enter username:',
|
|
114
|
+
name: 'username',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
type: 'password',
|
|
118
|
+
message: 'Enter password:',
|
|
119
|
+
name: 'password',
|
|
120
|
+
mask: '*',
|
|
121
|
+
},
|
|
122
|
+
])
|
|
123
|
+
|
|
124
|
+
return answers
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/* ************************************ */
|
|
128
|
+
async function readYamlFile(filename) {
|
|
129
|
+
try {
|
|
130
|
+
const data = await fs.readFile(filename, 'utf-8');
|
|
131
|
+
const config = yaml.load(data);
|
|
132
|
+
return config
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error('Error reading YAML file:', error.message);
|
|
135
|
+
}
|
|
63
136
|
}
|
|
64
137
|
|
|
65
138
|
/* ************************************ */
|
|
66
139
|
module.exports = {
|
|
67
140
|
SERVER_COB_CLI_DIRECTORY : SERVER_COB_CLI_DIRECTORY,
|
|
141
|
+
getSshArgs: getSshArgs,
|
|
142
|
+
getServerAddress: getServerAddress,
|
|
143
|
+
getRsyncSsh: getRsyncSsh,
|
|
68
144
|
checkConnectivity: checkConnectivity,
|
|
69
145
|
checkWorkingCopyCleanliness: checkWorkingCopyCleanliness,
|
|
70
|
-
getKeypress: getKeypress
|
|
146
|
+
getKeypress: getKeypress,
|
|
147
|
+
promptCredentials: promptCredentials,
|
|
148
|
+
readYamlFile: readYamlFile
|
|
71
149
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const fg = require('fast-glob');
|
|
3
|
+
const STATE_FILE = ".git/cob-cli-state";
|
|
4
|
+
|
|
5
|
+
function saveOperationState(environment, servername) {
|
|
6
|
+
try { fs.writeJsonSync(STATE_FILE, { environment, servername }); } catch {}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function loadOperationState() {
|
|
10
|
+
try { return fs.readJsonSync(STATE_FILE); } catch { return null; }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function clearOperationState() {
|
|
14
|
+
try { fs.unlinkSync(STATE_FILE); } catch {}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function checkNoInterruptedRun() {
|
|
18
|
+
const stateExists = !!loadOperationState();
|
|
19
|
+
const envBackupFiles = await fg(['**/*.ENV__ORIGINAL_BACKUP__.*', '**/*.ENV__DELETE__.*'], { onlyFiles: false, dot: true });
|
|
20
|
+
if (stateExists || envBackupFiles.length > 0) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
"\nAborted:".red + " found traces of a previous interrupted test/deploy.\n"
|
|
23
|
+
+ "Run " + "cob-cli cleanup".yellow + " to restore the repo to a clean state first.\n"
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = { saveOperationState, loadOperationState, clearOperationState, checkNoInterruptedRun };
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
const execa = require('execa');
|
|
2
|
-
const fs = require('fs-extra');
|
|
3
2
|
const git = require('simple-git');
|
|
4
3
|
|
|
5
4
|
const SERVER_CHANGELOG = "/opt/cob-cli/DEPLOYLOG.md";
|
|
6
5
|
const LAST_SHA_FILE = "lastDeploySha"
|
|
7
|
-
const { SERVER_COB_CLI_DIRECTORY } = require("./common_helpers");
|
|
6
|
+
const { SERVER_COB_CLI_DIRECTORY, getSshArgs } = require("./common_helpers");
|
|
8
7
|
const SERVER_LAST_SHA_FILE = SERVER_COB_CLI_DIRECTORY + LAST_SHA_FILE;
|
|
9
8
|
|
|
10
9
|
/* ************************************ */
|
|
@@ -44,18 +43,18 @@ async function registerRelease(cmdEnv) {
|
|
|
44
43
|
const dateTimeStr = date.toISOString().slice(0,16).replace(/T/," ")
|
|
45
44
|
const deploySignature = cmdEnv.servername + "_" + dateStr
|
|
46
45
|
const newDeployText = `${dateTimeStr} ${user} [${currentSha}/${cmdEnv.currentBranch}]`
|
|
47
|
-
await execa('ssh', [cmdEnv.server, "echo $'" + newDeployText + "\n" + diffs.replace(/'/g,"\\'") + "' >> " + SERVER_CHANGELOG])
|
|
46
|
+
await execa('ssh', [...getSshArgs(cmdEnv.server), "echo $'" + newDeployText + "\n" + diffs.replace(/'/g,"\\'") + "' >> " + SERVER_CHANGELOG])
|
|
48
47
|
|
|
49
48
|
if (diffs) {
|
|
50
49
|
// Add deploy tags to repo
|
|
51
|
-
git().fetch(["--tags","-f"]) // Apenas para ter certeza que temos todas as tags
|
|
50
|
+
await git().fetch(["--tags","-f"]) // Apenas para ter certeza que temos todas as tags
|
|
52
51
|
try {
|
|
53
52
|
await git().silent(true).tag([deploySignature, "-d"])
|
|
54
53
|
} catch {}
|
|
55
54
|
|
|
56
55
|
await git().addAnnotatedTag(deploySignature, newDeployText )
|
|
57
56
|
await git().push(["-f", "origin", deploySignature])
|
|
58
|
-
git().fetch(["--tags","-f"]) // Apenas para ter certeza que temos todas as tags
|
|
57
|
+
await git().fetch(["--tags","-f"]) // Apenas para ter certeza que temos todas as tags
|
|
59
58
|
}
|
|
60
59
|
_setLastDeployedSha(cmdEnv.server, currentSha);
|
|
61
60
|
}
|
|
@@ -65,9 +64,12 @@ exports.registerRelease = registerRelease;
|
|
|
65
64
|
async function getLastDeployedSha(server) {
|
|
66
65
|
let result
|
|
67
66
|
try {
|
|
68
|
-
result = await execa('ssh', [server, "cat " + SERVER_LAST_SHA_FILE ]);
|
|
67
|
+
result = await execa('ssh', [...getSshArgs(server), "cat " + SERVER_LAST_SHA_FILE ]);
|
|
69
68
|
} catch (error) {
|
|
70
|
-
|
|
69
|
+
if (error.exitCode === 255) {
|
|
70
|
+
throw new Error(`SSH connection to '${server}' failed: ${error.shortMessage}`);
|
|
71
|
+
}
|
|
72
|
+
await execa('ssh', [...getSshArgs(server), "mkdir -p " + SERVER_COB_CLI_DIRECTORY ]);
|
|
71
73
|
return null
|
|
72
74
|
}
|
|
73
75
|
return result.stdout;
|
|
@@ -76,7 +78,7 @@ exports.getLastDeployedSha = getLastDeployedSha;
|
|
|
76
78
|
|
|
77
79
|
/* ************************************ */
|
|
78
80
|
function _setLastDeployedSha(server, lastSha) {
|
|
79
|
-
execa('ssh', [server, "echo '" + lastSha + "' > " + SERVER_LAST_SHA_FILE ]);
|
|
81
|
+
execa('ssh', [...getSshArgs(server), "echo '" + lastSha + "' > " + SERVER_LAST_SHA_FILE ]);
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
/* ************************************ */
|
|
@@ -2,6 +2,7 @@ const execa = require('execa');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const fs = require('fs-extra');
|
|
4
4
|
const { getKeypress } = require("../task_lists/common_helpers");
|
|
5
|
+
const { getRsyncSsh, getServerAddress } = require("./common_helpers");
|
|
5
6
|
|
|
6
7
|
/* ************************************ */
|
|
7
8
|
const COB_PRODUCTS = ["recordm", "integrationm", "recordm-importer", "reportm", "userm", "confm", "others", "authm"];
|
|
@@ -32,6 +33,7 @@ async function _syncFiles(executionType, cmdEnv, from, to, extraOptions = [], ar
|
|
|
32
33
|
return new Promise(async (resolve, reject) => {
|
|
33
34
|
let fromPath = resolveCobPath(cmdEnv.server, from, product);
|
|
34
35
|
let toPath = resolveCobPath(cmdEnv.server, to, product);
|
|
36
|
+
if (toPath.startsWith('.') && !fs.existsSync(toPath)) fs.mkdirSync(toPath)
|
|
35
37
|
let productExtraOptions = extraOptions.map( option => option.replace(/=.*\|/,"="))
|
|
36
38
|
productExtraOptions = productExtraOptions.filter( option => option.indexOf("|") < 0 )
|
|
37
39
|
|
|
@@ -64,7 +66,7 @@ async function _syncFiles(executionType, cmdEnv, from, to, extraOptions = [], ar
|
|
|
64
66
|
}
|
|
65
67
|
await execa('rsync', rsyncArgs, {
|
|
66
68
|
shell: true,
|
|
67
|
-
env: { "RSYNC_RSH":
|
|
69
|
+
env: { "RSYNC_RSH": getRsyncSsh() }
|
|
68
70
|
})
|
|
69
71
|
.then((value) => {
|
|
70
72
|
if (args.verbose > 3)
|
|
@@ -169,16 +171,19 @@ function resolveCobPath(server, type, product) {
|
|
|
169
171
|
return "./" + product + "/";
|
|
170
172
|
|
|
171
173
|
case "serverInitial":
|
|
172
|
-
return server + ":/opt/" + product + "/etc.default/";
|
|
174
|
+
return getServerAddress(server) + ":/opt/" + product + "/etc.default/";
|
|
173
175
|
|
|
174
176
|
case "serverLive":
|
|
175
177
|
switch (product) {
|
|
176
178
|
case "recordm-importer":
|
|
177
179
|
case "others":
|
|
178
|
-
return server + ':' + "/opt/" + product + "/";
|
|
180
|
+
return getServerAddress(server) + ':' + "/opt/" + product + "/";
|
|
179
181
|
default:
|
|
180
|
-
return server + ':' + "/etc/" + product + "/";
|
|
182
|
+
return getServerAddress(server) + ':' + "/etc/" + product + "/";
|
|
181
183
|
}
|
|
184
|
+
|
|
185
|
+
case "staging":
|
|
186
|
+
return ".staging/" + product + "/";
|
|
182
187
|
}
|
|
183
188
|
}
|
|
184
189
|
exports.resolveCobPath = resolveCobPath;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const Listr = require('listr');
|
|
2
|
+
const UpdaterRenderer = require('listr-update-renderer');
|
|
3
|
+
const verboseRenderer = require('listr-verbose-renderer');
|
|
4
|
+
const { copyFiles } = require("./common_syncFiles");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const tar = require('tar');
|
|
7
|
+
const { format } = require('date-fns');
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function executeTasks(cmdEnv, args) {
|
|
11
|
+
console.log("\nPackaging ...");
|
|
12
|
+
|
|
13
|
+
return new Listr([
|
|
14
|
+
{title: "Apply new enviroment specifics", task: () => cmdEnv.applyCurrentCommandEnvironmentChanges() },
|
|
15
|
+
{title: "Create staging dir", task: () => fs.existsSync('.staging') || fs.mkdirSync('.staging') },
|
|
16
|
+
{title: "Copy files to staging dir", task: () => copyFiles(cmdEnv, "localCopy", "staging")},
|
|
17
|
+
{title: "Create package", task: async () => { const filename = await createPackage(cmdEnv, args); console.log("created " + filename) }},
|
|
18
|
+
{title: "Sign package", task: () => console.log("lololo")},
|
|
19
|
+
{title: "Delete staging dir", task: () => fs.rmSync('.staging', {recursive: true, force: true})},
|
|
20
|
+
{title: "Undo new enviroment specifics", task: () => cmdEnv.unApplyCurrentCommandEnvironmentChanges() },
|
|
21
|
+
], {
|
|
22
|
+
renderer: args.verbose ? verboseRenderer : UpdaterRenderer,
|
|
23
|
+
collapse: false
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
exports.executeTasks = executeTasks;
|
|
27
|
+
|
|
28
|
+
const createPackage = async function(cmdEnv, args){
|
|
29
|
+
const filename = `${cmdEnv.servername}-${cmdEnv.name}-${format(new Date(), 'yyyy-MM-dd_HH_mm')}.tar.gz`;
|
|
30
|
+
const files = fs.readdirSync('.staging')
|
|
31
|
+
if (args.verbose > 0){
|
|
32
|
+
console.log("packaging " + files)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await tar.create({
|
|
36
|
+
gzip: true,
|
|
37
|
+
file: filename,
|
|
38
|
+
cwd: '.staging',
|
|
39
|
+
onWriteEntry(entry) {
|
|
40
|
+
if (args.verbose > 2) console.log('adding', entry.path, entry.stat.mode.toString(8))
|
|
41
|
+
}
|
|
42
|
+
}, files)
|
|
43
|
+
|
|
44
|
+
return filename;
|
|
45
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
require('colors');
|
|
2
|
+
const Listr = require('listr');
|
|
3
|
+
const UpdaterRenderer = require('listr-update-renderer');
|
|
4
|
+
const VerboseRenderer = require('listr-verbose-renderer');
|
|
5
|
+
const { getFilteredDefinitions } = require('./common_definitions.js')
|
|
6
|
+
const axios = require("axios");
|
|
7
|
+
const inquirer = require("inquirer")
|
|
8
|
+
|
|
9
|
+
async function getUntaggedChanges(servername, defId) {
|
|
10
|
+
const uri = `https://${servername}.cultofbits.com/recordm/recordm/definitions/${defId}/changes?tagged=false`
|
|
11
|
+
|
|
12
|
+
const response = await axios.get(uri, {params: {tagged: false}});
|
|
13
|
+
|
|
14
|
+
if(response.status % 200 > 100){
|
|
15
|
+
throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return ( response.data || []).filter(c => !c.tag)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function tagDefinition(servername, defId, until) {
|
|
22
|
+
const uri = `https://${servername}.cultofbits.com/recordm/recordm/definitions/${defId}/tag`
|
|
23
|
+
|
|
24
|
+
const params = {}
|
|
25
|
+
if(until) {
|
|
26
|
+
params.until = until
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const response = await axios.put(uri, "", {headers: {'Content-Type': 'application/json'}, params: params});
|
|
30
|
+
|
|
31
|
+
if(response.status != 200){
|
|
32
|
+
throw new Error(`HTTP Error Response: ${response.status} ${response.statusText}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return response.data
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function tagDefinitions(servername, customization, defs, args) {
|
|
39
|
+
return new Listr([
|
|
40
|
+
{
|
|
41
|
+
title: `(${customization}) Definitions: `.bold + "searching for definitions matching queries",
|
|
42
|
+
task: async ctx => ctx.defs = (await getFilteredDefinitions(servername, defs))
|
|
43
|
+
.map(d => ({id: d.id, name: d.name}))
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
title: `(${customization}) Definitions: `.bold + "enriching metadata",
|
|
47
|
+
task: async ctx => {
|
|
48
|
+
for (const def of ctx.defs){
|
|
49
|
+
def.changes = await getUntaggedChanges(servername, def.id)
|
|
50
|
+
if(args.verbose) console.log(`definition ${def.name}: ${def.changes.length} untagged changes`)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
title: `(${customization}) Definitions: `.bold + "tagging un-tagged changes",
|
|
56
|
+
task: async ctx => {
|
|
57
|
+
const subtasks = ctx.defs.map(d => ({
|
|
58
|
+
title: "Tagging changes to " + d.name.bold,
|
|
59
|
+
task: async (_, task) => {
|
|
60
|
+
if (d.changes.length == 0) {
|
|
61
|
+
task.skip("no untagged changes")
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if(args.all) {
|
|
66
|
+
await tagDefinition(servername, d.id)
|
|
67
|
+
task.output = 'Tagged by all'
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
d.changes.forEach(c => console.log(JSON.stringify(c)))
|
|
72
|
+
|
|
73
|
+
const answer = await inquirer.prompt([
|
|
74
|
+
{
|
|
75
|
+
type: 'rawlist',
|
|
76
|
+
name: 'action',
|
|
77
|
+
message: `Do you wish to tag the ${d.changes.length} changes? (^c to abort)`,
|
|
78
|
+
choices: [
|
|
79
|
+
'Tag Changes',
|
|
80
|
+
'Choose Changes',
|
|
81
|
+
'Skip',
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
])
|
|
85
|
+
if (answer.action == 'Skip') {
|
|
86
|
+
task.skip()
|
|
87
|
+
|
|
88
|
+
} else if (answer.action == 'Tag Changes') {
|
|
89
|
+
await tagDefinition(servername, d.id)
|
|
90
|
+
task.output = 'Tagged'
|
|
91
|
+
|
|
92
|
+
} else if (answer.action == 'Choose Changes') {
|
|
93
|
+
const dates = d.changes.reduce((acc, cur) => {
|
|
94
|
+
if(!acc.includes(cur.date)) acc.push(cur.date)
|
|
95
|
+
return acc
|
|
96
|
+
}, [])
|
|
97
|
+
const answerDate = await inquirer.prompt([
|
|
98
|
+
{
|
|
99
|
+
type: 'rawlist',
|
|
100
|
+
name: 'date',
|
|
101
|
+
message: `Choose the oldest date to tag`,
|
|
102
|
+
choices: dates
|
|
103
|
+
}
|
|
104
|
+
])
|
|
105
|
+
await tagDefinition(servername, d.id, answerDate.date)
|
|
106
|
+
task.output = 'Tagged until ' + answerDate.date
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}))
|
|
110
|
+
return new Listr(subtasks)
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
],{
|
|
114
|
+
// we need to list changes and read user input, the VerboseRenderer is needed
|
|
115
|
+
renderer: VerboseRenderer,
|
|
116
|
+
collapse: false
|
|
117
|
+
}).run()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
exports.tagDefinitions = tagDefinitions
|
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
const execa = require('execa');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const net = require('net');
|
|
3
4
|
const concurrently = require('concurrently');
|
|
4
5
|
const fs= require('fs');
|
|
5
6
|
const util = require('util');
|
|
6
7
|
const child_process = require('child_process');
|
|
7
8
|
const exec = util.promisify(child_process.exec);
|
|
8
9
|
|
|
10
|
+
function findPort(preferred = 8040) {
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
const server = net.createServer();
|
|
13
|
+
server.listen(preferred, '0.0.0.0', () => {
|
|
14
|
+
server.close(() => resolve(preferred));
|
|
15
|
+
});
|
|
16
|
+
server.on('error', () => {
|
|
17
|
+
const fallback = net.createServer();
|
|
18
|
+
fallback.listen(0, '0.0.0.0', () => {
|
|
19
|
+
const port = fallback.address().port;
|
|
20
|
+
fallback.close(() => resolve(port));
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
9
26
|
|
|
10
27
|
async function customUIsContinuosReload(cmdEnv, dashboard) {
|
|
11
28
|
process.env.dash_dir = dashboard
|
|
@@ -22,6 +39,9 @@ async function customUIsContinuosReload(cmdEnv, dashboard) {
|
|
|
22
39
|
throw new Error("Aborted: ".red + " dashboard " + dashboard.bold + " does not exist.\n ")
|
|
23
40
|
}
|
|
24
41
|
|
|
42
|
+
const dashPort = await findPort(8041);
|
|
43
|
+
process.env.dash_port = dashPort;
|
|
44
|
+
|
|
25
45
|
if(dashboardPathIsDirectory) {
|
|
26
46
|
// If we have a dashboard to serve run it from local dashboard directory on 8081 with vue-cli-service
|
|
27
47
|
// it will use its vue.config.js
|
|
@@ -31,13 +51,15 @@ async function customUIsContinuosReload(cmdEnv, dashboard) {
|
|
|
31
51
|
commands.push( {
|
|
32
52
|
command: [
|
|
33
53
|
"cd " + dashboardPath + ";",
|
|
34
|
-
"node_modules/.bin/vue-cli-service serve --port
|
|
35
|
-
].join(" "),
|
|
54
|
+
"node_modules/.bin/vue-cli-service serve --port " + dashPort + " 2>/dev/null"
|
|
55
|
+
].join(" "),
|
|
36
56
|
name: 'dashboard server',
|
|
37
|
-
prefixColor: "red"
|
|
57
|
+
prefixColor: "red"
|
|
38
58
|
})
|
|
39
59
|
}
|
|
40
60
|
|
|
61
|
+
const port = await findPort(8040);
|
|
62
|
+
|
|
41
63
|
console.log( "\n" + (" NOTE: Press " + "O".bold.red + " to open default browser, " + "CTRL+C".bold.red + " or " + "Q".bold.red + " to stop the tests... ").yellow.bold + "\n\n" )
|
|
42
64
|
|
|
43
65
|
const insideCobCliPath = path.resolve(__dirname, '../../node_modules/.bin/webpack-dev-server');
|
|
@@ -50,8 +72,9 @@ async function customUIsContinuosReload(cmdEnv, dashboard) {
|
|
|
50
72
|
webpackDevServerPath,
|
|
51
73
|
"--config",
|
|
52
74
|
path.resolve(__dirname, "../webpack/webpack.config.js"),
|
|
53
|
-
"--mode=development"
|
|
54
|
-
|
|
75
|
+
"--mode=development",
|
|
76
|
+
"--port", port
|
|
77
|
+
].join(" "),
|
|
55
78
|
name: 'customUIs server',
|
|
56
79
|
prefixColor: "blue"
|
|
57
80
|
})
|
|
@@ -82,14 +105,20 @@ async function customUIsContinuosReload(cmdEnv, dashboard) {
|
|
|
82
105
|
}
|
|
83
106
|
}
|
|
84
107
|
|
|
85
|
-
|
|
108
|
+
let notifyFailure;
|
|
109
|
+
const failurePromise = new Promise((_, reject) => { notifyFailure = reject; });
|
|
110
|
+
|
|
111
|
+
conc.result.catch( _err => {
|
|
86
112
|
if(isStopping) return;
|
|
87
113
|
|
|
88
114
|
stopEverything();
|
|
115
|
+
notifyFailure(new Error('processes-failed'));
|
|
89
116
|
});
|
|
90
117
|
|
|
91
118
|
return {
|
|
92
|
-
stop: stopEverything
|
|
119
|
+
stop: stopEverything,
|
|
120
|
+
failed: failurePromise,
|
|
121
|
+
port
|
|
93
122
|
};
|
|
94
123
|
}
|
|
95
124
|
exports.customUIsContinuosReload = customUIsContinuosReload;
|