@devdogsuga/devtools 0.1.50 → 0.1.51-next.526
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/README.md +81 -7
- package/bin/with-env.mjs +17 -0
- package/dist/app-registry-BoAnenqC.js +38 -0
- package/dist/{cli-t3gWyy3w.js → cli-BEFBVH8e.js} +2286 -718
- package/dist/launch.js +19 -10
- package/dist/load-Cp8AMERM.js +640 -0
- package/dist/load-Czhayrsk.js +2 -0
- package/dist/{menu-DYQ-Dd-O.js → menu-CJC7OElU.js} +473 -178
- package/dist/{failure-log-DOt6E3hS.js → root-CsocfxnK.js} +104 -5
- package/dist/session-CmDxHEMn.js +2 -0
- package/dist/session-rbONwFvY.js +431 -0
- package/dist/{stack-C_50FafG.js → stack-7Lbym6Wq.js} +2 -3
- package/dist/stack-7qA2UP9g.js +2 -0
- package/dist/supabase-project-BoJKMCxL.js +766 -0
- package/dist/{telemetry-C07-ioHm.js → telemetry-BKFDtzXI.js} +8 -9
- package/dist/telemetry.js +1 -1
- package/dist/with-env.js +370 -0
- package/dist/worker-env-CAFmquhW.js +36 -0
- package/dist/wrangler-env-BrlNe4pm.js +34 -0
- package/env.ts +1 -1
- package/package.json +11 -20
- package/dist/local-env-BuHU5Ckd.js +0 -357
- package/dist/local-env-MqH18s-N.js +0 -2
- package/dist/peers-DvUH5VxX.js +0 -354
- package/dist/root-CjPWNkBu.js +0 -99
- package/dist/stack-o1Kml3V0.js +0 -2
- package/dist/supabase-project-C93xRFkN.js +0 -436
|
@@ -1,436 +0,0 @@
|
|
|
1
|
-
import { a as cliName, r as noteRan } from "./failure-log-DOt6E3hS.js";
|
|
2
|
-
import { r as findRepoRoot } from "./root-CjPWNkBu.js";
|
|
3
|
-
import { p as resolveLayout } from "./peers-DvUH5VxX.js";
|
|
4
|
-
import { readFileSync } from "node:fs";
|
|
5
|
-
import { join } from "node:path";
|
|
6
|
-
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
7
|
-
import { promisify } from "node:util";
|
|
8
|
-
//#region ../cli-core/src/dry-run.ts
|
|
9
|
-
const DRY_RUN_FLAG = "--dry-run";
|
|
10
|
-
/** Commands that forward everything after their name to another tool. */
|
|
11
|
-
const FORWARDING = /* @__PURE__ */ new Set([
|
|
12
|
-
"supabase",
|
|
13
|
-
"wrangler",
|
|
14
|
-
"drizzle-kit",
|
|
15
|
-
"psql",
|
|
16
|
-
"bw",
|
|
17
|
-
"run"
|
|
18
|
-
]);
|
|
19
|
-
let active = false;
|
|
20
|
-
function setDryRun(on) {
|
|
21
|
-
active = on;
|
|
22
|
-
}
|
|
23
|
-
function isDryRun() {
|
|
24
|
-
return active;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Decides whether `argv` asks for a dry run and returns the argv to dispatch.
|
|
28
|
-
*
|
|
29
|
-
* For a forwarding command a leading `--dry-run` is consumed (the tool never
|
|
30
|
-
* sees it) and a later one is the tool's. For any other command the argv is
|
|
31
|
-
* returned as it came, because several commands read `--dry-run` themselves.
|
|
32
|
-
*/
|
|
33
|
-
function resolveDryRun(argv) {
|
|
34
|
-
const firstPositional = argv.findIndex((arg) => !arg.startsWith("-"));
|
|
35
|
-
const lead = firstPositional === -1 ? argv.length : firstPositional;
|
|
36
|
-
const command = argv[firstPositional];
|
|
37
|
-
if (command !== void 0 && FORWARDING.has(command)) {
|
|
38
|
-
const rest = argv.filter((arg, i) => i >= lead || arg !== "--dry-run");
|
|
39
|
-
return {
|
|
40
|
-
dryRun: rest.length !== argv.length,
|
|
41
|
-
rest
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
return {
|
|
45
|
-
dryRun: argv.includes(DRY_RUN_FLAG),
|
|
46
|
-
rest: [...argv]
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* How a command treats `--dry-run`, from the nearest node on its path that
|
|
51
|
-
* says. `undefined` means it did not say, and the dispatcher stops it.
|
|
52
|
-
*/
|
|
53
|
-
function dryRunKind(catalog, path) {
|
|
54
|
-
for (let end = path.length; end > 0; end -= 1) {
|
|
55
|
-
const kind = catalog.findCommand(path.slice(0, end))?.dryRun;
|
|
56
|
-
if (kind !== void 0) return kind;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
/** The command line a stopped command would have run. */
|
|
60
|
-
function wouldRunLine(argv) {
|
|
61
|
-
return `Would run: ${cliName()} ${argv.join(" ")}`;
|
|
62
|
-
}
|
|
63
|
-
//#endregion
|
|
64
|
-
//#region ../cli-core/src/process-group.ts
|
|
65
|
-
/**
|
|
66
|
-
* Running a child tool so that stopping it stops everything it started.
|
|
67
|
-
*
|
|
68
|
-
* `pnpm run dev` forks a shell that forks vinext that forks workerd. Killing
|
|
69
|
-
* only the pnpm process (what a plain `child.kill()` does, and what a closed
|
|
70
|
-
* terminal tab or a `kill` from another window delivers) orphans the rest,
|
|
71
|
-
* and the orphan keeps its port. So the child gets its own process group, and
|
|
72
|
-
* every stop signal this process receives is sent to the whole group.
|
|
73
|
-
*
|
|
74
|
-
* Ctrl-C needs the same care in reverse: a child in its own group is not in
|
|
75
|
-
* the terminal's foreground group, so the terminal no longer delivers ^C to
|
|
76
|
-
* it. The signal handlers below forward it.
|
|
77
|
-
*
|
|
78
|
-
* The group is only swept after the direct child exits when that exit was a
|
|
79
|
-
* signal we forwarded; a child that exits on its own is left to have cleaned
|
|
80
|
-
* up after itself, so a tool that deliberately leaves a daemon running keeps it.
|
|
81
|
-
*/
|
|
82
|
-
const FORWARDED = [
|
|
83
|
-
"SIGINT",
|
|
84
|
-
"SIGTERM",
|
|
85
|
-
"SIGHUP"
|
|
86
|
-
];
|
|
87
|
-
const SIGNAL_NUMBERS = {
|
|
88
|
-
SIGHUP: 1,
|
|
89
|
-
SIGINT: 2,
|
|
90
|
-
SIGTERM: 15,
|
|
91
|
-
SIGKILL: 9
|
|
92
|
-
};
|
|
93
|
-
/** How long a group gets to leave after SIGTERM before SIGKILL. */
|
|
94
|
-
const GRACE_MS = 5e3;
|
|
95
|
-
/** Sends `signal` to every process in `pid`'s group. False if it is already gone. */
|
|
96
|
-
function signalGroup(pid, signal) {
|
|
97
|
-
try {
|
|
98
|
-
process.kill(process.platform === "win32" ? pid : -pid, signal);
|
|
99
|
-
return true;
|
|
100
|
-
} catch {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
function runInGroup(command, args, opts = {}) {
|
|
105
|
-
if (isDryRun()) {
|
|
106
|
-
process.stderr.write(`Would run: ${formatCommand(command, args)}\n`);
|
|
107
|
-
return Promise.resolve({
|
|
108
|
-
code: 0,
|
|
109
|
-
signal: null
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
return new Promise((resolve) => {
|
|
113
|
-
const child = spawn(command, [...args], {
|
|
114
|
-
stdio: opts.stdio ?? "inherit",
|
|
115
|
-
cwd: opts.cwd,
|
|
116
|
-
env: opts.env,
|
|
117
|
-
detached: process.platform !== "win32"
|
|
118
|
-
});
|
|
119
|
-
const pid = child.pid;
|
|
120
|
-
let forwarded = null;
|
|
121
|
-
let escalation;
|
|
122
|
-
const forward = (signal) => {
|
|
123
|
-
if (pid === void 0) return;
|
|
124
|
-
forwarded ??= signal;
|
|
125
|
-
signalGroup(pid, signal);
|
|
126
|
-
escalation ??= setTimeout(() => signalGroup(pid, "SIGKILL"), GRACE_MS);
|
|
127
|
-
escalation.unref();
|
|
128
|
-
};
|
|
129
|
-
if (opts.abort?.aborted) forward("SIGTERM");
|
|
130
|
-
else opts.abort?.addEventListener("abort", () => forward("SIGTERM"), { once: true });
|
|
131
|
-
const handlers = FORWARDED.map((signal) => {
|
|
132
|
-
const handler = () => forward(signal);
|
|
133
|
-
process.on(signal, handler);
|
|
134
|
-
return [signal, handler];
|
|
135
|
-
});
|
|
136
|
-
const onExit = () => {
|
|
137
|
-
if (pid !== void 0) signalGroup(pid, "SIGKILL");
|
|
138
|
-
};
|
|
139
|
-
process.on("exit", onExit);
|
|
140
|
-
const finish = (result) => {
|
|
141
|
-
noteRan(`${formatCommand(command, args)} (exit ${result.code})`);
|
|
142
|
-
for (const [signal, handler] of handlers) process.off(signal, handler);
|
|
143
|
-
process.off("exit", onExit);
|
|
144
|
-
if (escalation) clearTimeout(escalation);
|
|
145
|
-
if (forwarded !== null && pid !== void 0) signalGroup(pid, "SIGKILL");
|
|
146
|
-
resolve(result);
|
|
147
|
-
};
|
|
148
|
-
child.on("error", (error) => {
|
|
149
|
-
process.stderr.write(`${error.message}\n`);
|
|
150
|
-
finish({
|
|
151
|
-
code: 1,
|
|
152
|
-
signal: null
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
child.on("exit", (code, signal) => {
|
|
156
|
-
const bySignal = signal ? 128 + (SIGNAL_NUMBERS[signal] ?? 0) : 1;
|
|
157
|
-
finish({
|
|
158
|
-
code: code ?? bySignal,
|
|
159
|
-
signal
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
const URL_CREDENTIALS = /\/\/[^/@\s]*@/;
|
|
165
|
-
/**
|
|
166
|
-
* The command as a person would type it, for printing. A database URL carries
|
|
167
|
-
* its password, so the value after `--db-url` (and any URL with credentials)
|
|
168
|
-
* is replaced rather than echoed.
|
|
169
|
-
*/
|
|
170
|
-
function formatCommand(command, args) {
|
|
171
|
-
const shown = [];
|
|
172
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
173
|
-
const arg = args[i];
|
|
174
|
-
if (arg === "--db-url" && i + 1 < args.length) {
|
|
175
|
-
shown.push(arg, "<DB_URL>");
|
|
176
|
-
i += 1;
|
|
177
|
-
} else if (arg.startsWith("--db-url=")) shown.push("--db-url=<DB_URL>");
|
|
178
|
-
else shown.push(arg.replace(URL_CREDENTIALS, "//***@"));
|
|
179
|
-
}
|
|
180
|
-
return [command, ...shown].map((part) => /^[\w@%+=:,./<>*-]+$/.test(part) ? part : JSON.stringify(part)).join(" ");
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* Prints the exact command that ran, AFTER it ran so the tool's own output
|
|
184
|
-
* cannot bury it. On stderr, so a tool whose stdout is a pipe stays clean.
|
|
185
|
-
*/
|
|
186
|
-
function reportRan(command, args, result) {
|
|
187
|
-
if (isDryRun()) return;
|
|
188
|
-
const status = result.code === 0 ? "" : ` (exit ${result.code})`;
|
|
189
|
-
process.stderr.write(`Ran: ${formatCommand(command, args)}${status}\n`);
|
|
190
|
-
}
|
|
191
|
-
//#endregion
|
|
192
|
-
//#region ../cli-core/src/db/run.ts
|
|
193
|
-
/**
|
|
194
|
-
* Shared helpers for running supabase CLI and pnpm commands.
|
|
195
|
-
*
|
|
196
|
-
* The supabase CLI is a workspace devDependency (not a global install), so
|
|
197
|
-
* every invocation goes through `pnpm exec supabase`. Plain pnpm commands run
|
|
198
|
-
* in `findRepoRoot()`; the supabase ones run in DevDogsUGA's root
|
|
199
|
-
* (`supabaseRoot()`), where `supabase/config.toml` lives and where the CLI is
|
|
200
|
-
* installed. From a Backstage checkout that is `<root>/devdogsuga`, so it needs
|
|
201
|
-
* its own `pnpm install` there before any of these work.
|
|
202
|
-
*/
|
|
203
|
-
const runFile = promisify(execFile);
|
|
204
|
-
/** Where `supabase/` lives, and so the cwd of every supabase CLI call. */
|
|
205
|
-
function supabaseRoot() {
|
|
206
|
-
return resolveLayout().devdogsugaRoot;
|
|
207
|
-
}
|
|
208
|
-
/** Spawn a pnpm command with inherited stdio; resolves to the exit code. Pass
|
|
209
|
-
* `env` to run the child against a loaded tier rather than process.env. The
|
|
210
|
-
* child runs in its own process group, so stopping this process stops
|
|
211
|
-
* whatever it started (see `process-group.ts`). */
|
|
212
|
-
async function run(args, env, cwd = findRepoRoot()) {
|
|
213
|
-
const result = await runInGroup("pnpm", args, {
|
|
214
|
-
cwd,
|
|
215
|
-
...env ? { env } : {}
|
|
216
|
-
});
|
|
217
|
-
if (reporting) reportRan("pnpm", args, result);
|
|
218
|
-
return result.code;
|
|
219
|
-
}
|
|
220
|
-
let reporting = false;
|
|
221
|
-
/**
|
|
222
|
-
* Makes every `run()` after this print the exact command it ran (on stderr,
|
|
223
|
-
* after it finishes). Off by default, so a deploy log or a piped command is
|
|
224
|
-
* not changed under its caller; the presets turn it on because showing the
|
|
225
|
-
* tool calls behind a button is their whole point.
|
|
226
|
-
*/
|
|
227
|
-
function reportRuns(enabled) {
|
|
228
|
-
reporting = enabled;
|
|
229
|
-
}
|
|
230
|
-
/** Spawn a pnpm command while echoing and retaining stderr for diagnostics.
|
|
231
|
-
* Pass `env` to run the child against a loaded tier rather than process.env. */
|
|
232
|
-
function runWithStderr(args, env, cwd = findRepoRoot()) {
|
|
233
|
-
return new Promise((resolve) => {
|
|
234
|
-
const child = spawn("pnpm", args, {
|
|
235
|
-
stdio: [
|
|
236
|
-
"inherit",
|
|
237
|
-
"inherit",
|
|
238
|
-
"pipe"
|
|
239
|
-
],
|
|
240
|
-
cwd,
|
|
241
|
-
...env ? { env } : {}
|
|
242
|
-
});
|
|
243
|
-
let stderr = "";
|
|
244
|
-
child.stderr.setEncoding("utf8");
|
|
245
|
-
child.stderr.on("data", (chunk) => {
|
|
246
|
-
stderr += chunk;
|
|
247
|
-
process.stderr.write(chunk);
|
|
248
|
-
});
|
|
249
|
-
child.on("error", (error) => {
|
|
250
|
-
const message = `${error.message}\n`;
|
|
251
|
-
stderr += message;
|
|
252
|
-
process.stderr.write(message);
|
|
253
|
-
resolve({
|
|
254
|
-
code: 1,
|
|
255
|
-
stderr
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
child.on("exit", (code) => resolve({
|
|
259
|
-
code: code ?? 1,
|
|
260
|
-
stderr
|
|
261
|
-
}));
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
/** Spawn a pnpm command, capture stdout, throw on non-zero exit. */
|
|
265
|
-
async function capture(args, cwd = findRepoRoot()) {
|
|
266
|
-
const { stdout } = await runFile("pnpm", args, {
|
|
267
|
-
cwd,
|
|
268
|
-
encoding: "utf8"
|
|
269
|
-
});
|
|
270
|
-
return stdout;
|
|
271
|
-
}
|
|
272
|
-
/** `pnpm exec supabase …` with inherited stdio. */
|
|
273
|
-
const supabase = (...args) => run([
|
|
274
|
-
"exec",
|
|
275
|
-
"supabase",
|
|
276
|
-
...args
|
|
277
|
-
], void 0, supabaseRoot());
|
|
278
|
-
/** `pnpm exec supabase …` with captured stdout. */
|
|
279
|
-
const supabaseCapture = (...args) => capture([
|
|
280
|
-
"exec",
|
|
281
|
-
"supabase",
|
|
282
|
-
...args
|
|
283
|
-
], supabaseRoot());
|
|
284
|
-
/**
|
|
285
|
-
* `supabase db push` over `dbUrl`, inherited stdio, resolving to the exit code.
|
|
286
|
-
*
|
|
287
|
-
* The one spelling of the migration push, shared by the contributor `db
|
|
288
|
-
* migrate` path (`stack.ts`'s `pushMigrations`) and CI's `deploy migrate`.
|
|
289
|
-
* `--yes` is the unattended apply CI wants; the contributor path omits it so a
|
|
290
|
-
* human still confirms. `includeSeed` adds the `[db.seed]` files, which the
|
|
291
|
-
* CLI runs once each and records in `supabase_migrations.seed_files`. Neither variant regenerates types — that is
|
|
292
|
-
* `pushMigrations`' own second step, layered on top only where a checkout is
|
|
293
|
-
* meant to be rewritten. A CI apply must NOT write back into the repo, which is
|
|
294
|
-
* exactly why the bare push is factored out here rather than reused whole.
|
|
295
|
-
*/
|
|
296
|
-
function dbPush(dbUrl, opts = {}) {
|
|
297
|
-
const args = [
|
|
298
|
-
"db",
|
|
299
|
-
"push",
|
|
300
|
-
"--db-url",
|
|
301
|
-
dbUrl
|
|
302
|
-
];
|
|
303
|
-
if (opts.includeSeed) args.push("--include-seed");
|
|
304
|
-
if (opts.yes) args.push("--yes");
|
|
305
|
-
return supabase(...args);
|
|
306
|
-
}
|
|
307
|
-
/** Seed storage buckets on the local stack or a resolved tier's project. */
|
|
308
|
-
async function seedBuckets(conn) {
|
|
309
|
-
if (conn.kind === "local") return supabase("seed", "buckets", "--local", "--yes");
|
|
310
|
-
if (!conn.projectRef) {
|
|
311
|
-
process.stderr.write("devtools db: seed buckets against a hosted project needs PROJECT_REF in the session's env file.\n");
|
|
312
|
-
return 1;
|
|
313
|
-
}
|
|
314
|
-
return supabase("seed", "buckets", "--project-ref", conn.projectRef, "--linked");
|
|
315
|
-
}
|
|
316
|
-
//#endregion
|
|
317
|
-
//#region ../cli-core/src/repo/supabase-project.ts
|
|
318
|
-
/**
|
|
319
|
-
* This repo's Supabase project id — read from `supabase/config.toml` — and
|
|
320
|
-
* the container-name conventions the Supabase CLI derives from it.
|
|
321
|
-
*
|
|
322
|
-
* Shared by `environment.ts`'s adaptive-menu probe (does a stack answer for
|
|
323
|
-
* THIS project?) and the foreign-stack detection in `db/generated-env.ts`
|
|
324
|
-
* and `stack.ts` (does a DIFFERENT project's stack hold the shared ports? —
|
|
325
|
-
* the case where `supabase status -o env` fails not because nothing is
|
|
326
|
-
* running, but because what is running on 127.0.0.1:54321 belongs to
|
|
327
|
-
* another workspace, or to this same repo before/after a `project_id`
|
|
328
|
-
* rename). One project-id reader and one container-name parser, so both
|
|
329
|
-
* questions agree on what "this project" means.
|
|
330
|
-
*/
|
|
331
|
-
/**
|
|
332
|
-
* Reads `project_id` out of `<repoRoot>/supabase/config.toml`, or `null`
|
|
333
|
-
* when the file is missing or unparsable.
|
|
334
|
-
*
|
|
335
|
-
* Regex, not a TOML parser: it is one line, the shape has been stable
|
|
336
|
-
* across every CLI version this repo has seen, and a parser on a path some
|
|
337
|
-
* callers pay before their first real subprocess is a cost that stays
|
|
338
|
-
* invisible until it is why the tool feels slow.
|
|
339
|
-
*/
|
|
340
|
-
function readProjectId(repoRoot) {
|
|
341
|
-
try {
|
|
342
|
-
const path = join(repoRoot, "supabase", "config.toml");
|
|
343
|
-
return /^\s*project_id\s*=\s*"([^"]+)"/m.exec(readFileSync(path, "utf8"))?.[1] ?? null;
|
|
344
|
-
} catch {
|
|
345
|
-
return null;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
/**
|
|
349
|
-
* The Supabase CLI's container name prefix for a project id, or the bare
|
|
350
|
-
* `supabase_db_` fallback when the id could not be read.
|
|
351
|
-
*
|
|
352
|
-
* Falling back to the bare prefix means a machine whose `config.toml`
|
|
353
|
-
* cannot be read still detects *a* stack, at the cost of over-matching a
|
|
354
|
-
* differently-named project — deliberate, and unchanged from this
|
|
355
|
-
* function's original home in `environment.ts`: over-matching offers
|
|
356
|
-
* `stop`/a foreign-stack hint for a stack that turns out to be this
|
|
357
|
-
* project's own, which is harmless to see and decline, while
|
|
358
|
-
* under-matching hides a real signal entirely.
|
|
359
|
-
*/
|
|
360
|
-
function containerPrefix(projectId) {
|
|
361
|
-
return projectId ? `supabase_db_${projectId}` : "supabase_db_";
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* The local stack's API port, published by its Kong container. Mirrors
|
|
365
|
-
* `@devdogsuga/env/load`'s `LOCAL_STACK_PORT`, which this module cannot import
|
|
366
|
-
* statically (that package is an optional peer, loaded on demand).
|
|
367
|
-
*/
|
|
368
|
-
const STACK_API_PORT = 54321;
|
|
369
|
-
function defaultExec(file, args) {
|
|
370
|
-
try {
|
|
371
|
-
return execFileSync(file, args, {
|
|
372
|
-
encoding: "utf8",
|
|
373
|
-
stdio: [
|
|
374
|
-
"ignore",
|
|
375
|
-
"pipe",
|
|
376
|
-
"ignore"
|
|
377
|
-
],
|
|
378
|
-
timeout: 3e3
|
|
379
|
-
});
|
|
380
|
-
} catch {
|
|
381
|
-
return null;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
/**
|
|
385
|
-
* `docker ps --format '{{.Names}}'`, split into trimmed, non-empty names —
|
|
386
|
-
* or `null` when Docker could not be read at all (no daemon, not
|
|
387
|
-
* installed, timed out). With `publish`, only the containers publishing that
|
|
388
|
-
* host port. `execute` is injectable for tests, mirroring `environment.ts`'s
|
|
389
|
-
* own probes.
|
|
390
|
-
*/
|
|
391
|
-
function listContainerNames(publish, execute = defaultExec) {
|
|
392
|
-
const out = execute("docker", [
|
|
393
|
-
"ps",
|
|
394
|
-
...publish === void 0 ? [] : ["--filter", `publish=${publish}`],
|
|
395
|
-
"--format",
|
|
396
|
-
"{{.Names}}"
|
|
397
|
-
]);
|
|
398
|
-
if (out === null) return null;
|
|
399
|
-
return out.split("\n").map((name) => name.trim()).filter((name) => name.length > 0);
|
|
400
|
-
}
|
|
401
|
-
/**
|
|
402
|
-
* Finds the `supabase_kong_<id>` container among `names` whose id differs
|
|
403
|
-
* from `projectId` — the signature of a stack started from a DIFFERENT
|
|
404
|
-
* Supabase project (this same repo before/after a rename, or another
|
|
405
|
-
* project entirely) holding the shared ports. Kong is the container that
|
|
406
|
-
* publishes the API port, so callers pass the names `listContainerNames`
|
|
407
|
-
* returns for `STACK_API_PORT`: that names the stack actually on 54321,
|
|
408
|
-
* not just any Supabase stack on the machine.
|
|
409
|
-
*
|
|
410
|
-
* Returns that foreign id, or `null` when every matching container belongs
|
|
411
|
-
* to this project, none match at all, or `projectId` itself could not be
|
|
412
|
-
* read — with no known id of our own, there is nothing to compare against,
|
|
413
|
-
* and guessing would risk telling a contributor their OWN stack is
|
|
414
|
-
* foreign.
|
|
415
|
-
*/
|
|
416
|
-
function foreignStackProjectId(names, projectId) {
|
|
417
|
-
if (projectId === null) return null;
|
|
418
|
-
for (const name of names) {
|
|
419
|
-
const match = /^supabase_kong_(.+)$/.exec(name);
|
|
420
|
-
if (!match) continue;
|
|
421
|
-
const id = match[1];
|
|
422
|
-
if (id !== projectId) return id;
|
|
423
|
-
}
|
|
424
|
-
return null;
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* The actionable message for a detected foreign stack — shared by
|
|
428
|
-
* `db/generated-env.ts` (a failed `supabase status -o env`) and `stack.ts`
|
|
429
|
-
* (a failed `supabase start`), so both name the same fix in the same
|
|
430
|
-
* words.
|
|
431
|
-
*/
|
|
432
|
-
function foreignStackMessage(foreignProjectId) {
|
|
433
|
-
return `The stack on port ${STACK_API_PORT} belongs to project "${foreignProjectId}", not this checkout's. Stop it with \`supabase stop --project-id ${foreignProjectId}\` then \`pnpm devtools supabase start\`.`;
|
|
434
|
-
}
|
|
435
|
-
//#endregion
|
|
436
|
-
export { wouldRunLine as S, runInGroup as _, listContainerNames as a, resolveDryRun as b, reportRuns as c, seedBuckets as d, supabase as f, reportRan as g, formatCommand as h, foreignStackProjectId as i, run as l, supabaseRoot as m, containerPrefix as n, readProjectId as o, supabaseCapture as p, foreignStackMessage as r, dbPush as s, STACK_API_PORT as t, runWithStderr as u, dryRunKind as v, setDryRun as x, isDryRun as y };
|