@marcusrbrown/infra 0.12.0 → 0.12.1
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/package.json
CHANGED
|
@@ -167,6 +167,30 @@ describe('logs command', () => {
|
|
|
167
167
|
})
|
|
168
168
|
})
|
|
169
169
|
|
|
170
|
+
// ─── SSH command includes ConnectTimeout ─────────────────────────────────────
|
|
171
|
+
|
|
172
|
+
describe('streamDashboardLogs — SSH command includes ConnectTimeout', () => {
|
|
173
|
+
it('passes -o ConnectTimeout=10 to ssh', async () => {
|
|
174
|
+
delete process.env.CI
|
|
175
|
+
|
|
176
|
+
let capturedCmd: string[] = []
|
|
177
|
+
const spawnCapture: LogsSpawnFn = (cmd, _opts) => {
|
|
178
|
+
capturedCmd = cmd
|
|
179
|
+
return {exited: Promise.resolve(0)}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
await streamDashboardLogs(
|
|
183
|
+
{host: 'dashboard.fro.bot', service: 'dashboard', tail: 100, allowCi: false},
|
|
184
|
+
spawnCapture,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
const connectTimeoutIdx = capturedCmd.findIndex(arg => arg.startsWith('ConnectTimeout='))
|
|
188
|
+
expect(connectTimeoutIdx).toBeGreaterThan(-1)
|
|
189
|
+
expect(capturedCmd[connectTimeoutIdx - 1]).toBe('-o')
|
|
190
|
+
expect(capturedCmd[connectTimeoutIdx]).toBe('ConnectTimeout=10')
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
170
194
|
// ─── SSH command includes repo-pinned UserKnownHostsFile ─────────────────────
|
|
171
195
|
|
|
172
196
|
describe('streamDashboardLogs — SSH command includes UserKnownHostsFile', () => {
|
|
@@ -133,6 +133,40 @@ function makeSpawn(stdout: string, stderr = '', exitCode = 0): SpawnFn {
|
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
// ─── SSH command includes ConnectTimeout ─────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
describe('getDashboardComposeStatus — SSH command includes ConnectTimeout', () => {
|
|
139
|
+
it('passes -o ConnectTimeout=10 to ssh', async () => {
|
|
140
|
+
let capturedCmd: string[] = []
|
|
141
|
+
|
|
142
|
+
const capturingSpawn: SpawnFn = (cmd, _opts) => {
|
|
143
|
+
capturedCmd = cmd
|
|
144
|
+
const enc = new TextEncoder()
|
|
145
|
+
return {
|
|
146
|
+
stdout: new ReadableStream({
|
|
147
|
+
start(c) {
|
|
148
|
+
c.enqueue(enc.encode(JSON.stringify([{Name: 'dashboard', State: 'running', Health: 'healthy'}])))
|
|
149
|
+
c.close()
|
|
150
|
+
},
|
|
151
|
+
}),
|
|
152
|
+
stderr: new ReadableStream({
|
|
153
|
+
start(c) {
|
|
154
|
+
c.close()
|
|
155
|
+
},
|
|
156
|
+
}),
|
|
157
|
+
exited: Promise.resolve(0),
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
await getDashboardComposeStatus('dashboard.fro.bot', capturingSpawn)
|
|
162
|
+
|
|
163
|
+
const connectTimeoutIdx = capturedCmd.findIndex(arg => arg.startsWith('ConnectTimeout='))
|
|
164
|
+
expect(connectTimeoutIdx).toBeGreaterThan(-1)
|
|
165
|
+
expect(capturedCmd[connectTimeoutIdx - 1]).toBe('-o')
|
|
166
|
+
expect(capturedCmd[connectTimeoutIdx]).toBe('ConnectTimeout=10')
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
|
|
136
170
|
// ─── SSH command includes repo-pinned UserKnownHostsFile ─────────────────────
|
|
137
171
|
|
|
138
172
|
describe('getDashboardComposeStatus — SSH command includes UserKnownHostsFile', () => {
|
|
@@ -362,6 +396,28 @@ describe('status command', () => {
|
|
|
362
396
|
expect(captured.exit?.code).toBe(1)
|
|
363
397
|
})
|
|
364
398
|
|
|
399
|
+
it('prints the SSH error message (not a generic no-services message) when SSH fails', async () => {
|
|
400
|
+
process.env.DASHBOARD_DOMAIN = 'dashboard.fro.bot'
|
|
401
|
+
|
|
402
|
+
const {ctx, captured} = createCapturedCtx()
|
|
403
|
+
|
|
404
|
+
try {
|
|
405
|
+
await dashboardStatusAction(
|
|
406
|
+
{},
|
|
407
|
+
ctx,
|
|
408
|
+
makeSpawn('', 'ssh: connect to host dashboard.fro.bot port 22: Connection timed out', 255),
|
|
409
|
+
)
|
|
410
|
+
} catch (error) {
|
|
411
|
+
if (!(error instanceof MockProcessExit)) throw error
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const stderrText = captured.stderr.join('')
|
|
415
|
+
// Must surface the SSH failure detail, not the generic no-services fallback
|
|
416
|
+
expect(stderrText).toContain('SSH command failed')
|
|
417
|
+
expect(stderrText).not.toContain('No services reported by docker compose ps')
|
|
418
|
+
expect(captured.exit?.code).toBe(1)
|
|
419
|
+
})
|
|
420
|
+
|
|
365
421
|
it('calls ctx.console.error and exits when DASHBOARD_DOMAIN is not set', async () => {
|
|
366
422
|
delete process.env.DASHBOARD_DOMAIN
|
|
367
423
|
|
|
@@ -109,6 +109,8 @@ export async function getDashboardComposeStatus(
|
|
|
109
109
|
'-o',
|
|
110
110
|
'BatchMode=yes',
|
|
111
111
|
'-o',
|
|
112
|
+
'ConnectTimeout=10',
|
|
113
|
+
'-o',
|
|
112
114
|
'StrictHostKeyChecking=yes',
|
|
113
115
|
...knownHostsArgs,
|
|
114
116
|
...identityArgs,
|
|
@@ -250,7 +252,8 @@ export async function dashboardStatusAction(
|
|
|
250
252
|
}
|
|
251
253
|
|
|
252
254
|
if (result.services.length === 0) {
|
|
253
|
-
|
|
255
|
+
const msg = result.error ?? 'No services reported by docker compose ps'
|
|
256
|
+
ctx.console.error(`Error: ${msg}`)
|
|
254
257
|
ctx.process.exit(1)
|
|
255
258
|
return
|
|
256
259
|
}
|