@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.cjs
CHANGED
|
@@ -38,6 +38,8 @@ __export(deploy_exports, {
|
|
|
38
38
|
initServiceStructure: () => initServiceStructure,
|
|
39
39
|
installDeps: () => installDeps,
|
|
40
40
|
installOperatorShell: () => installOperatorShell,
|
|
41
|
+
isFreshOnline: () => isFreshOnline,
|
|
42
|
+
isPidAlive: () => isPidAlive,
|
|
41
43
|
listReleases: () => listReleases,
|
|
42
44
|
loadServices: () => loadServices,
|
|
43
45
|
npmEnv: () => npmEnv,
|
|
@@ -129,6 +131,8 @@ function servicePaths(service) {
|
|
|
129
131
|
current: (0, import_node_path.join)(root, "current"),
|
|
130
132
|
sharedEnv: (0, import_node_path.join)(root, "shared", ".env"),
|
|
131
133
|
ecosystem: (0, import_node_path.join)(root, "shared", "ecosystem.config.cjs"),
|
|
134
|
+
/** Preloaded by pm2 `node_args --require` so ENV survives flaky restarts. */
|
|
135
|
+
ensureEnv: (0, import_node_path.join)(root, "shared", "ensure-env.cjs"),
|
|
132
136
|
deployLog: (0, import_node_path.join)(root, "logs", "deploy.log"),
|
|
133
137
|
lockHashFile: (0, import_node_path.join)(root, "shared", ".package-lock.sha256")
|
|
134
138
|
};
|
|
@@ -447,6 +451,16 @@ async function pruneReleases(service, paths, options = {}) {
|
|
|
447
451
|
function sleep(ms) {
|
|
448
452
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
449
453
|
}
|
|
454
|
+
function isPidAlive(pid) {
|
|
455
|
+
const n = Number(pid);
|
|
456
|
+
if (!Number.isFinite(n) || n <= 0) return false;
|
|
457
|
+
try {
|
|
458
|
+
process.kill(n, 0);
|
|
459
|
+
return true;
|
|
460
|
+
} catch {
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
450
464
|
async function getPm2Process(appName, options = {}) {
|
|
451
465
|
const { logger } = options;
|
|
452
466
|
const { stdout } = await runCapture("bash", ["-lc", "pm2 jlist"], { logger });
|
|
@@ -483,21 +497,44 @@ async function waitPm2(appName, predicate, options = {}) {
|
|
|
483
497
|
`pm2 wait timed out after ${timeoutMs}ms for ${appName} (${label}); last status=${lastStatus}`
|
|
484
498
|
);
|
|
485
499
|
}
|
|
500
|
+
function isFreshOnline(proc, oldPid) {
|
|
501
|
+
if (proc?.pm2_env?.status !== "online") return false;
|
|
502
|
+
const pid = Number(proc.pid);
|
|
503
|
+
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
504
|
+
if (oldPid != null) {
|
|
505
|
+
if (pid === oldPid) return false;
|
|
506
|
+
if (isPidAlive(oldPid)) return false;
|
|
507
|
+
}
|
|
508
|
+
return true;
|
|
509
|
+
}
|
|
486
510
|
async function reloadPm2(paths, options = {}) {
|
|
487
511
|
const { dryRun = false, logger = console, appName = null, waitTimeoutMs = 65e3 } = options;
|
|
488
512
|
if (dryRun) {
|
|
489
513
|
logger.info(`[dryRun] would pm2 startOrReload ${paths.ecosystem} --update-env`);
|
|
490
514
|
return;
|
|
491
515
|
}
|
|
516
|
+
let oldPid = null;
|
|
517
|
+
if (appName) {
|
|
518
|
+
const before = await getPm2Process(appName, { logger });
|
|
519
|
+
const n = Number(before?.pid);
|
|
520
|
+
if (Number.isFinite(n) && n > 0 && before?.pm2_env?.status === "online") {
|
|
521
|
+
oldPid = n;
|
|
522
|
+
logger.info(`pm2 ${appName}: reloading (old pid=${oldPid})`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
492
525
|
await runShell(`pm2 startOrReload "${paths.ecosystem}" --update-env`, { logger });
|
|
493
526
|
logger.info("pm2 reloaded");
|
|
494
527
|
if (appName) {
|
|
495
|
-
await waitPm2(
|
|
528
|
+
const proc = await waitPm2(
|
|
496
529
|
appName,
|
|
497
|
-
(
|
|
498
|
-
{
|
|
530
|
+
(p) => isFreshOnline(p, oldPid),
|
|
531
|
+
{
|
|
532
|
+
timeoutMs: waitTimeoutMs,
|
|
533
|
+
logger,
|
|
534
|
+
label: oldPid ? `online on new pid (old ${oldPid} gone)` : "online after reload"
|
|
535
|
+
}
|
|
499
536
|
);
|
|
500
|
-
logger.info(`pm2 ${appName} online`);
|
|
537
|
+
logger.info(`pm2 ${appName} online pid=${proc?.pid ?? "?"}`);
|
|
501
538
|
}
|
|
502
539
|
}
|
|
503
540
|
async function stopPm2(appName, options = {}) {
|
|
@@ -515,10 +552,15 @@ async function stopPm2(appName, options = {}) {
|
|
|
515
552
|
logger.info(`pm2 ${appName}: already stopped`);
|
|
516
553
|
return;
|
|
517
554
|
}
|
|
555
|
+
const oldPid = Number(before.pid);
|
|
518
556
|
await runShell(`pm2 stop "${appName}"`, { logger });
|
|
519
557
|
await waitPm2(
|
|
520
558
|
appName,
|
|
521
|
-
(proc) =>
|
|
559
|
+
(proc) => {
|
|
560
|
+
if (proc && proc.pm2_env?.status !== "stopped") return false;
|
|
561
|
+
if (Number.isFinite(oldPid) && oldPid > 0 && isPidAlive(oldPid)) return false;
|
|
562
|
+
return true;
|
|
563
|
+
},
|
|
522
564
|
{ timeoutMs: waitTimeoutMs, logger, label: "stopped" }
|
|
523
565
|
);
|
|
524
566
|
logger.info(`pm2 ${appName} stopped`);
|
|
@@ -532,12 +574,12 @@ async function startPm2(paths, options = {}) {
|
|
|
532
574
|
await runShell(`pm2 start "${paths.ecosystem}" --update-env`, { logger });
|
|
533
575
|
logger.info("pm2 started");
|
|
534
576
|
if (appName) {
|
|
535
|
-
await waitPm2(
|
|
577
|
+
const proc = await waitPm2(
|
|
536
578
|
appName,
|
|
537
|
-
(
|
|
579
|
+
(p) => isFreshOnline(p, null),
|
|
538
580
|
{ timeoutMs: waitTimeoutMs, logger, label: "online after start" }
|
|
539
581
|
);
|
|
540
|
-
logger.info(`pm2 ${appName} online`);
|
|
582
|
+
logger.info(`pm2 ${appName} online pid=${proc?.pid ?? "?"}`);
|
|
541
583
|
}
|
|
542
584
|
}
|
|
543
585
|
|
|
@@ -781,12 +823,23 @@ function resolvePm2Args(pm2) {
|
|
|
781
823
|
if (/(?:^|\s)--stopAllowance=/.test(base)) return base;
|
|
782
824
|
return `${base}${base ? " " : ""}--stopAllowance=${Math.floor(stopSec)}`.trim();
|
|
783
825
|
}
|
|
826
|
+
function buildEnsureEnvScript() {
|
|
827
|
+
return `/**
|
|
828
|
+
* Written by cli-toolkit deploy (init-structure). Do not hand-edit.
|
|
829
|
+
* Preloaded via pm2 node_args --require so Args sees ENV=production even when
|
|
830
|
+
* a post-SIGKILL restart briefly omits ecosystem env (otherwise Db \u2192 local:6032).
|
|
831
|
+
*/
|
|
832
|
+
if (!process.env.ENV) process.env.ENV = "production";
|
|
833
|
+
if (!process.env.NODE_ENV) process.env.NODE_ENV = "production";
|
|
834
|
+
`;
|
|
835
|
+
}
|
|
784
836
|
function buildEcosystemConfig(service, paths) {
|
|
785
837
|
const { pm2 } = service;
|
|
786
838
|
const outLog = (0, import_node_path8.join)(paths.logs, `${pm2.appName}.out.log`);
|
|
787
839
|
const errLog = (0, import_node_path8.join)(paths.logs, `${pm2.appName}.err.log`);
|
|
788
840
|
const killTimeoutMs = resolveKillTimeoutMs(pm2);
|
|
789
841
|
const args = resolvePm2Args(pm2);
|
|
842
|
+
const ensureEnv = paths.ensureEnv;
|
|
790
843
|
return `/**
|
|
791
844
|
* pm2 ecosystem for ${service.name} \u2014 written by cli-toolkit deploy from the
|
|
792
845
|
* service manifest (init/deploy). Do not hand-edit; change pm2.* in the
|
|
@@ -799,6 +852,8 @@ module.exports = {
|
|
|
799
852
|
script: "${pm2.script}",
|
|
800
853
|
cwd: "${paths.current}",
|
|
801
854
|
args: ${JSON.stringify(args)},
|
|
855
|
+
// Absolute --require so ENV is set before Args constructs (see shared/ensure-env.cjs).
|
|
856
|
+
node_args: ${JSON.stringify(`--require ${ensureEnv}`)},
|
|
802
857
|
instances: 1,
|
|
803
858
|
exec_mode: "fork",
|
|
804
859
|
autorestart: true,
|
|
@@ -840,9 +895,17 @@ async function initServiceStructure(service, options = {}) {
|
|
|
840
895
|
created.push(dir);
|
|
841
896
|
}
|
|
842
897
|
const ecosystemExisted = await pathExists4(paths.ecosystem);
|
|
898
|
+
const ensureEnvExisted = await pathExists4(paths.ensureEnv);
|
|
843
899
|
if (!dryRun) {
|
|
900
|
+
await (0, import_promises11.writeFile)(paths.ensureEnv, buildEnsureEnvScript(), { mode: 420 });
|
|
844
901
|
await (0, import_promises11.writeFile)(paths.ecosystem, buildEcosystemConfig(service, paths), { mode: 420 });
|
|
845
902
|
}
|
|
903
|
+
if (ensureEnvExisted) {
|
|
904
|
+
logger.info(`synced ${paths.ensureEnv}`);
|
|
905
|
+
} else {
|
|
906
|
+
created.push(paths.ensureEnv);
|
|
907
|
+
logger.info(`seeded ${paths.ensureEnv}`);
|
|
908
|
+
}
|
|
846
909
|
if (ecosystemExisted) {
|
|
847
910
|
logger.info(`synced ${paths.ecosystem} from manifest`);
|
|
848
911
|
} else {
|
|
@@ -1352,6 +1415,8 @@ function resolveServiceFrom(serviceMap, name, { appsRoot } = {}) {
|
|
|
1352
1415
|
initServiceStructure,
|
|
1353
1416
|
installDeps,
|
|
1354
1417
|
installOperatorShell,
|
|
1418
|
+
isFreshOnline,
|
|
1419
|
+
isPidAlive,
|
|
1355
1420
|
listReleases,
|
|
1356
1421
|
loadServices,
|
|
1357
1422
|
npmEnv,
|