Repository navigation
Expand file tree
/
Copy pathdaemonSilence.ts
More file actions
77 lines (67 loc) · 3.42 KB
/
Copy pathdaemonSilence.ts
File metadata and controls
77 lines (67 loc) · 3.42 KB
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
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
// Keep this module free of runtime imports: AgentProgressRenderer, which start.ts loads before
// @microsoft/rush-lib, uses it.
import type { IDaemonClientLivenessOptions, IDaemonSilence } from '@rushstack/rush-client-core';
/**
* What a daemon that has not responded means, and what to do, to follow a semicolon. On Linux,
* `rush-client daemon status` waits 5 s for the daemon to get ready and then names its process's state, such as
* stopped (state T) or running (state R), with what to do. Elsewhere it cannot read that state.
*/
export const DAEMON_SILENCE_ADVICE: string =
'its process may be stopped or overloaded; on Linux, "rush-client daemon status" says which. This command goes on if rushd responds; interrupt it (Ctrl+C) to stop waiting';
function formatDaemon(pid: number | undefined): string {
return pid === undefined ? 'rushd' : `rushd (PID ${pid})`;
}
function formatSeconds(ms: number): string {
return `${Math.floor(ms / 1000)}s`;
}
/** Says how long the daemon has sent nothing, for example "rushd (PID 41) has not responded for 30s". */
export function formatDaemonSilence(pid: number | undefined, silentForMs: number): string {
return `${formatDaemon(pid)} has not responded for ${formatSeconds(silentForMs)}`;
}
/** Says that the daemon sent something again, for example "rushd (PID 41) responded again after 70s". */
export function formatDaemonResponded(silence: IDaemonSilence): string {
return `${formatDaemon(silence.pid)} responded again after ${formatSeconds(silence.silentForMs)}`;
}
/** The agent renderer's methods for a daemon that stops responding, and for one that responds again. */
export interface IAgentDaemonSilenceRenderer {
onDaemonUnresponsive(silence: IDaemonSilence): void;
onDaemonResponsive(silence: IDaemonSilence): void;
}
/** Where the lines about a daemon that stopped responding go. */
export interface IDaemonSilenceTarget {
readonly rushx: boolean;
readonly agentRenderer: IAgentDaemonSilenceRenderer | undefined;
readonly writeStderrAsync: (text: string) => Promise<void>;
}
/**
* Creates a request's liveness check. When the daemon stops responding, the agent renderer says so, if one is
* active, and otherwise a line on stderr does, with what to do. When it responds again, a second line says so.
* An interrupt asks the daemon to cancel the request; a stopped daemon cannot confirm that, so the client stops
* waiting when its cancellation timeout ends.
*/
export function createDaemonLivenessOptions(target: IDaemonSilenceTarget): IDaemonClientLivenessOptions {
const { agentRenderer } = target;
const prefix: string = target.rushx ? 'rushx-client' : 'rush-client';
const writeLine = (line: string): void => {
// After SIGHUP the terminal may be gone.
target.writeStderrAsync(`${prefix}: ${line}\n`).catch(() => undefined);
};
return {
onUnresponsive: (silence: IDaemonSilence): void => {
if (agentRenderer) {
agentRenderer.onDaemonUnresponsive(silence);
return;
}
writeLine(`${formatDaemonSilence(silence.pid, silence.silentForMs)}; ${DAEMON_SILENCE_ADVICE}.`);
},
onResponsive: (silence: IDaemonSilence): void => {
if (agentRenderer) {
agentRenderer.onDaemonResponsive(silence);
return;
}
writeLine(`${formatDaemonResponded(silence)}.`);
}
};
}