@nmakarov/cli-toolkit 0.53.0 → 0.57.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/args.cjs +4 -0
- package/dist/args.cjs.map +1 -1
- package/dist/args.js +4 -0
- package/dist/args.js.map +1 -1
- package/dist/cli-runner.cjs +6 -0
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +6 -0
- package/dist/cli-runner.js.map +1 -1
- package/dist/deploy.cjs +73 -8
- package/dist/deploy.cjs.map +1 -1
- package/dist/deploy.js +71 -8
- package/dist/deploy.js.map +1 -1
- package/dist/index.cjs +77 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +75 -8
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +6 -0
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +6 -0
- package/dist/init.js.map +1 -1
- package/package.json +1 -1
package/dist/deploy.js
CHANGED
|
@@ -54,6 +54,8 @@ function servicePaths(service) {
|
|
|
54
54
|
current: join(root, "current"),
|
|
55
55
|
sharedEnv: join(root, "shared", ".env"),
|
|
56
56
|
ecosystem: join(root, "shared", "ecosystem.config.cjs"),
|
|
57
|
+
/** Preloaded by pm2 `node_args --require` so ENV survives flaky restarts. */
|
|
58
|
+
ensureEnv: join(root, "shared", "ensure-env.cjs"),
|
|
57
59
|
deployLog: join(root, "logs", "deploy.log"),
|
|
58
60
|
lockHashFile: join(root, "shared", ".package-lock.sha256")
|
|
59
61
|
};
|
|
@@ -372,6 +374,16 @@ async function pruneReleases(service, paths, options = {}) {
|
|
|
372
374
|
function sleep(ms) {
|
|
373
375
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
374
376
|
}
|
|
377
|
+
function isPidAlive(pid) {
|
|
378
|
+
const n = Number(pid);
|
|
379
|
+
if (!Number.isFinite(n) || n <= 0) return false;
|
|
380
|
+
try {
|
|
381
|
+
process.kill(n, 0);
|
|
382
|
+
return true;
|
|
383
|
+
} catch {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
375
387
|
async function getPm2Process(appName, options = {}) {
|
|
376
388
|
const { logger } = options;
|
|
377
389
|
const { stdout } = await runCapture("bash", ["-lc", "pm2 jlist"], { logger });
|
|
@@ -408,21 +420,44 @@ async function waitPm2(appName, predicate, options = {}) {
|
|
|
408
420
|
`pm2 wait timed out after ${timeoutMs}ms for ${appName} (${label}); last status=${lastStatus}`
|
|
409
421
|
);
|
|
410
422
|
}
|
|
423
|
+
function isFreshOnline(proc, oldPid) {
|
|
424
|
+
if (proc?.pm2_env?.status !== "online") return false;
|
|
425
|
+
const pid = Number(proc.pid);
|
|
426
|
+
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
427
|
+
if (oldPid != null) {
|
|
428
|
+
if (pid === oldPid) return false;
|
|
429
|
+
if (isPidAlive(oldPid)) return false;
|
|
430
|
+
}
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
411
433
|
async function reloadPm2(paths, options = {}) {
|
|
412
434
|
const { dryRun = false, logger = console, appName = null, waitTimeoutMs = 65e3 } = options;
|
|
413
435
|
if (dryRun) {
|
|
414
436
|
logger.info(`[dryRun] would pm2 startOrReload ${paths.ecosystem} --update-env`);
|
|
415
437
|
return;
|
|
416
438
|
}
|
|
439
|
+
let oldPid = null;
|
|
440
|
+
if (appName) {
|
|
441
|
+
const before = await getPm2Process(appName, { logger });
|
|
442
|
+
const n = Number(before?.pid);
|
|
443
|
+
if (Number.isFinite(n) && n > 0 && before?.pm2_env?.status === "online") {
|
|
444
|
+
oldPid = n;
|
|
445
|
+
logger.info(`pm2 ${appName}: reloading (old pid=${oldPid})`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
417
448
|
await runShell(`pm2 startOrReload "${paths.ecosystem}" --update-env`, { logger });
|
|
418
449
|
logger.info("pm2 reloaded");
|
|
419
450
|
if (appName) {
|
|
420
|
-
await waitPm2(
|
|
451
|
+
const proc = await waitPm2(
|
|
421
452
|
appName,
|
|
422
|
-
(
|
|
423
|
-
{
|
|
453
|
+
(p) => isFreshOnline(p, oldPid),
|
|
454
|
+
{
|
|
455
|
+
timeoutMs: waitTimeoutMs,
|
|
456
|
+
logger,
|
|
457
|
+
label: oldPid ? `online on new pid (old ${oldPid} gone)` : "online after reload"
|
|
458
|
+
}
|
|
424
459
|
);
|
|
425
|
-
logger.info(`pm2 ${appName} online`);
|
|
460
|
+
logger.info(`pm2 ${appName} online pid=${proc?.pid ?? "?"}`);
|
|
426
461
|
}
|
|
427
462
|
}
|
|
428
463
|
async function stopPm2(appName, options = {}) {
|
|
@@ -440,10 +475,15 @@ async function stopPm2(appName, options = {}) {
|
|
|
440
475
|
logger.info(`pm2 ${appName}: already stopped`);
|
|
441
476
|
return;
|
|
442
477
|
}
|
|
478
|
+
const oldPid = Number(before.pid);
|
|
443
479
|
await runShell(`pm2 stop "${appName}"`, { logger });
|
|
444
480
|
await waitPm2(
|
|
445
481
|
appName,
|
|
446
|
-
(proc) =>
|
|
482
|
+
(proc) => {
|
|
483
|
+
if (proc && proc.pm2_env?.status !== "stopped") return false;
|
|
484
|
+
if (Number.isFinite(oldPid) && oldPid > 0 && isPidAlive(oldPid)) return false;
|
|
485
|
+
return true;
|
|
486
|
+
},
|
|
447
487
|
{ timeoutMs: waitTimeoutMs, logger, label: "stopped" }
|
|
448
488
|
);
|
|
449
489
|
logger.info(`pm2 ${appName} stopped`);
|
|
@@ -457,12 +497,12 @@ async function startPm2(paths, options = {}) {
|
|
|
457
497
|
await runShell(`pm2 start "${paths.ecosystem}" --update-env`, { logger });
|
|
458
498
|
logger.info("pm2 started");
|
|
459
499
|
if (appName) {
|
|
460
|
-
await waitPm2(
|
|
500
|
+
const proc = await waitPm2(
|
|
461
501
|
appName,
|
|
462
|
-
(
|
|
502
|
+
(p) => isFreshOnline(p, null),
|
|
463
503
|
{ timeoutMs: waitTimeoutMs, logger, label: "online after start" }
|
|
464
504
|
);
|
|
465
|
-
logger.info(`pm2 ${appName} online`);
|
|
505
|
+
logger.info(`pm2 ${appName} online pid=${proc?.pid ?? "?"}`);
|
|
466
506
|
}
|
|
467
507
|
}
|
|
468
508
|
|
|
@@ -706,12 +746,23 @@ function resolvePm2Args(pm2) {
|
|
|
706
746
|
if (/(?:^|\s)--stopAllowance=/.test(base)) return base;
|
|
707
747
|
return `${base}${base ? " " : ""}--stopAllowance=${Math.floor(stopSec)}`.trim();
|
|
708
748
|
}
|
|
749
|
+
function buildEnsureEnvScript() {
|
|
750
|
+
return `/**
|
|
751
|
+
* 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).
|
|
754
|
+
*/
|
|
755
|
+
if (!process.env.ENV) process.env.ENV = "production";
|
|
756
|
+
if (!process.env.NODE_ENV) process.env.NODE_ENV = "production";
|
|
757
|
+
`;
|
|
758
|
+
}
|
|
709
759
|
function buildEcosystemConfig(service, paths) {
|
|
710
760
|
const { pm2 } = service;
|
|
711
761
|
const outLog = join8(paths.logs, `${pm2.appName}.out.log`);
|
|
712
762
|
const errLog = join8(paths.logs, `${pm2.appName}.err.log`);
|
|
713
763
|
const killTimeoutMs = resolveKillTimeoutMs(pm2);
|
|
714
764
|
const args = resolvePm2Args(pm2);
|
|
765
|
+
const ensureEnv = paths.ensureEnv;
|
|
715
766
|
return `/**
|
|
716
767
|
* pm2 ecosystem for ${service.name} \u2014 written by cli-toolkit deploy from the
|
|
717
768
|
* service manifest (init/deploy). Do not hand-edit; change pm2.* in the
|
|
@@ -724,6 +775,8 @@ module.exports = {
|
|
|
724
775
|
script: "${pm2.script}",
|
|
725
776
|
cwd: "${paths.current}",
|
|
726
777
|
args: ${JSON.stringify(args)},
|
|
778
|
+
// Absolute --require so ENV is set before Args constructs (see shared/ensure-env.cjs).
|
|
779
|
+
node_args: ${JSON.stringify(`--require ${ensureEnv}`)},
|
|
727
780
|
instances: 1,
|
|
728
781
|
exec_mode: "fork",
|
|
729
782
|
autorestart: true,
|
|
@@ -765,9 +818,17 @@ async function initServiceStructure(service, options = {}) {
|
|
|
765
818
|
created.push(dir);
|
|
766
819
|
}
|
|
767
820
|
const ecosystemExisted = await pathExists4(paths.ecosystem);
|
|
821
|
+
const ensureEnvExisted = await pathExists4(paths.ensureEnv);
|
|
768
822
|
if (!dryRun) {
|
|
823
|
+
await writeFile5(paths.ensureEnv, buildEnsureEnvScript(), { mode: 420 });
|
|
769
824
|
await writeFile5(paths.ecosystem, buildEcosystemConfig(service, paths), { mode: 420 });
|
|
770
825
|
}
|
|
826
|
+
if (ensureEnvExisted) {
|
|
827
|
+
logger.info(`synced ${paths.ensureEnv}`);
|
|
828
|
+
} else {
|
|
829
|
+
created.push(paths.ensureEnv);
|
|
830
|
+
logger.info(`seeded ${paths.ensureEnv}`);
|
|
831
|
+
}
|
|
771
832
|
if (ecosystemExisted) {
|
|
772
833
|
logger.info(`synced ${paths.ecosystem} from manifest`);
|
|
773
834
|
} else {
|
|
@@ -1276,6 +1337,8 @@ export {
|
|
|
1276
1337
|
initServiceStructure,
|
|
1277
1338
|
installDeps,
|
|
1278
1339
|
installOperatorShell,
|
|
1340
|
+
isFreshOnline,
|
|
1341
|
+
isPidAlive,
|
|
1279
1342
|
listReleases,
|
|
1280
1343
|
loadServices,
|
|
1281
1344
|
npmEnv,
|