refactor: extract duplicate waitForSshSnapshotBoot to shared/ssh.ts (#2783)

The waitForSshOnly function was identically duplicated in hetzner.ts and
digitalocean.ts. Extract the shared logic into waitForSshSnapshotBoot() in
shared/ssh.ts and replace the duplicate cloud implementations with thin
wrappers that resolve module-local state before delegating.

-- qa/code-quality

Co-authored-by: spawn-qa-bot <qa@openrouter.ai>
Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
This commit is contained in:
A 2026-03-18 22:10:25 -07:00 committed by GitHub
parent a023223a58
commit 148cc9e7ee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 20 additions and 20 deletions

View file

@ -28,6 +28,7 @@ import {
waitForSsh as sharedWaitForSsh,
sleep,
spawnInteractive,
waitForSshSnapshotBoot,
} from "../shared/ssh";
import { ensureSshKeys, getSshFingerprint, getSshKeyOpts } from "../shared/ssh-keys";
import {
@ -1196,16 +1197,8 @@ export async function findSpawnSnapshot(agentName: string): Promise<string | nul
// ─── SSH-Only Wait (for snapshot boots) ──────────────────────────────────────
export async function waitForSshOnly(ip?: string): Promise<void> {
const serverIp = ip || _state.serverIp;
const selectedKeys = await ensureSshKeys();
const keyOpts = getSshKeyOpts(selectedKeys);
await sharedWaitForSsh({
host: serverIp,
user: "root",
maxAttempts: 36,
extraSshOpts: keyOpts,
});
logInfo("SSH available (snapshot boot — skipping cloud-init)");
const keyOpts = getSshKeyOpts(await ensureSshKeys());
await waitForSshSnapshotBoot(ip ?? _state.serverIp, keyOpts);
}
// ─── SSH Execution ───────────────────────────────────────────────────────────

View file

@ -18,6 +18,7 @@ import {
waitForSsh as sharedWaitForSsh,
sleep,
spawnInteractive,
waitForSshSnapshotBoot,
} from "../shared/ssh";
import { ensureSshKeys, getSshFingerprint, getSshKeyOpts } from "../shared/ssh-keys";
import {
@ -505,16 +506,8 @@ export async function findSpawnSnapshot(agentName: string): Promise<string | nul
// ─── SSH-Only Wait (for snapshot boots) ──────────────────────────────────────
export async function waitForSshOnly(ip?: string): Promise<void> {
const serverIp = ip || _state.serverIp;
const selectedKeys = await ensureSshKeys();
const keyOpts = getSshKeyOpts(selectedKeys);
await sharedWaitForSsh({
host: serverIp,
user: "root",
maxAttempts: 36,
extraSshOpts: keyOpts,
});
logInfo("SSH available (snapshot boot — skipping cloud-init)");
const keyOpts = getSshKeyOpts(await ensureSshKeys());
await waitForSshSnapshotBoot(ip ?? _state.serverIp, keyOpts);
}
// ─── Provisioning ────────────────────────────────────────────────────────────

View file

@ -362,3 +362,17 @@ export async function waitForSsh(opts: WaitForSshOpts): Promise<void> {
logError(`SSH handshake failed after ${handshakeAttempts} attempts`);
throw new Error("SSH connectivity timeout — handshake never succeeded");
}
/**
* Wait for SSH availability on a snapshot-booted VM (no cloud-init needed).
* Used by cloud modules that support snapshot-based provisioning (Hetzner, DigitalOcean).
*/
export async function waitForSshSnapshotBoot(ip: string, extraSshOpts: string[]): Promise<void> {
await waitForSsh({
host: ip,
user: "root",
maxAttempts: 36,
extraSshOpts,
});
logInfo("SSH available (snapshot boot — skipping cloud-init)");
}