@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/index.js
CHANGED
|
@@ -1081,6 +1081,10 @@ var Args = class _Args {
|
|
|
1081
1081
|
this.parseArgs(args);
|
|
1082
1082
|
this.env = this.get("env")?.toLowerCase() || "local";
|
|
1083
1083
|
this.loadDotEnv();
|
|
1084
|
+
const envAfterDotEnv = this.get("env")?.toLowerCase();
|
|
1085
|
+
if (envAfterDotEnv) {
|
|
1086
|
+
this.env = envAfterDotEnv;
|
|
1087
|
+
}
|
|
1084
1088
|
this.loadConfigFiles();
|
|
1085
1089
|
this.checkConflicts();
|
|
1086
1090
|
if (context && typeof context.registerCleanup === "function") {
|
|
@@ -5269,6 +5273,8 @@ function servicePaths(service) {
|
|
|
5269
5273
|
current: join2(root, "current"),
|
|
5270
5274
|
sharedEnv: join2(root, "shared", ".env"),
|
|
5271
5275
|
ecosystem: join2(root, "shared", "ecosystem.config.cjs"),
|
|
5276
|
+
/** Preloaded by pm2 `node_args --require` so ENV survives flaky restarts. */
|
|
5277
|
+
ensureEnv: join2(root, "shared", "ensure-env.cjs"),
|
|
5272
5278
|
deployLog: join2(root, "logs", "deploy.log"),
|
|
5273
5279
|
lockHashFile: join2(root, "shared", ".package-lock.sha256")
|
|
5274
5280
|
};
|
|
@@ -5587,6 +5593,16 @@ async function pruneReleases(service, paths, options = {}) {
|
|
|
5587
5593
|
function sleep(ms) {
|
|
5588
5594
|
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
5589
5595
|
}
|
|
5596
|
+
function isPidAlive(pid) {
|
|
5597
|
+
const n = Number(pid);
|
|
5598
|
+
if (!Number.isFinite(n) || n <= 0) return false;
|
|
5599
|
+
try {
|
|
5600
|
+
process.kill(n, 0);
|
|
5601
|
+
return true;
|
|
5602
|
+
} catch {
|
|
5603
|
+
return false;
|
|
5604
|
+
}
|
|
5605
|
+
}
|
|
5590
5606
|
async function getPm2Process(appName, options = {}) {
|
|
5591
5607
|
const { logger } = options;
|
|
5592
5608
|
const { stdout } = await runCapture("bash", ["-lc", "pm2 jlist"], { logger });
|
|
@@ -5623,21 +5639,44 @@ async function waitPm2(appName, predicate, options = {}) {
|
|
|
5623
5639
|
`pm2 wait timed out after ${timeoutMs}ms for ${appName} (${label}); last status=${lastStatus}`
|
|
5624
5640
|
);
|
|
5625
5641
|
}
|
|
5642
|
+
function isFreshOnline(proc, oldPid) {
|
|
5643
|
+
if (proc?.pm2_env?.status !== "online") return false;
|
|
5644
|
+
const pid = Number(proc.pid);
|
|
5645
|
+
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
5646
|
+
if (oldPid != null) {
|
|
5647
|
+
if (pid === oldPid) return false;
|
|
5648
|
+
if (isPidAlive(oldPid)) return false;
|
|
5649
|
+
}
|
|
5650
|
+
return true;
|
|
5651
|
+
}
|
|
5626
5652
|
async function reloadPm2(paths, options = {}) {
|
|
5627
5653
|
const { dryRun = false, logger = console, appName = null, waitTimeoutMs = 65e3 } = options;
|
|
5628
5654
|
if (dryRun) {
|
|
5629
5655
|
logger.info(`[dryRun] would pm2 startOrReload ${paths.ecosystem} --update-env`);
|
|
5630
5656
|
return;
|
|
5631
5657
|
}
|
|
5658
|
+
let oldPid = null;
|
|
5659
|
+
if (appName) {
|
|
5660
|
+
const before = await getPm2Process(appName, { logger });
|
|
5661
|
+
const n = Number(before?.pid);
|
|
5662
|
+
if (Number.isFinite(n) && n > 0 && before?.pm2_env?.status === "online") {
|
|
5663
|
+
oldPid = n;
|
|
5664
|
+
logger.info(`pm2 ${appName}: reloading (old pid=${oldPid})`);
|
|
5665
|
+
}
|
|
5666
|
+
}
|
|
5632
5667
|
await runShell(`pm2 startOrReload "${paths.ecosystem}" --update-env`, { logger });
|
|
5633
5668
|
logger.info("pm2 reloaded");
|
|
5634
5669
|
if (appName) {
|
|
5635
|
-
await waitPm2(
|
|
5670
|
+
const proc = await waitPm2(
|
|
5636
5671
|
appName,
|
|
5637
|
-
(
|
|
5638
|
-
{
|
|
5672
|
+
(p) => isFreshOnline(p, oldPid),
|
|
5673
|
+
{
|
|
5674
|
+
timeoutMs: waitTimeoutMs,
|
|
5675
|
+
logger,
|
|
5676
|
+
label: oldPid ? `online on new pid (old ${oldPid} gone)` : "online after reload"
|
|
5677
|
+
}
|
|
5639
5678
|
);
|
|
5640
|
-
logger.info(`pm2 ${appName} online`);
|
|
5679
|
+
logger.info(`pm2 ${appName} online pid=${proc?.pid ?? "?"}`);
|
|
5641
5680
|
}
|
|
5642
5681
|
}
|
|
5643
5682
|
async function stopPm2(appName, options = {}) {
|
|
@@ -5655,10 +5694,15 @@ async function stopPm2(appName, options = {}) {
|
|
|
5655
5694
|
logger.info(`pm2 ${appName}: already stopped`);
|
|
5656
5695
|
return;
|
|
5657
5696
|
}
|
|
5697
|
+
const oldPid = Number(before.pid);
|
|
5658
5698
|
await runShell(`pm2 stop "${appName}"`, { logger });
|
|
5659
5699
|
await waitPm2(
|
|
5660
5700
|
appName,
|
|
5661
|
-
(proc) =>
|
|
5701
|
+
(proc) => {
|
|
5702
|
+
if (proc && proc.pm2_env?.status !== "stopped") return false;
|
|
5703
|
+
if (Number.isFinite(oldPid) && oldPid > 0 && isPidAlive(oldPid)) return false;
|
|
5704
|
+
return true;
|
|
5705
|
+
},
|
|
5662
5706
|
{ timeoutMs: waitTimeoutMs, logger, label: "stopped" }
|
|
5663
5707
|
);
|
|
5664
5708
|
logger.info(`pm2 ${appName} stopped`);
|
|
@@ -5672,12 +5716,12 @@ async function startPm2(paths, options = {}) {
|
|
|
5672
5716
|
await runShell(`pm2 start "${paths.ecosystem}" --update-env`, { logger });
|
|
5673
5717
|
logger.info("pm2 started");
|
|
5674
5718
|
if (appName) {
|
|
5675
|
-
await waitPm2(
|
|
5719
|
+
const proc = await waitPm2(
|
|
5676
5720
|
appName,
|
|
5677
|
-
(
|
|
5721
|
+
(p) => isFreshOnline(p, null),
|
|
5678
5722
|
{ timeoutMs: waitTimeoutMs, logger, label: "online after start" }
|
|
5679
5723
|
);
|
|
5680
|
-
logger.info(`pm2 ${appName} online`);
|
|
5724
|
+
logger.info(`pm2 ${appName} online pid=${proc?.pid ?? "?"}`);
|
|
5681
5725
|
}
|
|
5682
5726
|
}
|
|
5683
5727
|
|
|
@@ -5921,12 +5965,23 @@ function resolvePm2Args(pm2) {
|
|
|
5921
5965
|
if (/(?:^|\s)--stopAllowance=/.test(base)) return base;
|
|
5922
5966
|
return `${base}${base ? " " : ""}--stopAllowance=${Math.floor(stopSec)}`.trim();
|
|
5923
5967
|
}
|
|
5968
|
+
function buildEnsureEnvScript() {
|
|
5969
|
+
return `/**
|
|
5970
|
+
* Written by cli-toolkit deploy (init-structure). Do not hand-edit.
|
|
5971
|
+
* Preloaded via pm2 node_args --require so Args sees ENV=production even when
|
|
5972
|
+
* a post-SIGKILL restart briefly omits ecosystem env (otherwise Db \u2192 local:6032).
|
|
5973
|
+
*/
|
|
5974
|
+
if (!process.env.ENV) process.env.ENV = "production";
|
|
5975
|
+
if (!process.env.NODE_ENV) process.env.NODE_ENV = "production";
|
|
5976
|
+
`;
|
|
5977
|
+
}
|
|
5924
5978
|
function buildEcosystemConfig(service, paths) {
|
|
5925
5979
|
const { pm2 } = service;
|
|
5926
5980
|
const outLog = join9(paths.logs, `${pm2.appName}.out.log`);
|
|
5927
5981
|
const errLog = join9(paths.logs, `${pm2.appName}.err.log`);
|
|
5928
5982
|
const killTimeoutMs = resolveKillTimeoutMs(pm2);
|
|
5929
5983
|
const args = resolvePm2Args(pm2);
|
|
5984
|
+
const ensureEnv = paths.ensureEnv;
|
|
5930
5985
|
return `/**
|
|
5931
5986
|
* pm2 ecosystem for ${service.name} \u2014 written by cli-toolkit deploy from the
|
|
5932
5987
|
* service manifest (init/deploy). Do not hand-edit; change pm2.* in the
|
|
@@ -5939,6 +5994,8 @@ module.exports = {
|
|
|
5939
5994
|
script: "${pm2.script}",
|
|
5940
5995
|
cwd: "${paths.current}",
|
|
5941
5996
|
args: ${JSON.stringify(args)},
|
|
5997
|
+
// Absolute --require so ENV is set before Args constructs (see shared/ensure-env.cjs).
|
|
5998
|
+
node_args: ${JSON.stringify(`--require ${ensureEnv}`)},
|
|
5942
5999
|
instances: 1,
|
|
5943
6000
|
exec_mode: "fork",
|
|
5944
6001
|
autorestart: true,
|
|
@@ -5980,9 +6037,17 @@ async function initServiceStructure(service, options = {}) {
|
|
|
5980
6037
|
created.push(dir);
|
|
5981
6038
|
}
|
|
5982
6039
|
const ecosystemExisted = await pathExists4(paths.ecosystem);
|
|
6040
|
+
const ensureEnvExisted = await pathExists4(paths.ensureEnv);
|
|
5983
6041
|
if (!dryRun) {
|
|
6042
|
+
await writeFile5(paths.ensureEnv, buildEnsureEnvScript(), { mode: 420 });
|
|
5984
6043
|
await writeFile5(paths.ecosystem, buildEcosystemConfig(service, paths), { mode: 420 });
|
|
5985
6044
|
}
|
|
6045
|
+
if (ensureEnvExisted) {
|
|
6046
|
+
logger.info(`synced ${paths.ensureEnv}`);
|
|
6047
|
+
} else {
|
|
6048
|
+
created.push(paths.ensureEnv);
|
|
6049
|
+
logger.info(`seeded ${paths.ensureEnv}`);
|
|
6050
|
+
}
|
|
5986
6051
|
if (ecosystemExisted) {
|
|
5987
6052
|
logger.info(`synced ${paths.ecosystem} from manifest`);
|
|
5988
6053
|
} else {
|
|
@@ -9206,6 +9271,8 @@ export {
|
|
|
9206
9271
|
installDeps,
|
|
9207
9272
|
installOperatorShell,
|
|
9208
9273
|
ipcFileLogsTableNameForSourceResource,
|
|
9274
|
+
isFreshOnline,
|
|
9275
|
+
isPidAlive,
|
|
9209
9276
|
joiEdateType,
|
|
9210
9277
|
joiStringArrayType,
|
|
9211
9278
|
listServicesRegistry as listAliveRunnerHeartbeats,
|