-
-
Notifications
You must be signed in to change notification settings - Fork 197
/
Copy pathcleanup-service.ts
157 lines (127 loc) · 4.69 KB
/
cleanup-service.ts
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import { ChildProcess } from "child_process";
import * as path from "path";
import { cache, exported } from "../common/decorators";
export class CleanupService implements ICleanupService {
private static CLEANUP_PROCESS_START_TIMEOUT = 10 * 1000;
private pathToCleanupLogFile: string;
private cleanupProcess: ChildProcess;
constructor($options: IOptions,
private $staticConfig: Config.IStaticConfig,
private $childProcess: IChildProcess) {
this.pathToCleanupLogFile = $options.cleanupLogFile;
}
public shouldDispose = true;
public async addCleanupCommand(commandInfo: ISpawnCommandInfo): Promise<void> {
const cleanupProcess = await this.getCleanupProcess();
cleanupProcess.send(<ISpawnCommandCleanupMessage>{ messageType: CleanupProcessMessage.AddCleanCommand, commandInfo });
}
public async removeCleanupCommand(commandInfo: ISpawnCommandInfo): Promise<void> {
const cleanupProcess = await this.getCleanupProcess();
cleanupProcess.send(<ISpawnCommandCleanupMessage>{ messageType: CleanupProcessMessage.RemoveCleanCommand, commandInfo });
}
public async addCleanupDeleteAction(filePath: string): Promise<void> {
const cleanupProcess = await this.getCleanupProcess();
cleanupProcess.send(<IFileCleanupMessage>{ messageType: CleanupProcessMessage.AddDeleteFileAction, filePath });
}
public async removeCleanupDeleteAction(filePath: string): Promise<void> {
const cleanupProcess = await this.getCleanupProcess();
cleanupProcess.send(<IFileCleanupMessage>{ messageType: CleanupProcessMessage.RemoveDeleteFileAction, filePath });
}
public async addCleanupJS(jsCommand: IJSCommand): Promise<void> {
const cleanupProcess = await this.getCleanupProcess();
cleanupProcess.send(<IJSCleanupMessage>{ messageType: CleanupProcessMessage.AddJSFileToRequire, jsCommand });
}
public async removeCleanupJS(jsCommand: IJSCommand): Promise<void> {
const cleanupProcess = await this.getCleanupProcess();
cleanupProcess.send(<IJSCleanupMessage>{ messageType: CleanupProcessMessage.RemoveJSFileToRequire, jsCommand});
}
public async addKillProcess(pid: string): Promise<void> {
const killSpawnCommandInfo = this.getKillProcesSpawnInfo(pid);
await this.addCleanupCommand(killSpawnCommandInfo);
}
public async removeKillProcess(pid: string): Promise<void> {
const killSpawnCommandInfo = this.getKillProcesSpawnInfo(pid);
await this.removeCleanupCommand(killSpawnCommandInfo);
}
@exported("cleanupService")
public setCleanupLogFile(filePath: string): void {
this.pathToCleanupLogFile = filePath;
}
public dispose(): void {
if (this.cleanupProcess && this.shouldDispose) {
this.cleanupProcess.disconnect();
}
}
public setShouldDispose(shouldDispose: boolean): void {
this.shouldDispose = shouldDispose;
}
// TODO: Consider extracting this method to a separate service
// as it has the same logic as the one used in analytics-service
@cache()
private getCleanupProcess(): Promise<ChildProcess> {
return new Promise<ChildProcess>((resolve, reject) => {
const cleanupProcessArgs = this.getCleanupProcessArgs();
const cleanupProcess = this.$childProcess.spawn(process.execPath,
cleanupProcessArgs,
{
stdio: ["ignore", "ignore", "ignore", "ipc"],
detached: true
}
);
cleanupProcess.unref();
let isSettled = false;
const timeoutId = setTimeout(() => {
if (!isSettled) {
reject(new Error("Unable to start Cleanup process."));
}
}, CleanupService.CLEANUP_PROCESS_START_TIMEOUT);
cleanupProcess.on("error", (err: Error) => {
clearTimeout(timeoutId);
if (!isSettled) {
isSettled = true;
// In case we throw error here, CLI will break its execution.
reject(err);
}
});
cleanupProcess.on("message", (data: any) => {
if (data === DetachedProcessMessages.ProcessReadyToReceive) {
clearTimeout(timeoutId);
if (!isSettled) {
isSettled = true;
this.cleanupProcess = cleanupProcess;
resolve(cleanupProcess);
}
}
});
});
}
private getCleanupProcessArgs(): string[] {
const cleanupProcessArgs = [
path.join(__dirname, "..", "detached-processes", "cleanup-process.js"),
this.$staticConfig.PATH_TO_BOOTSTRAP,
];
if (this.pathToCleanupLogFile) {
cleanupProcessArgs.push(path.resolve(this.pathToCleanupLogFile));
}
return cleanupProcessArgs;
}
private getKillProcesSpawnInfo(pid: string): ISpawnCommandInfo {
let command;
let args;
switch (process.platform) {
case 'win32':
command = "taskkill";
args = ["/pid", pid, "/T", "/F"];
break;
default:
command = path.join(__dirname, '../bash-scripts/terminateProcess.sh');
args = [pid];
break;
}
return {
command,
args
};
}
}
$injector.register("cleanupService", CleanupService);