@nmakarov/cli-toolkit 0.57.0 → 0.61.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/dist/deploy.js CHANGED
@@ -430,10 +430,47 @@ function isFreshOnline(proc, oldPid) {
430
430
  }
431
431
  return true;
432
432
  }
433
+ async function deletePm2App(appName, options = {}) {
434
+ const { dryRun = false, logger = console, waitTimeoutMs = 65e3, oldPid = null } = options;
435
+ if (dryRun) {
436
+ logger.info(`[dryRun] would pm2 delete ${appName}`);
437
+ return;
438
+ }
439
+ const before = await getPm2Process(appName, { logger });
440
+ if (!before) {
441
+ logger.info(`pm2 ${appName}: not present (skip delete)`);
442
+ return;
443
+ }
444
+ await runShell(`pm2 delete "${appName}"`, { logger });
445
+ await waitPm2(
446
+ appName,
447
+ (proc) => {
448
+ if (proc) return false;
449
+ if (oldPid != null && isPidAlive(oldPid)) return false;
450
+ return true;
451
+ },
452
+ { timeoutMs: waitTimeoutMs, logger, label: "deleted (process gone)" }
453
+ );
454
+ logger.info(`pm2 ${appName} deleted`);
455
+ }
456
+ async function startFromEcosystem(paths, options = {}) {
457
+ const { dryRun = false, logger = console } = options;
458
+ if (dryRun) {
459
+ logger.info(`[dryRun] would pm2 start ${paths.ecosystem} --update-env`);
460
+ return;
461
+ }
462
+ await runShell(
463
+ `ENV=production NODE_ENV=production pm2 start "${paths.ecosystem}" --update-env`,
464
+ { logger }
465
+ );
466
+ logger.info("pm2 started from ecosystem");
467
+ }
433
468
  async function reloadPm2(paths, options = {}) {
434
469
  const { dryRun = false, logger = console, appName = null, waitTimeoutMs = 65e3 } = options;
435
470
  if (dryRun) {
436
- logger.info(`[dryRun] would pm2 startOrReload ${paths.ecosystem} --update-env`);
471
+ logger.info(
472
+ `[dryRun] would pm2 delete${appName ? ` ${appName}` : ""} + start ${paths.ecosystem} --update-env`
473
+ );
437
474
  return;
438
475
  }
439
476
  let oldPid = null;
@@ -442,11 +479,15 @@ async function reloadPm2(paths, options = {}) {
442
479
  const n = Number(before?.pid);
443
480
  if (Number.isFinite(n) && n > 0 && before?.pm2_env?.status === "online") {
444
481
  oldPid = n;
445
- logger.info(`pm2 ${appName}: reloading (old pid=${oldPid})`);
482
+ logger.info(`pm2 ${appName}: recreate (old pid=${oldPid})`);
446
483
  }
484
+ await deletePm2App(appName, { dryRun, logger, waitTimeoutMs, oldPid });
485
+ } else {
486
+ logger.warn?.(
487
+ "reloadPm2: appName missing; starting ecosystem without delete (pass appName so script/env always recreate cleanly)"
488
+ );
447
489
  }
448
- await runShell(`pm2 startOrReload "${paths.ecosystem}" --update-env`, { logger });
449
- logger.info("pm2 reloaded");
490
+ await startFromEcosystem(paths, { dryRun, logger });
450
491
  if (appName) {
451
492
  const proc = await waitPm2(
452
493
  appName,
@@ -454,7 +495,7 @@ async function reloadPm2(paths, options = {}) {
454
495
  {
455
496
  timeoutMs: waitTimeoutMs,
456
497
  logger,
457
- label: oldPid ? `online on new pid (old ${oldPid} gone)` : "online after reload"
498
+ label: oldPid ? `online on new pid (old ${oldPid} gone)` : "online after recreate"
458
499
  }
459
500
  );
460
501
  logger.info(`pm2 ${appName} online pid=${proc?.pid ?? "?"}`);
@@ -491,11 +532,15 @@ async function stopPm2(appName, options = {}) {
491
532
  async function startPm2(paths, options = {}) {
492
533
  const { dryRun = false, logger = console, appName = null, waitTimeoutMs = 65e3 } = options;
493
534
  if (dryRun) {
494
- logger.info(`[dryRun] would pm2 start ${paths.ecosystem} --update-env`);
535
+ logger.info(
536
+ `[dryRun] would pm2 delete${appName ? ` ${appName}` : ""} + start ${paths.ecosystem} --update-env`
537
+ );
495
538
  return;
496
539
  }
497
- await runShell(`pm2 start "${paths.ecosystem}" --update-env`, { logger });
498
- logger.info("pm2 started");
540
+ if (appName) {
541
+ await deletePm2App(appName, { dryRun, logger, waitTimeoutMs });
542
+ }
543
+ await startFromEcosystem(paths, { dryRun, logger });
499
544
  if (appName) {
500
545
  const proc = await waitPm2(
501
546
  appName,
@@ -749,11 +794,12 @@ function resolvePm2Args(pm2) {
749
794
  function buildEnsureEnvScript() {
750
795
  return `/**
751
796
  * Written by cli-toolkit deploy (init-structure). Do not hand-edit.
752
- * Preloaded via pm2 node_args --require so Args sees ENV=production even when
753
- * a post-SIGKILL restart briefly omits ecosystem env (otherwise Db \u2192 local:6032).
797
+ * Preloaded via pm2 node_args --require. Always set (do not only fill when unset):
798
+ * \`pm2 start \u2026 --update-env\` can inject a non-production ENV from the
799
+ * deploy shell / prior dump, and a conditional assign would leave it in place.
754
800
  */
755
- if (!process.env.ENV) process.env.ENV = "production";
756
- if (!process.env.NODE_ENV) process.env.NODE_ENV = "production";
801
+ process.env.ENV = "production";
802
+ process.env.NODE_ENV = "production";
757
803
  `;
758
804
  }
759
805
  function buildEcosystemConfig(service, paths) {
@@ -1115,7 +1161,9 @@ async function rollbackService(service, options = {}) {
1115
1161
  logger.info(`rollback target: v${buildInfo.version} release=${buildInfo.release ?? rollbackTarget.name}`);
1116
1162
  }
1117
1163
  await activateRelease(rollbackTarget.path, paths, { dryRun, logger });
1118
- await reloadPm2(paths, { dryRun, logger });
1164
+ const appName = service.pm2?.appName ?? null;
1165
+ const waitTimeoutMs = 65e3;
1166
+ await reloadPm2(paths, { dryRun, logger, appName, waitTimeoutMs });
1119
1167
  const summary = `rollback ${activeName} \u2192 ${rollbackTarget.name} dryRun=${dryRun}`;
1120
1168
  if (!dryRun) await appendDeployLog(paths.deployLog, summary);
1121
1169
  return { from: activeName, to: rollbackTarget.name, path: rollbackTarget.path };