-
Notifications
You must be signed in to change notification settings - Fork 0
/
util.js
109 lines (95 loc) · 2.79 KB
/
util.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
const execa = require("execa");
const psTree = require("ps-tree");
// let's try gracefully, otherwise we can do SIGTERM or SIGKILL
const defaultKillSignal = "SIGINT";
const defaultLogger = process.env.DEBUG ? console.log : () => {};
export async function startBackgroundProcess({
cmd,
args,
execaOpts,
readyOutput,
logger = defaultLogger,
killSignal = defaultKillSignal
}) {
return new Promise((resolve, reject) => {
// start the process
const subprocess = execa(cmd, args, execaOpts);
let stdout = "";
let stderr = "";
let logPrefix;
if (args && args.length > 0) {
logPrefix = `${cmd} ${args[0]}`;
} else {
logPrefix = cmd;
}
logger(logPrefix, "spawned with PID: ", subprocess.pid);
// return this function so the process can be killed
const exit = () =>
new Promise((resolve, reject) => {
psTree(subprocess.pid, (err, children) => {
if (err) reject(err);
children.map(child => {
// each child has the properties: COMMAND, PPID, PID, STAT
logger(logPrefix, "killing child: ", child);
process.kill(child.PID, killSignal);
});
resolve();
});
});
subprocess.stdout.on("data", data => {
// parse
data = data.toString();
// log
logger(logPrefix, "stdout:", data);
// build output stream
stdout += data;
// check for ready signal
if (data.includes(readyOutput)) {
resolve({
exit,
stdout
});
}
});
subprocess.stderr.on("data", data => {
// parse
data = data.toString();
// log
logger(logPrefix, "stderr:", data);
// build error stream
stderr += data;
});
subprocess.on("close", (code, signal) => {
// log
logger(logPrefix, "closing with code:", code, "and signal:", signal);
// reject only if the promise did not previously resolve
// which means this is probably getting killed by the test which is ok
if (!stdout.includes(readyOutput)) {
const err = new Error(
`Process closed unexpectedly with code ${code} and signal ${signal}`
);
err.stdout = stdout;
err.stderr = stderr;
reject(err);
}
});
subprocess.on("exit", (code, signal) => {
logger(logPrefix, "exiting with code:", code, "and signal:", signal);
});
});
}
/**
* Some characters are rendered differently depending on the OS.
*
* @param {string} stdout
*/
export function normalizeOutput(stdout) {
const next = stdout
.replace(/❯/g, ">")
.replace(/ℹ/g, "i")
// TODO: remove after https://github.com/aragon/aragon-cli/issues/367 is fixed
.replace(/cli.js/g, "aragon")
// sometimes there's an extra LF
.trim();
return next;
}