@seekrit/cli 0.23.2 → 0.23.3
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/index.js +86 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
2
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
3
3
|
import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { Command } from "commander";
|
|
@@ -1976,7 +1976,7 @@ function isServiceToken(value) {
|
|
|
1976
1976
|
}
|
|
1977
1977
|
//#endregion
|
|
1978
1978
|
//#region package.json
|
|
1979
|
-
var version = "0.23.
|
|
1979
|
+
var version = "0.23.3";
|
|
1980
1980
|
//#endregion
|
|
1981
1981
|
//#region ../../packages/api-client/src/index.ts
|
|
1982
1982
|
var SeekritApiError = class extends Error {
|
|
@@ -4574,6 +4574,79 @@ async function materializeForRun(options) {
|
|
|
4574
4574
|
};
|
|
4575
4575
|
}
|
|
4576
4576
|
}
|
|
4577
|
+
/**
|
|
4578
|
+
* Every live descendant of `root`, from a single `ps(1)` snapshot of the
|
|
4579
|
+
* process table. We follow the parent chain rather than the process group
|
|
4580
|
+
* because a process that calls setsid(2)/setpgid(2) leaves the group — nodemon
|
|
4581
|
+
* does exactly that for the script it watches — while the parent chain still
|
|
4582
|
+
* leads to it. Returns nothing if `ps` is unavailable; callers fall back to
|
|
4583
|
+
* signalling the immediate child.
|
|
4584
|
+
*/
|
|
4585
|
+
function descendantsOf(root) {
|
|
4586
|
+
const ps = spawnSync("ps", [
|
|
4587
|
+
"-A",
|
|
4588
|
+
"-o",
|
|
4589
|
+
"pid=,ppid=,pgid="
|
|
4590
|
+
], { encoding: "utf8" });
|
|
4591
|
+
if (ps.status !== 0 || typeof ps.stdout !== "string") return [];
|
|
4592
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
4593
|
+
const groupOf = /* @__PURE__ */ new Map();
|
|
4594
|
+
const isId = (value) => value !== void 0 && Number.isInteger(value);
|
|
4595
|
+
for (const line of ps.stdout.split("\n")) {
|
|
4596
|
+
const [pid, ppid, pgid] = line.trim().split(/\s+/, 3).map(Number);
|
|
4597
|
+
if (!isId(pid) || !isId(ppid) || !isId(pgid)) continue;
|
|
4598
|
+
const siblings = childrenOf.get(ppid);
|
|
4599
|
+
if (siblings) siblings.push(pid);
|
|
4600
|
+
else childrenOf.set(ppid, [pid]);
|
|
4601
|
+
groupOf.set(pid, pgid);
|
|
4602
|
+
}
|
|
4603
|
+
const ourGroup = groupOf.get(process.pid);
|
|
4604
|
+
const found = [];
|
|
4605
|
+
const queue = [root];
|
|
4606
|
+
const seen = new Set(queue);
|
|
4607
|
+
for (let pid = queue.shift(); pid !== void 0; pid = queue.shift()) for (const kid of childrenOf.get(pid) ?? []) {
|
|
4608
|
+
if (seen.has(kid)) continue;
|
|
4609
|
+
seen.add(kid);
|
|
4610
|
+
found.push({
|
|
4611
|
+
pid: kid,
|
|
4612
|
+
escaped: ourGroup === void 0 || groupOf.get(kid) !== ourGroup
|
|
4613
|
+
});
|
|
4614
|
+
queue.push(kid);
|
|
4615
|
+
}
|
|
4616
|
+
return found;
|
|
4617
|
+
}
|
|
4618
|
+
/** Whether `pid` still exists — signal 0 tests reachability, delivers nothing. */
|
|
4619
|
+
function isAlive(pid) {
|
|
4620
|
+
try {
|
|
4621
|
+
process.kill(pid, 0);
|
|
4622
|
+
return true;
|
|
4623
|
+
} catch {
|
|
4624
|
+
return false;
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4627
|
+
/** How long a signalled escapee gets to exit on its own before SIGKILL. */
|
|
4628
|
+
const ESCAPEE_GRACE_MS = 2e3;
|
|
4629
|
+
/**
|
|
4630
|
+
* Give already-signalled processes that left our group a moment to finish
|
|
4631
|
+
* shutting down, then SIGKILL the holdouts. We are the last process that knows
|
|
4632
|
+
* their pids: the tty won't hang them up, and once their parent exits they
|
|
4633
|
+
* reparent to init, out of reach of any walk down from our own child — which is
|
|
4634
|
+
* how one survives for days, invisible to `ps -a` for want of a tty. Only
|
|
4635
|
+
* processes that already ignored a termination signal are killed; the command
|
|
4636
|
+
* itself and anything still in our group shut down at their own pace.
|
|
4637
|
+
*/
|
|
4638
|
+
async function reapEscapees(pids) {
|
|
4639
|
+
if (pids.size === 0) return;
|
|
4640
|
+
const deadline = Date.now() + ESCAPEE_GRACE_MS;
|
|
4641
|
+
let live = [...pids].filter(isAlive);
|
|
4642
|
+
while (live.length > 0 && Date.now() < deadline) {
|
|
4643
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
4644
|
+
live = live.filter(isAlive);
|
|
4645
|
+
}
|
|
4646
|
+
for (const pid of live) try {
|
|
4647
|
+
process.kill(pid, "SIGKILL");
|
|
4648
|
+
} catch {}
|
|
4649
|
+
}
|
|
4577
4650
|
program.command("run").description("run a command with decrypted secrets injected (process env > .env > app > group)").passThroughOptions().option("--org <slug>").option("--app <slug>", "application slug (token path infers this)").option("--env <slug>", "environment slug (token path infers this)").option("--with <group=env>", "override one group’s slice for this run", collectKv).option("--env-file <path>", "a .env file to overlay (repeatable; default .env)", collectList).option("--explain", "print where each variable resolved from (to stderr)").argument("<command...>", "command to run (prefix with -- to pass flags)").action(async (commandParts, options) => {
|
|
4578
4651
|
const [cmd, ...args] = commandParts;
|
|
4579
4652
|
if (!cmd) fail("no command given");
|
|
@@ -4588,8 +4661,7 @@ program.command("run").description("run a command with decrypted secrets injecte
|
|
|
4588
4661
|
env: {
|
|
4589
4662
|
...values,
|
|
4590
4663
|
...process.env
|
|
4591
|
-
}
|
|
4592
|
-
detached: posix
|
|
4664
|
+
}
|
|
4593
4665
|
});
|
|
4594
4666
|
const signals = [
|
|
4595
4667
|
"SIGINT",
|
|
@@ -4597,16 +4669,21 @@ program.command("run").description("run a command with decrypted secrets injecte
|
|
|
4597
4669
|
"SIGHUP",
|
|
4598
4670
|
"SIGQUIT"
|
|
4599
4671
|
];
|
|
4672
|
+
const escapees = /* @__PURE__ */ new Set();
|
|
4600
4673
|
const forward = (signal) => {
|
|
4601
4674
|
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
4602
|
-
if (posix && child.pid) try {
|
|
4603
|
-
process.kill(-child.pid, signal);
|
|
4604
|
-
return;
|
|
4605
|
-
} catch {}
|
|
4606
4675
|
child.kill(signal);
|
|
4676
|
+
if (!posix || !child.pid) return;
|
|
4677
|
+
for (const { pid, escaped } of descendantsOf(child.pid)) {
|
|
4678
|
+
if (escaped) escapees.add(pid);
|
|
4679
|
+
try {
|
|
4680
|
+
process.kill(pid, signal);
|
|
4681
|
+
} catch {}
|
|
4682
|
+
}
|
|
4607
4683
|
};
|
|
4608
4684
|
for (const signal of signals) process.on(signal, forward);
|
|
4609
|
-
child.on("exit", (code, signal) => {
|
|
4685
|
+
child.on("exit", async (code, signal) => {
|
|
4686
|
+
await reapEscapees(escapees);
|
|
4610
4687
|
for (const s of signals) process.off(s, forward);
|
|
4611
4688
|
if (signal) process.kill(process.pid, signal);
|
|
4612
4689
|
else process.exit(code ?? 1);
|