@rpcbase/cli 0.191.0 → 0.193.0

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpcbase/cli",
3
- "version": "0.191.0",
3
+ "version": "0.193.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src"
@@ -1,4 +1,4 @@
1
- import { execSync } from "child_process"
1
+ import { execFileSync, execSync } from "child_process"
2
2
  import fs from "fs"
3
3
  import os from "os"
4
4
  import path from "path"
@@ -246,9 +246,15 @@ export const deploy = async (argv) => {
246
246
  let preparedDeploy = null
247
247
 
248
248
  const ssh = async (command, stdioInherit = false) => {
249
- const sshCommand = `ssh -i "${keyPath}" -o StrictHostKeyChecking=no ${user}@${host} "${command}"`
250
249
  log(`${command}`)
251
- const out = execSync(sshCommand, {
250
+ const out = execFileSync("ssh", [
251
+ "-i",
252
+ keyPath,
253
+ "-o",
254
+ "StrictHostKeyChecking=no",
255
+ `${user}@${host}`,
256
+ command,
257
+ ], {
252
258
  stdio: (argv.verbose || stdioInherit) ? "inherit" : "pipe",
253
259
  })
254
260
  const res = out?.toString()?.trim() || ""
@@ -278,12 +284,50 @@ export const deploy = async (argv) => {
278
284
  await ssh(`cd ~/apps/${baseDeployDir}/infrastructure && RB_DEPLOY_DIR=${shellQuote(baseDeployDir)} node ctrl.js ${action}-instance ${instance} prod`, true)
279
285
  }
280
286
 
287
+ const printInstanceDiagnostics = async (instance) => {
288
+ const composePrefix = [
289
+ `cd ~/apps/${baseDeployDir}/infrastructure &&`,
290
+ `RB_DEPLOY_DIR=${shellQuote(baseDeployDir)}`,
291
+ "docker compose -p sample-app --env-file ../.env -f compose.yml -f compose.instances.yml",
292
+ `--profile instance-${instance}`,
293
+ ].join(" ")
294
+ const services = `server-${instance} worker-${instance}`
295
+
296
+ console.error(`Instance ${instance} diagnostics:`)
297
+ for (const command of [
298
+ `${composePrefix} ps ${services}`,
299
+ `${composePrefix} logs --tail=160 ${services}`,
300
+ ]) {
301
+ try {
302
+ await ssh(command, true)
303
+ } catch (error) {
304
+ console.error(`Failed to run diagnostics command: ${error instanceof Error ? error.message : error}`)
305
+ }
306
+ }
307
+ }
308
+
281
309
  const waitForInstance = async (instance, target) => {
282
310
  const port = Number(target?.port)
283
311
  if (!Number.isFinite(port) || port <= 0) throw new Error(`Cannot healthcheck instance ${instance}: invalid port`)
284
312
 
285
- const url = `http://127.0.0.1:${port}/`
286
- const healthcheckScript = `fetch(${JSON.stringify(url)}).then((res) => process.exit(res.status < 500 ? 0 : 1)).catch(() => process.exit(1))`
313
+ const healthPath = typeof target?.healthPath === "string" && target.healthPath.startsWith("/") ? target.healthPath : "/_healthcheck"
314
+ const url = `http://127.0.0.1:${port}${healthPath}`
315
+ const healthcheckScript = [
316
+ `const url = ${JSON.stringify(url)}`,
317
+ "const controller = new AbortController()",
318
+ "const timeout = setTimeout(() => controller.abort(), 2000)",
319
+ "fetch(url, { signal: controller.signal }).then(async (res) => {",
320
+ " clearTimeout(timeout)",
321
+ " if (res.status < 500) process.exit(0)",
322
+ " const body = await res.text().catch(() => '')",
323
+ " console.error(`healthcheck ${url} failed with status ${res.status}: ${body.slice(0, 500)}`)",
324
+ " process.exit(1)",
325
+ "}).catch((error) => {",
326
+ " clearTimeout(timeout)",
327
+ " console.error(`healthcheck ${url} failed: ${error?.code || error?.name || 'Error'} ${error?.message || error}`)",
328
+ " process.exit(1)",
329
+ "})",
330
+ ].join(";")
287
331
  for (let attempt = 1; attempt <= 60; attempt += 1) {
288
332
  try {
289
333
  await ssh(`node -e ${shellQuote(healthcheckScript)}`)
@@ -294,6 +338,7 @@ export const deploy = async (argv) => {
294
338
  }
295
339
  }
296
340
 
341
+ await printInstanceDiagnostics(instance)
297
342
  throw new Error(`Instance ${instance} did not become healthy at ${url}`)
298
343
  }
299
344
 
package/src/cmd-ssh.js CHANGED
@@ -1,4 +1,4 @@
1
- import { execSync } from "child_process"
1
+ import { execFileSync } from "child_process"
2
2
  import fs from "fs"
3
3
  import path from "path"
4
4
  import os from "os"
@@ -45,9 +45,15 @@ export const ssh = async (argv) => {
45
45
  let tempKeyPath = null
46
46
 
47
47
  const execSsh = async (command, stdioInherit = true) => {
48
- const sshCommand = `ssh -i "${keyPath}" -o StrictHostKeyChecking=no ${user}@${host} "${command}"`
49
48
  log(`${command}`)
50
- const out = execSync(sshCommand, {
49
+ const out = execFileSync("ssh", [
50
+ "-i",
51
+ keyPath,
52
+ "-o",
53
+ "StrictHostKeyChecking=no",
54
+ `${user}@${host}`,
55
+ command,
56
+ ], {
51
57
  stdio: (argv.verbose || stdioInherit) ? "inherit" : "pipe",
52
58
  })
53
59
  const res = out?.toString()?.trim() || ""