@push.rocks/smartshell 3.2.2 → 3.2.4
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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.shelllog.d.ts +1 -1
- package/dist_ts/classes.smartshell.d.ts +10 -4
- package/dist_ts/classes.smartshell.js +66 -55
- package/package.json +5 -6
- package/readme.md +732 -69
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.smartshell.ts +83 -86
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/classes.smartshell.ts
CHANGED
|
@@ -2,7 +2,6 @@ import * as plugins from './plugins.js';
|
|
|
2
2
|
import { ShellEnv } from './classes.shellenv.js';
|
|
3
3
|
import type { IShellEnvContructorOptions, TExecutor } from './classes.shellenv.js';
|
|
4
4
|
import { ShellLog } from './classes.shelllog.js';
|
|
5
|
-
|
|
6
5
|
import * as cp from 'child_process';
|
|
7
6
|
|
|
8
7
|
// -- interfaces --
|
|
@@ -17,7 +16,15 @@ export interface IExecResultStreaming {
|
|
|
17
16
|
kill: () => Promise<void>;
|
|
18
17
|
terminate: () => Promise<void>;
|
|
19
18
|
keyboardInterrupt: () => Promise<void>;
|
|
20
|
-
customSignal: (
|
|
19
|
+
customSignal: (signal: plugins.smartexit.TProcessSignal) => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface IExecOptions {
|
|
23
|
+
commandString: string;
|
|
24
|
+
silent?: boolean;
|
|
25
|
+
strict?: boolean;
|
|
26
|
+
streaming?: boolean;
|
|
27
|
+
interactive?: boolean;
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
export class Smartshell {
|
|
@@ -29,61 +36,48 @@ export class Smartshell {
|
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
/**
|
|
32
|
-
*
|
|
39
|
+
* Executes a given command asynchronously.
|
|
33
40
|
*/
|
|
34
|
-
private async _exec(options: {
|
|
35
|
-
commandString: string;
|
|
36
|
-
silent?: boolean;
|
|
37
|
-
strict?: boolean;
|
|
38
|
-
streaming?: boolean;
|
|
39
|
-
interactive?: boolean;
|
|
40
|
-
}): Promise<IExecResult | IExecResultStreaming | void> {
|
|
41
|
+
private async _exec(options: IExecOptions): Promise<IExecResult | IExecResultStreaming | void> {
|
|
41
42
|
if (options.interactive) {
|
|
42
|
-
return await this._execInteractive(options);
|
|
43
|
+
return await this._execInteractive({ commandString: options.commandString });
|
|
43
44
|
}
|
|
44
|
-
|
|
45
45
|
return await this._execCommand(options);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Executes an interactive command.
|
|
50
|
+
*/
|
|
51
|
+
private async _execInteractive(options: Pick<IExecOptions, 'commandString'>): Promise<void> {
|
|
52
|
+
// Skip interactive execution in CI environments.
|
|
52
53
|
if (process.env.CI) {
|
|
53
54
|
return;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
});
|
|
57
|
+
return new Promise<void>((resolve) => {
|
|
58
|
+
const shell = cp.spawn(options.commandString, {
|
|
59
|
+
stdio: 'inherit',
|
|
60
|
+
shell: true,
|
|
61
|
+
detached: true,
|
|
62
|
+
});
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
this.smartexit.addProcess(shell);
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
shell.on('close', (code) => {
|
|
67
|
+
console.log(`Interactive shell terminated with code ${code}`);
|
|
68
|
+
this.smartexit.removeProcess(shell);
|
|
69
|
+
resolve();
|
|
70
|
+
});
|
|
70
71
|
});
|
|
71
|
-
|
|
72
|
-
await done.promise;
|
|
73
72
|
}
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
streaming?: boolean;
|
|
80
|
-
}): Promise<IExecResult | IExecResultStreaming> {
|
|
81
|
-
const done = plugins.smartpromise.defer<IExecResult | IExecResultStreaming>();
|
|
82
|
-
const childProcessEnded = plugins.smartpromise.defer<IExecResult>();
|
|
83
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Executes a command and returns either a non-streaming result or a streaming interface.
|
|
76
|
+
*/
|
|
77
|
+
private async _execCommand(options: IExecOptions): Promise<IExecResult | IExecResultStreaming> {
|
|
84
78
|
const commandToExecute = this.shellEnv.createEnvExecString(options.commandString);
|
|
85
|
-
|
|
86
79
|
const shellLogInstance = new ShellLog();
|
|
80
|
+
|
|
87
81
|
const execChildProcess = cp.spawn(commandToExecute, [], {
|
|
88
82
|
shell: true,
|
|
89
83
|
cwd: process.cwd(),
|
|
@@ -93,6 +87,7 @@ export class Smartshell {
|
|
|
93
87
|
|
|
94
88
|
this.smartexit.addProcess(execChildProcess);
|
|
95
89
|
|
|
90
|
+
// Capture stdout and stderr output.
|
|
96
91
|
execChildProcess.stdout.on('data', (data) => {
|
|
97
92
|
if (!options.silent) {
|
|
98
93
|
shellLogInstance.writeToConsole(data);
|
|
@@ -107,47 +102,55 @@ export class Smartshell {
|
|
|
107
102
|
shellLogInstance.addToBuffer(data);
|
|
108
103
|
});
|
|
109
104
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
105
|
+
// Wrap child process termination into a Promise.
|
|
106
|
+
const childProcessEnded: Promise<IExecResult> = new Promise((resolve, reject) => {
|
|
107
|
+
execChildProcess.on('exit', (code, signal) => {
|
|
108
|
+
this.smartexit.removeProcess(execChildProcess);
|
|
109
|
+
|
|
110
|
+
const execResult: IExecResult = {
|
|
111
|
+
exitCode: typeof code === 'number' ? code : (signal ? 1 : 0),
|
|
112
|
+
stdout: shellLogInstance.logStore.toString(),
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
if (options.strict && code !== 0) {
|
|
116
|
+
reject(new Error(`Command "${options.commandString}" exited with code ${code}`));
|
|
117
|
+
} else {
|
|
118
|
+
resolve(execResult);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
120
121
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
execChildProcess.on('error', (error) => {
|
|
123
|
+
this.smartexit.removeProcess(execChildProcess);
|
|
124
|
+
reject(error);
|
|
125
|
+
});
|
|
125
126
|
});
|
|
126
127
|
|
|
128
|
+
// If streaming mode is enabled, return a streaming interface immediately.
|
|
127
129
|
if (options.streaming) {
|
|
128
|
-
|
|
130
|
+
return {
|
|
129
131
|
childProcess: execChildProcess,
|
|
130
|
-
finalPromise: childProcessEnded
|
|
132
|
+
finalPromise: childProcessEnded,
|
|
131
133
|
kill: async () => {
|
|
132
|
-
console.log(`
|
|
134
|
+
console.log(`Running tree kill with SIGKILL on process ${execChildProcess.pid}`);
|
|
133
135
|
await plugins.smartexit.SmartExit.killTreeByPid(execChildProcess.pid, 'SIGKILL');
|
|
134
136
|
},
|
|
135
137
|
terminate: async () => {
|
|
136
|
-
console.log(`
|
|
138
|
+
console.log(`Running tree kill with SIGTERM on process ${execChildProcess.pid}`);
|
|
137
139
|
await plugins.smartexit.SmartExit.killTreeByPid(execChildProcess.pid, 'SIGTERM');
|
|
138
140
|
},
|
|
139
141
|
keyboardInterrupt: async () => {
|
|
140
|
-
console.log(`
|
|
142
|
+
console.log(`Running tree kill with SIGINT on process ${execChildProcess.pid}`);
|
|
141
143
|
await plugins.smartexit.SmartExit.killTreeByPid(execChildProcess.pid, 'SIGINT');
|
|
142
144
|
},
|
|
143
|
-
customSignal: async (
|
|
144
|
-
console.log(`
|
|
145
|
-
await plugins.smartexit.SmartExit.killTreeByPid(execChildProcess.pid,
|
|
145
|
+
customSignal: async (signal: plugins.smartexit.TProcessSignal) => {
|
|
146
|
+
console.log(`Running tree kill with custom signal ${signal} on process ${execChildProcess.pid}`);
|
|
147
|
+
await plugins.smartexit.SmartExit.killTreeByPid(execChildProcess.pid, signal);
|
|
146
148
|
},
|
|
147
|
-
}
|
|
149
|
+
} as IExecResultStreaming;
|
|
148
150
|
}
|
|
149
151
|
|
|
150
|
-
|
|
152
|
+
// For non-streaming mode, wait for the process to complete.
|
|
153
|
+
return await childProcessEnded;
|
|
151
154
|
}
|
|
152
155
|
|
|
153
156
|
public async exec(commandString: string): Promise<IExecResult> {
|
|
@@ -166,41 +169,35 @@ export class Smartshell {
|
|
|
166
169
|
return (await this._exec({ commandString, silent: true, strict: true })) as IExecResult;
|
|
167
170
|
}
|
|
168
171
|
|
|
169
|
-
public async execStreaming(
|
|
170
|
-
commandString: string,
|
|
171
|
-
silent: boolean = false
|
|
172
|
-
): Promise<IExecResultStreaming> {
|
|
172
|
+
public async execStreaming(commandString: string, silent: boolean = false): Promise<IExecResultStreaming> {
|
|
173
173
|
return (await this._exec({ commandString, silent, streaming: true })) as IExecResultStreaming;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
public async execStreamingSilent(commandString: string): Promise<IExecResultStreaming> {
|
|
177
|
-
return (await this._exec({
|
|
178
|
-
commandString,
|
|
179
|
-
silent: true,
|
|
180
|
-
streaming: true,
|
|
181
|
-
})) as IExecResultStreaming;
|
|
177
|
+
return (await this._exec({ commandString, silent: true, streaming: true })) as IExecResultStreaming;
|
|
182
178
|
}
|
|
183
179
|
|
|
184
|
-
public async execInteractive(commandString: string) {
|
|
180
|
+
public async execInteractive(commandString: string): Promise<void> {
|
|
185
181
|
await this._exec({ commandString, interactive: true });
|
|
186
182
|
}
|
|
187
183
|
|
|
188
184
|
public async execAndWaitForLine(
|
|
189
185
|
commandString: string,
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
186
|
+
regex: RegExp,
|
|
187
|
+
silent: boolean = false
|
|
188
|
+
): Promise<void> {
|
|
189
|
+
const execStreamingResult = await this.execStreaming(commandString, silent);
|
|
190
|
+
return new Promise<void>((resolve) => {
|
|
191
|
+
execStreamingResult.childProcess.stdout.on('data', (chunk: Buffer | string) => {
|
|
192
|
+
const data = typeof chunk === 'string' ? chunk : chunk.toString();
|
|
193
|
+
if (regex.test(data)) {
|
|
194
|
+
resolve();
|
|
195
|
+
}
|
|
196
|
+
});
|
|
199
197
|
});
|
|
200
|
-
return done.promise;
|
|
201
198
|
}
|
|
202
199
|
|
|
203
|
-
public async execAndWaitForLineSilent(commandString: string,
|
|
204
|
-
return this.execAndWaitForLine(commandString,
|
|
200
|
+
public async execAndWaitForLineSilent(commandString: string, regex: RegExp): Promise<void> {
|
|
201
|
+
return this.execAndWaitForLine(commandString, regex, true);
|
|
205
202
|
}
|
|
206
203
|
}
|