feat(deploy, ec2, ssh): add timeout parameters for SSH and Git commands, improve AWS credentials logging
Deploy / Build (push) Successful in 35s
Deploy / Build and Push Docker Image (push) Successful in 1m10s

This commit is contained in:
2026-07-26 15:14:16 +02:00
parent accbd0d2c6
commit d86b35f231
3 changed files with 34 additions and 22 deletions
+20 -10
View File
@@ -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 }));
});
});
});