feat(deploy, ec2, ssh): add timeout parameters for SSH and Git commands, improve AWS credentials logging
This commit is contained in:
+20
-10
@@ -4,7 +4,7 @@ import { createLogger } from "../lib/logger";
|
||||
const log = createLogger("SSH");
|
||||
|
||||
export interface SshSession {
|
||||
exec(command: string): Promise<{ stdout: string; stderr: string; code: number }>;
|
||||
exec(command: string, timeoutMs?: number): Promise<{ stdout: string; stderr: string; code: number }>;
|
||||
// Run a long-lived command (e.g. `tail -f`) and receive its stdout/stderr as
|
||||
// it arrives. Unlike exec(), this never buffers or times out — the channel
|
||||
// stays open until the returned handle's close() is called (or the connection
|
||||
@@ -29,9 +29,9 @@ export async function connectSsh(host: string, privateKey: string, maxWaitMs = 3
|
||||
aborted = true;
|
||||
try { conn.end(); } catch {}
|
||||
},
|
||||
async exec(command: string) {
|
||||
async exec(command: string, timeoutMs?: number) {
|
||||
if (aborted) throw new Error("SSH session aborted");
|
||||
return execOnConn(conn, command);
|
||||
return execOnConn(conn, command, timeoutMs);
|
||||
},
|
||||
execStream(command: string, onData: (chunk: string) => void) {
|
||||
let stream: any = null;
|
||||
@@ -99,22 +99,32 @@ function tryConnect(host: string, privateKey: string, timeoutMs: number): Promis
|
||||
|
||||
const EXEC_TIMEOUT_MS = 30_000;
|
||||
|
||||
function execOnConn(conn: Client, command: string): Promise<{ stdout: string; stderr: string; code: number }> {
|
||||
function execOnConn(conn: Client, command: string, timeoutMs = EXEC_TIMEOUT_MS): Promise<{ stdout: string; stderr: string; code: number }> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => reject(new Error(`SSH exec timed out: ${command.slice(0, 60)}`)), EXEC_TIMEOUT_MS);
|
||||
let stream: any = null;
|
||||
let settled = false;
|
||||
const settle = (fn: () => void) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
clearTimeout(timer);
|
||||
fn();
|
||||
};
|
||||
const timer = setTimeout(() => {
|
||||
settle(() => reject(new Error(`SSH exec timed out after ${Math.round(timeoutMs / 1000)}s: ${command.slice(0, 60)}`)));
|
||||
try { stream?.close(); } catch {}
|
||||
}, timeoutMs);
|
||||
|
||||
conn.exec(command, (err, stream) => {
|
||||
conn.exec(command, (err, s) => {
|
||||
if (err) {
|
||||
clearTimeout(timer);
|
||||
return reject(err);
|
||||
return settle(() => reject(err));
|
||||
}
|
||||
stream = s;
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
stream.on("data", (d: Buffer) => { stdout += d.toString(); });
|
||||
stream.stderr.on("data", (d: Buffer) => { stderr += d.toString(); });
|
||||
stream.on("close", (code: number) => {
|
||||
clearTimeout(timer);
|
||||
resolve({ stdout, stderr, code: code ?? 0 });
|
||||
settle(() => resolve({ stdout, stderr, code: code ?? 0 }));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user