@devdogsuga/devtools 0.1.51-next.541 → 0.1.51-next.545
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 +6 -3
- package/dist/{cli-BoTmTNqH.js → cli-DNZ-6WEI.js} +105 -61
- package/dist/{root-CsocfxnK.js → failure-log-MSdCgvLc.js} +5 -75
- package/dist/launch.js +28 -11
- package/dist/{load-Cp8AMERM.js → load-Z2CXCpyx.js} +7 -4
- package/dist/load-yn7JCcS2.js +2 -0
- package/dist/{menu-C30rQm5v.js → menu-oQE5IqAI.js} +168 -146
- package/dist/root-DfH38DFq.js +2 -0
- package/dist/root-PA8K5G2d.js +77 -0
- package/dist/{supabase-project-BoJKMCxL.js → run-CRBte7p0.js} +235 -249
- package/dist/{session-rbONwFvY.js → session-CuexI1f9.js} +1 -1
- package/dist/{session-CmDxHEMn.js → session-DzgtIg6K.js} +1 -1
- package/dist/spawn-BvLQzc_K.js +100 -0
- package/dist/{stack-7Lbym6Wq.js → stack-BRevXgI3.js} +1 -1
- package/dist/stack-Cl_B_dt7.js +2 -0
- package/dist/{telemetry-BKFDtzXI.js → telemetry-BDTAGquh.js} +19 -4
- package/dist/telemetry.js +2 -2
- package/dist/with-env.js +15 -17
- package/package.json +6 -4
- package/dist/load-Czhayrsk.js +0 -2
- package/dist/stack-7qA2UP9g.js +0 -2
|
@@ -1,10 +1,234 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as execSync, r as spawnProcess, t as exec } from "./spawn-BvLQzc_K.js";
|
|
2
|
+
import { r as findRepoRoot, t as RepoNotFoundError } from "./root-PA8K5G2d.js";
|
|
3
|
+
import { c as cliName, r as noteRan } from "./failure-log-MSdCgvLc.js";
|
|
2
4
|
import { createRequire } from "node:module";
|
|
3
5
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
4
6
|
import { dirname, join } from "node:path";
|
|
5
|
-
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
6
|
-
import { promisify } from "node:util";
|
|
7
7
|
import { pathToFileURL } from "node:url";
|
|
8
|
+
/** The directory (relative to a Backstage root) holding the DevDogsUGA checkout. */
|
|
9
|
+
const DEVDOGSUGA_LINK = "devdogsuga";
|
|
10
|
+
var DevdogsugaMissingError = class extends Error {
|
|
11
|
+
constructor(path) {
|
|
12
|
+
super(`The DevDogsUGA checkout is missing at ${path}. Run \`pnpm devdogsuga\` (links the sibling clone, or \$DEVDOGSUGA_DIR) or \`pnpm install\` in the Backstage root first.`);
|
|
13
|
+
this.name = "DevdogsugaMissingError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
/** The kind of repo `dir` is the root of, by its `package.json` name; null if neither. */
|
|
17
|
+
function repoKindOf(dir) {
|
|
18
|
+
try {
|
|
19
|
+
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
20
|
+
if (pkg.name === "devdogs-monorepo") return "devdogsuga";
|
|
21
|
+
if (pkg.name === "backstage") return "backstage";
|
|
22
|
+
} catch {}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function isDir(path) {
|
|
26
|
+
try {
|
|
27
|
+
return statSync(path).isDirectory();
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The layout of the repo rooted at `root`. Pure: nothing is memoized, so tests
|
|
34
|
+
* can build fixtures freely. An unrecognised package name is treated as
|
|
35
|
+
* DevDogsUGA (the contract fixtures and every pre-Backstage caller).
|
|
36
|
+
*/
|
|
37
|
+
function layoutAt(root) {
|
|
38
|
+
const kind = repoKindOf(root) === "backstage" ? "backstage" : "devdogsuga";
|
|
39
|
+
const sibling = join(root, DEVDOGSUGA_LINK);
|
|
40
|
+
const hasDevdogsuga = kind === "devdogsuga" || isDir(sibling);
|
|
41
|
+
const devdogsugaRoot = kind === "devdogsuga" ? root : sibling;
|
|
42
|
+
const appsDirs = [join(root, "apps")];
|
|
43
|
+
if (kind === "backstage" && hasDevdogsuga) appsDirs.push(join(devdogsugaRoot, "apps"));
|
|
44
|
+
return {
|
|
45
|
+
kind,
|
|
46
|
+
root,
|
|
47
|
+
get devdogsugaRoot() {
|
|
48
|
+
if (!hasDevdogsuga) throw new DevdogsugaMissingError(sibling);
|
|
49
|
+
return devdogsugaRoot;
|
|
50
|
+
},
|
|
51
|
+
...kind === "backstage" ? { backstageRoot: root } : {},
|
|
52
|
+
hasDevdogsuga,
|
|
53
|
+
appsDirs: appsDirs.filter(isDir),
|
|
54
|
+
envMirrors: kind === "backstage" && hasDevdogsuga ? [devdogsugaRoot] : []
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** `layout.devdogsugaRoot`, or null instead of a throw when the checkout is missing. */
|
|
58
|
+
function devdogsugaRootOrNull(layout) {
|
|
59
|
+
return layout.hasDevdogsuga ? layout.devdogsugaRoot : null;
|
|
60
|
+
}
|
|
61
|
+
let cached;
|
|
62
|
+
let cachedFor;
|
|
63
|
+
/** The layout of the repo the process is in (`findRepoRoot()`), memoized per root. */
|
|
64
|
+
function resolveLayout() {
|
|
65
|
+
const root = findRepoRoot();
|
|
66
|
+
if (cached === void 0 || cachedFor !== root) {
|
|
67
|
+
cached = layoutAt(root);
|
|
68
|
+
cachedFor = root;
|
|
69
|
+
}
|
|
70
|
+
return cached;
|
|
71
|
+
}
|
|
72
|
+
/** Every directory under `appsDirs` (one app each), de-duplicated by name, sorted by name. */
|
|
73
|
+
function listApps(layout = resolveLayout()) {
|
|
74
|
+
const seen = /* @__PURE__ */ new Map();
|
|
75
|
+
for (const parent of layout.appsDirs) for (const entry of readdirSync(parent, { withFileTypes: true })) {
|
|
76
|
+
if (!entry.isDirectory()) continue;
|
|
77
|
+
if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
|
|
78
|
+
if (!seen.has(entry.name)) seen.set(entry.name, {
|
|
79
|
+
name: entry.name,
|
|
80
|
+
dir: join(parent, entry.name)
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
84
|
+
}
|
|
85
|
+
/** The directory of the app called `name`, or undefined when no repo has it. */
|
|
86
|
+
function findApp(name, layout = resolveLayout()) {
|
|
87
|
+
return listApps(layout).find((app) => app.name === name)?.dir;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Resolves a workspace-relative path such as `workers.json`'s
|
|
91
|
+
* `apps/schedule-builder` to a directory: the repo you are in first, then
|
|
92
|
+
* DevDogsUGA's. When neither has it the first candidate is returned, so
|
|
93
|
+
* callers get an ordinary ENOENT naming the path rather than a second error.
|
|
94
|
+
*/
|
|
95
|
+
function resolveAppPath(relative, layout = resolveLayout()) {
|
|
96
|
+
const first = join(layout.root, relative);
|
|
97
|
+
if (existsSync(first)) return first;
|
|
98
|
+
if (layout.kind === "backstage" && layout.hasDevdogsuga) {
|
|
99
|
+
const second = join(layout.devdogsugaRoot, relative);
|
|
100
|
+
if (existsSync(second)) return second;
|
|
101
|
+
}
|
|
102
|
+
return first;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The pnpm workspace root holding the app called `name`: where
|
|
106
|
+
* `pnpm --filter <name>` has to run. DevDogsUGA's root for its apps seen from a
|
|
107
|
+
* Backstage checkout, `layout.root` otherwise.
|
|
108
|
+
*/
|
|
109
|
+
function appWorkspaceRoot(name, layout = resolveLayout()) {
|
|
110
|
+
const dir = findApp(name, layout);
|
|
111
|
+
return dir === void 0 ? layout.root : dirname(dirname(dir));
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
//#region ../cli-core/src/repo/supabase-project.ts
|
|
115
|
+
/**
|
|
116
|
+
* This repo's Supabase project id — read from `supabase/config.toml` — and
|
|
117
|
+
* the container-name conventions the Supabase CLI derives from it.
|
|
118
|
+
*
|
|
119
|
+
* Shared by `environment.ts`'s adaptive-menu probe (does a stack answer for
|
|
120
|
+
* THIS project?) and the foreign-stack detection in `db/generated-env.ts`
|
|
121
|
+
* and `stack.ts` (does a DIFFERENT project's stack hold the shared ports? —
|
|
122
|
+
* the case where `supabase status -o env` fails not because nothing is
|
|
123
|
+
* running, but because what is running on 127.0.0.1:54321 belongs to
|
|
124
|
+
* another workspace, or to this same repo before/after a `project_id`
|
|
125
|
+
* rename). One project-id reader and one container-name parser, so both
|
|
126
|
+
* questions agree on what "this project" means.
|
|
127
|
+
*/
|
|
128
|
+
/**
|
|
129
|
+
* Reads `project_id` out of `<repoRoot>/supabase/config.toml`, or `null`
|
|
130
|
+
* when the file is missing or unparsable.
|
|
131
|
+
*
|
|
132
|
+
* Regex, not a TOML parser: it is one line, the shape has been stable
|
|
133
|
+
* across every CLI version this repo has seen, and a parser on a path some
|
|
134
|
+
* callers pay before their first real subprocess is a cost that stays
|
|
135
|
+
* invisible until it is why the tool feels slow.
|
|
136
|
+
*/
|
|
137
|
+
function readProjectId(repoRoot) {
|
|
138
|
+
try {
|
|
139
|
+
const path = join(repoRoot, "supabase", "config.toml");
|
|
140
|
+
return /^\s*project_id\s*=\s*"([^"]+)"/m.exec(readFileSync(path, "utf8"))?.[1] ?? null;
|
|
141
|
+
} catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The Supabase CLI's container name prefix for a project id, or the bare
|
|
147
|
+
* `supabase_db_` fallback when the id could not be read.
|
|
148
|
+
*
|
|
149
|
+
* Falling back to the bare prefix means a machine whose `config.toml`
|
|
150
|
+
* cannot be read still detects *a* stack, at the cost of over-matching a
|
|
151
|
+
* differently-named project — deliberate, and unchanged from this
|
|
152
|
+
* function's original home in `environment.ts`: over-matching offers
|
|
153
|
+
* `stop`/a foreign-stack hint for a stack that turns out to be this
|
|
154
|
+
* project's own, which is harmless to see and decline, while
|
|
155
|
+
* under-matching hides a real signal entirely.
|
|
156
|
+
*/
|
|
157
|
+
function containerPrefix(projectId) {
|
|
158
|
+
return projectId ? `supabase_db_${projectId}` : "supabase_db_";
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The local stack's API port, published by its Kong container. Mirrors
|
|
162
|
+
* `env-runtime/load`'s `LOCAL_STACK_PORT`, which this module cannot import
|
|
163
|
+
* statically (that package is an optional peer, loaded on demand).
|
|
164
|
+
*/
|
|
165
|
+
const STACK_API_PORT = 54321;
|
|
166
|
+
function defaultExec(file, args) {
|
|
167
|
+
try {
|
|
168
|
+
return execSync(file, args, {
|
|
169
|
+
stdio: [
|
|
170
|
+
"ignore",
|
|
171
|
+
"pipe",
|
|
172
|
+
"ignore"
|
|
173
|
+
],
|
|
174
|
+
timeout: 3e3
|
|
175
|
+
});
|
|
176
|
+
} catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* `docker ps --format '{{.Names}}'`, split into trimmed, non-empty names —
|
|
182
|
+
* or `null` when Docker could not be read at all (no daemon, not
|
|
183
|
+
* installed, timed out). With `publish`, only the containers publishing that
|
|
184
|
+
* host port. `execute` is injectable for tests, mirroring `environment.ts`'s
|
|
185
|
+
* own probes.
|
|
186
|
+
*/
|
|
187
|
+
function listContainerNames(publish, execute = defaultExec) {
|
|
188
|
+
const out = execute("docker", [
|
|
189
|
+
"ps",
|
|
190
|
+
...publish === void 0 ? [] : ["--filter", `publish=${publish}`],
|
|
191
|
+
"--format",
|
|
192
|
+
"{{.Names}}"
|
|
193
|
+
]);
|
|
194
|
+
if (out === null) return null;
|
|
195
|
+
return out.split("\n").map((name) => name.trim()).filter((name) => name.length > 0);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Finds the `supabase_kong_<id>` container among `names` whose id differs
|
|
199
|
+
* from `projectId` — the signature of a stack started from a DIFFERENT
|
|
200
|
+
* Supabase project (this same repo before/after a rename, or another
|
|
201
|
+
* project entirely) holding the shared ports. Kong is the container that
|
|
202
|
+
* publishes the API port, so callers pass the names `listContainerNames`
|
|
203
|
+
* returns for `STACK_API_PORT`: that names the stack actually on 54321,
|
|
204
|
+
* not just any Supabase stack on the machine.
|
|
205
|
+
*
|
|
206
|
+
* Returns that foreign id, or `null` when every matching container belongs
|
|
207
|
+
* to this project, none match at all, or `projectId` itself could not be
|
|
208
|
+
* read — with no known id of our own, there is nothing to compare against,
|
|
209
|
+
* and guessing would risk telling a contributor their OWN stack is
|
|
210
|
+
* foreign.
|
|
211
|
+
*/
|
|
212
|
+
function foreignStackProjectId(names, projectId) {
|
|
213
|
+
if (projectId === null) return null;
|
|
214
|
+
for (const name of names) {
|
|
215
|
+
const match = /^supabase_kong_(.+)$/.exec(name);
|
|
216
|
+
if (!match) continue;
|
|
217
|
+
const id = match[1];
|
|
218
|
+
if (id !== projectId) return id;
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* The actionable message for a detected foreign stack — shared by
|
|
224
|
+
* `db/generated-env.ts` (a failed `supabase status -o env`) and `stack.ts`
|
|
225
|
+
* (a failed `supabase start`), so both name the same fix in the same
|
|
226
|
+
* words.
|
|
227
|
+
*/
|
|
228
|
+
function foreignStackMessage(foreignProjectId) {
|
|
229
|
+
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\`.`;
|
|
230
|
+
}
|
|
231
|
+
//#endregion
|
|
8
232
|
//#region ../cli-core/src/dry-run.ts
|
|
9
233
|
const DRY_RUN_FLAG = "--dry-run";
|
|
10
234
|
/** Commands that forward everything after their name to another tool. */
|
|
@@ -110,7 +334,7 @@ function runInGroup(command, args, opts = {}) {
|
|
|
110
334
|
});
|
|
111
335
|
}
|
|
112
336
|
return new Promise((resolve) => {
|
|
113
|
-
const child =
|
|
337
|
+
const child = spawnProcess(command, args, {
|
|
114
338
|
stdio: opts.stdio ?? "inherit",
|
|
115
339
|
cwd: opts.cwd,
|
|
116
340
|
env: opts.env,
|
|
@@ -188,111 +412,6 @@ function reportRan(command, args, result) {
|
|
|
188
412
|
const status = result.code === 0 ? "" : ` (exit ${result.code})`;
|
|
189
413
|
process.stderr.write(`Ran: ${formatCommand(command, args)}${status}\n`);
|
|
190
414
|
}
|
|
191
|
-
/** The directory (relative to a Backstage root) holding the DevDogsUGA checkout. */
|
|
192
|
-
const DEVDOGSUGA_LINK = "devdogsuga";
|
|
193
|
-
var DevdogsugaMissingError = class extends Error {
|
|
194
|
-
constructor(path) {
|
|
195
|
-
super(`The DevDogsUGA checkout is missing at ${path}. Run \`pnpm devdogsuga\` (links the sibling clone, or \$DEVDOGSUGA_DIR) or \`pnpm install\` in the Backstage root first.`);
|
|
196
|
-
this.name = "DevdogsugaMissingError";
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
/** The kind of repo `dir` is the root of, by its `package.json` name; null if neither. */
|
|
200
|
-
function repoKindOf(dir) {
|
|
201
|
-
try {
|
|
202
|
-
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
203
|
-
if (pkg.name === "devdogs-monorepo") return "devdogsuga";
|
|
204
|
-
if (pkg.name === "backstage") return "backstage";
|
|
205
|
-
} catch {}
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
function isDir(path) {
|
|
209
|
-
try {
|
|
210
|
-
return statSync(path).isDirectory();
|
|
211
|
-
} catch {
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* The layout of the repo rooted at `root`. Pure: nothing is memoized, so tests
|
|
217
|
-
* can build fixtures freely. An unrecognised package name is treated as
|
|
218
|
-
* DevDogsUGA (the contract fixtures and every pre-Backstage caller).
|
|
219
|
-
*/
|
|
220
|
-
function layoutAt(root) {
|
|
221
|
-
const kind = repoKindOf(root) === "backstage" ? "backstage" : "devdogsuga";
|
|
222
|
-
const sibling = join(root, DEVDOGSUGA_LINK);
|
|
223
|
-
const hasDevdogsuga = kind === "devdogsuga" || isDir(sibling);
|
|
224
|
-
const devdogsugaRoot = kind === "devdogsuga" ? root : sibling;
|
|
225
|
-
const appsDirs = [join(root, "apps")];
|
|
226
|
-
if (kind === "backstage" && hasDevdogsuga) appsDirs.push(join(devdogsugaRoot, "apps"));
|
|
227
|
-
return {
|
|
228
|
-
kind,
|
|
229
|
-
root,
|
|
230
|
-
get devdogsugaRoot() {
|
|
231
|
-
if (!hasDevdogsuga) throw new DevdogsugaMissingError(sibling);
|
|
232
|
-
return devdogsugaRoot;
|
|
233
|
-
},
|
|
234
|
-
...kind === "backstage" ? { backstageRoot: root } : {},
|
|
235
|
-
hasDevdogsuga,
|
|
236
|
-
appsDirs: appsDirs.filter(isDir),
|
|
237
|
-
envMirrors: kind === "backstage" && hasDevdogsuga ? [devdogsugaRoot] : []
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
/** `layout.devdogsugaRoot`, or null instead of a throw when the checkout is missing. */
|
|
241
|
-
function devdogsugaRootOrNull(layout) {
|
|
242
|
-
return layout.hasDevdogsuga ? layout.devdogsugaRoot : null;
|
|
243
|
-
}
|
|
244
|
-
let cached;
|
|
245
|
-
let cachedFor;
|
|
246
|
-
/** The layout of the repo the process is in (`findRepoRoot()`), memoized per root. */
|
|
247
|
-
function resolveLayout() {
|
|
248
|
-
const root = findRepoRoot();
|
|
249
|
-
if (cached === void 0 || cachedFor !== root) {
|
|
250
|
-
cached = layoutAt(root);
|
|
251
|
-
cachedFor = root;
|
|
252
|
-
}
|
|
253
|
-
return cached;
|
|
254
|
-
}
|
|
255
|
-
/** Every directory under `appsDirs` (one app each), de-duplicated by name, sorted by name. */
|
|
256
|
-
function listApps(layout = resolveLayout()) {
|
|
257
|
-
const seen = /* @__PURE__ */ new Map();
|
|
258
|
-
for (const parent of layout.appsDirs) for (const entry of readdirSync(parent, { withFileTypes: true })) {
|
|
259
|
-
if (!entry.isDirectory()) continue;
|
|
260
|
-
if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
|
|
261
|
-
if (!seen.has(entry.name)) seen.set(entry.name, {
|
|
262
|
-
name: entry.name,
|
|
263
|
-
dir: join(parent, entry.name)
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
267
|
-
}
|
|
268
|
-
/** The directory of the app called `name`, or undefined when no repo has it. */
|
|
269
|
-
function findApp(name, layout = resolveLayout()) {
|
|
270
|
-
return listApps(layout).find((app) => app.name === name)?.dir;
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Resolves a workspace-relative path such as `workers.json`'s
|
|
274
|
-
* `apps/schedule-builder` to a directory: the repo you are in first, then
|
|
275
|
-
* DevDogsUGA's. When neither has it the first candidate is returned, so
|
|
276
|
-
* callers get an ordinary ENOENT naming the path rather than a second error.
|
|
277
|
-
*/
|
|
278
|
-
function resolveAppPath(relative, layout = resolveLayout()) {
|
|
279
|
-
const first = join(layout.root, relative);
|
|
280
|
-
if (existsSync(first)) return first;
|
|
281
|
-
if (layout.kind === "backstage" && layout.hasDevdogsuga) {
|
|
282
|
-
const second = join(layout.devdogsugaRoot, relative);
|
|
283
|
-
if (existsSync(second)) return second;
|
|
284
|
-
}
|
|
285
|
-
return first;
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* The pnpm workspace root holding the app called `name`: where
|
|
289
|
-
* `pnpm --filter <name>` has to run. DevDogsUGA's root for its apps seen from a
|
|
290
|
-
* Backstage checkout, `layout.root` otherwise.
|
|
291
|
-
*/
|
|
292
|
-
function appWorkspaceRoot(name, layout = resolveLayout()) {
|
|
293
|
-
const dir = findApp(name, layout);
|
|
294
|
-
return dir === void 0 ? layout.root : dirname(dirname(dir));
|
|
295
|
-
}
|
|
296
415
|
//#endregion
|
|
297
416
|
//#region ../cli-core/src/repo/resolve.ts
|
|
298
417
|
/**
|
|
@@ -512,25 +631,14 @@ function getEnvSync() {
|
|
|
512
631
|
}
|
|
513
632
|
/** The env loader (`loadEnvironment` and the tier/file selection). Bundled. */
|
|
514
633
|
function loadEnvLoad() {
|
|
515
|
-
return import("./load-
|
|
634
|
+
return import("./load-yn7JCcS2.js");
|
|
516
635
|
}
|
|
517
636
|
/** The session policy (tier resolution, `enterEnvironment`). Bundled. */
|
|
518
637
|
function loadEnvSession() {
|
|
519
|
-
return import("./session-
|
|
638
|
+
return import("./session-DzgtIg6K.js");
|
|
520
639
|
}
|
|
521
640
|
//#endregion
|
|
522
641
|
//#region ../cli-core/src/db/run.ts
|
|
523
|
-
/**
|
|
524
|
-
* Shared helpers for running supabase CLI and pnpm commands.
|
|
525
|
-
*
|
|
526
|
-
* The supabase CLI is a workspace devDependency (not a global install), so
|
|
527
|
-
* every invocation goes through `pnpm exec supabase`. Plain pnpm commands run
|
|
528
|
-
* in `findRepoRoot()`; the supabase ones run in DevDogsUGA's root
|
|
529
|
-
* (`supabaseRoot()`), where `supabase/config.toml` lives and where the CLI is
|
|
530
|
-
* installed. From a Backstage checkout that is `<root>/devdogsuga`, so it needs
|
|
531
|
-
* its own `pnpm install` there before any of these work.
|
|
532
|
-
*/
|
|
533
|
-
const runFile = promisify(execFile);
|
|
534
642
|
/** Where `supabase/` lives, and so the cwd of every supabase CLI call. */
|
|
535
643
|
function supabaseRoot() {
|
|
536
644
|
return resolveLayout().devdogsugaRoot;
|
|
@@ -561,7 +669,7 @@ function reportRuns(enabled) {
|
|
|
561
669
|
* Pass `env` to run the child against a loaded tier rather than process.env. */
|
|
562
670
|
function runWithStderr(args, env, cwd = findRepoRoot()) {
|
|
563
671
|
return new Promise((resolve) => {
|
|
564
|
-
const child =
|
|
672
|
+
const child = spawnProcess("pnpm", args, {
|
|
565
673
|
stdio: [
|
|
566
674
|
"inherit",
|
|
567
675
|
"inherit",
|
|
@@ -571,8 +679,8 @@ function runWithStderr(args, env, cwd = findRepoRoot()) {
|
|
|
571
679
|
...env ? { env } : {}
|
|
572
680
|
});
|
|
573
681
|
let stderr = "";
|
|
574
|
-
child.stderr
|
|
575
|
-
child.stderr
|
|
682
|
+
child.stderr?.setEncoding("utf8");
|
|
683
|
+
child.stderr?.on("data", (chunk) => {
|
|
576
684
|
stderr += chunk;
|
|
577
685
|
process.stderr.write(chunk);
|
|
578
686
|
});
|
|
@@ -593,10 +701,7 @@ function runWithStderr(args, env, cwd = findRepoRoot()) {
|
|
|
593
701
|
}
|
|
594
702
|
/** Spawn a pnpm command, capture stdout, throw on non-zero exit. */
|
|
595
703
|
async function capture(args, cwd = findRepoRoot()) {
|
|
596
|
-
const { stdout } = await
|
|
597
|
-
cwd,
|
|
598
|
-
encoding: "utf8"
|
|
599
|
-
});
|
|
704
|
+
const { stdout } = await exec("pnpm", args, { cwd });
|
|
600
705
|
return stdout;
|
|
601
706
|
}
|
|
602
707
|
/** `pnpm exec supabase …` with inherited stdio. */
|
|
@@ -644,123 +749,4 @@ async function seedBuckets(conn) {
|
|
|
644
749
|
return supabase("seed", "buckets", "--project-ref", conn.projectRef, "--linked");
|
|
645
750
|
}
|
|
646
751
|
//#endregion
|
|
647
|
-
|
|
648
|
-
/**
|
|
649
|
-
* This repo's Supabase project id — read from `supabase/config.toml` — and
|
|
650
|
-
* the container-name conventions the Supabase CLI derives from it.
|
|
651
|
-
*
|
|
652
|
-
* Shared by `environment.ts`'s adaptive-menu probe (does a stack answer for
|
|
653
|
-
* THIS project?) and the foreign-stack detection in `db/generated-env.ts`
|
|
654
|
-
* and `stack.ts` (does a DIFFERENT project's stack hold the shared ports? —
|
|
655
|
-
* the case where `supabase status -o env` fails not because nothing is
|
|
656
|
-
* running, but because what is running on 127.0.0.1:54321 belongs to
|
|
657
|
-
* another workspace, or to this same repo before/after a `project_id`
|
|
658
|
-
* rename). One project-id reader and one container-name parser, so both
|
|
659
|
-
* questions agree on what "this project" means.
|
|
660
|
-
*/
|
|
661
|
-
/**
|
|
662
|
-
* Reads `project_id` out of `<repoRoot>/supabase/config.toml`, or `null`
|
|
663
|
-
* when the file is missing or unparsable.
|
|
664
|
-
*
|
|
665
|
-
* Regex, not a TOML parser: it is one line, the shape has been stable
|
|
666
|
-
* across every CLI version this repo has seen, and a parser on a path some
|
|
667
|
-
* callers pay before their first real subprocess is a cost that stays
|
|
668
|
-
* invisible until it is why the tool feels slow.
|
|
669
|
-
*/
|
|
670
|
-
function readProjectId(repoRoot) {
|
|
671
|
-
try {
|
|
672
|
-
const path = join(repoRoot, "supabase", "config.toml");
|
|
673
|
-
return /^\s*project_id\s*=\s*"([^"]+)"/m.exec(readFileSync(path, "utf8"))?.[1] ?? null;
|
|
674
|
-
} catch {
|
|
675
|
-
return null;
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
/**
|
|
679
|
-
* The Supabase CLI's container name prefix for a project id, or the bare
|
|
680
|
-
* `supabase_db_` fallback when the id could not be read.
|
|
681
|
-
*
|
|
682
|
-
* Falling back to the bare prefix means a machine whose `config.toml`
|
|
683
|
-
* cannot be read still detects *a* stack, at the cost of over-matching a
|
|
684
|
-
* differently-named project — deliberate, and unchanged from this
|
|
685
|
-
* function's original home in `environment.ts`: over-matching offers
|
|
686
|
-
* `stop`/a foreign-stack hint for a stack that turns out to be this
|
|
687
|
-
* project's own, which is harmless to see and decline, while
|
|
688
|
-
* under-matching hides a real signal entirely.
|
|
689
|
-
*/
|
|
690
|
-
function containerPrefix(projectId) {
|
|
691
|
-
return projectId ? `supabase_db_${projectId}` : "supabase_db_";
|
|
692
|
-
}
|
|
693
|
-
/**
|
|
694
|
-
* The local stack's API port, published by its Kong container. Mirrors
|
|
695
|
-
* `env-runtime/load`'s `LOCAL_STACK_PORT`, which this module cannot import
|
|
696
|
-
* statically (that package is an optional peer, loaded on demand).
|
|
697
|
-
*/
|
|
698
|
-
const STACK_API_PORT = 54321;
|
|
699
|
-
function defaultExec(file, args) {
|
|
700
|
-
try {
|
|
701
|
-
return execFileSync(file, args, {
|
|
702
|
-
encoding: "utf8",
|
|
703
|
-
stdio: [
|
|
704
|
-
"ignore",
|
|
705
|
-
"pipe",
|
|
706
|
-
"ignore"
|
|
707
|
-
],
|
|
708
|
-
timeout: 3e3
|
|
709
|
-
});
|
|
710
|
-
} catch {
|
|
711
|
-
return null;
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
/**
|
|
715
|
-
* `docker ps --format '{{.Names}}'`, split into trimmed, non-empty names —
|
|
716
|
-
* or `null` when Docker could not be read at all (no daemon, not
|
|
717
|
-
* installed, timed out). With `publish`, only the containers publishing that
|
|
718
|
-
* host port. `execute` is injectable for tests, mirroring `environment.ts`'s
|
|
719
|
-
* own probes.
|
|
720
|
-
*/
|
|
721
|
-
function listContainerNames(publish, execute = defaultExec) {
|
|
722
|
-
const out = execute("docker", [
|
|
723
|
-
"ps",
|
|
724
|
-
...publish === void 0 ? [] : ["--filter", `publish=${publish}`],
|
|
725
|
-
"--format",
|
|
726
|
-
"{{.Names}}"
|
|
727
|
-
]);
|
|
728
|
-
if (out === null) return null;
|
|
729
|
-
return out.split("\n").map((name) => name.trim()).filter((name) => name.length > 0);
|
|
730
|
-
}
|
|
731
|
-
/**
|
|
732
|
-
* Finds the `supabase_kong_<id>` container among `names` whose id differs
|
|
733
|
-
* from `projectId` — the signature of a stack started from a DIFFERENT
|
|
734
|
-
* Supabase project (this same repo before/after a rename, or another
|
|
735
|
-
* project entirely) holding the shared ports. Kong is the container that
|
|
736
|
-
* publishes the API port, so callers pass the names `listContainerNames`
|
|
737
|
-
* returns for `STACK_API_PORT`: that names the stack actually on 54321,
|
|
738
|
-
* not just any Supabase stack on the machine.
|
|
739
|
-
*
|
|
740
|
-
* Returns that foreign id, or `null` when every matching container belongs
|
|
741
|
-
* to this project, none match at all, or `projectId` itself could not be
|
|
742
|
-
* read — with no known id of our own, there is nothing to compare against,
|
|
743
|
-
* and guessing would risk telling a contributor their OWN stack is
|
|
744
|
-
* foreign.
|
|
745
|
-
*/
|
|
746
|
-
function foreignStackProjectId(names, projectId) {
|
|
747
|
-
if (projectId === null) return null;
|
|
748
|
-
for (const name of names) {
|
|
749
|
-
const match = /^supabase_kong_(.+)$/.exec(name);
|
|
750
|
-
if (!match) continue;
|
|
751
|
-
const id = match[1];
|
|
752
|
-
if (id !== projectId) return id;
|
|
753
|
-
}
|
|
754
|
-
return null;
|
|
755
|
-
}
|
|
756
|
-
/**
|
|
757
|
-
* The actionable message for a detected foreign stack — shared by
|
|
758
|
-
* `db/generated-env.ts` (a failed `supabase status -o env`) and `stack.ts`
|
|
759
|
-
* (a failed `supabase start`), so both name the same fix in the same
|
|
760
|
-
* words.
|
|
761
|
-
*/
|
|
762
|
-
function foreignStackMessage(foreignProjectId) {
|
|
763
|
-
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\`.`;
|
|
764
|
-
}
|
|
765
|
-
//#endregion
|
|
766
|
-
export { dryRunKind as A, layoutAt as C, formatCommand as D, resolveLayout as E, resolveDryRun as M, setDryRun as N, reportRan as O, wouldRunLine as P, findApp as S, resolveAppPath as T, loadEnvLoad as _, listContainerNames as a, appWorkspaceRoot as b, reportRuns as c, seedBuckets as d, supabase as f, loadEnv as g, getEnvSync as h, foreignStackProjectId as i, isDryRun as j, runInGroup as k, 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, loadEnvSession as v, listApps as w, devdogsugaRootOrNull as x, repoPeerUrl as y };
|
|
752
|
+
export { findApp as A, containerPrefix as C, readProjectId as D, listContainerNames as E, listApps as M, resolveAppPath as N, appWorkspaceRoot as O, resolveLayout as P, STACK_API_PORT as S, foreignStackProjectId as T, dryRunKind as _, seedBuckets as a, setDryRun as b, supabaseRoot as c, loadEnvLoad as d, loadEnvSession as f, runInGroup as g, reportRan as h, runWithStderr as i, layoutAt as j, devdogsugaRootOrNull as k, getEnvSync as l, formatCommand as m, reportRuns as n, supabase as o, repoPeerUrl as p, run as r, supabaseCapture as s, dbPush as t, loadEnv as u, isDryRun as v, foreignStackMessage as w, wouldRunLine as x, resolveDryRun as y };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as loadEnvironment, g as DEPLOY_ENVIRONMENTS, m as probeLocalStack, p as parseRawAssignments, t as DEV_DB_ENV, u as isDevDatabase, v as fileFor, y as isDeployEnvironment } from "./load-Z2CXCpyx.js";
|
|
2
2
|
//#region ../cli-core/src/env-runtime/ask.ts
|
|
3
3
|
/**
|
|
4
4
|
* The interactive half of session resolution, shared by `with-env` and the
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as parseSessionSelector, c as rememberDevDatabase, i as enterEnvironment, l as rememberedDevDatabase, n as availableTiers, o as resolveSessionTier, r as developmentRemoteCandidate, s as askSession, t as SESSION_SELECTORS } from "./session-
|
|
1
|
+
import { a as parseSessionSelector, c as rememberDevDatabase, i as enterEnvironment, l as rememberedDevDatabase, n as availableTiers, o as resolveSessionTier, r as developmentRemoteCandidate, s as askSession, t as SESSION_SELECTORS } from "./session-CuexI1f9.js";
|
|
2
2
|
export { SESSION_SELECTORS, askSession, availableTiers, developmentRemoteCandidate, enterEnvironment, parseSessionSelector, rememberDevDatabase, rememberedDevDatabase, resolveSessionTier };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import crossSpawn from "cross-spawn";
|
|
2
|
+
//#region ../cli-core/src/spawn.ts
|
|
3
|
+
/** `child_process.spawn`, but able to start Windows `.cmd` shims. */
|
|
4
|
+
function spawnProcess(command, args, options = {}) {
|
|
5
|
+
return crossSpawn(command, [...args], options);
|
|
6
|
+
}
|
|
7
|
+
function execError(message, extra) {
|
|
8
|
+
return Object.assign(new Error(message), {
|
|
9
|
+
stdout: "",
|
|
10
|
+
stderr: "",
|
|
11
|
+
...extra
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* `execFileSync(command, args, { encoding: "utf8" })` for a command that may
|
|
16
|
+
* be a Windows shim. Returns stdout; throws an {@link ExecError} when the
|
|
17
|
+
* program cannot start (`code: "ENOENT"`), times out, or exits non-zero.
|
|
18
|
+
*/
|
|
19
|
+
function execSync(command, args, options = {}) {
|
|
20
|
+
const result = crossSpawn.sync(command, [...args], {
|
|
21
|
+
cwd: options.cwd,
|
|
22
|
+
env: options.env,
|
|
23
|
+
timeout: options.timeout,
|
|
24
|
+
encoding: "utf8",
|
|
25
|
+
stdio: options.stdio ?? [
|
|
26
|
+
"ignore",
|
|
27
|
+
"pipe",
|
|
28
|
+
"ignore"
|
|
29
|
+
]
|
|
30
|
+
});
|
|
31
|
+
const stdout = typeof result.stdout === "string" ? result.stdout : "";
|
|
32
|
+
const stderr = typeof result.stderr === "string" ? result.stderr : "";
|
|
33
|
+
if (result.error) throw execError(result.error.message, {
|
|
34
|
+
code: result.error.code,
|
|
35
|
+
stdout,
|
|
36
|
+
stderr
|
|
37
|
+
});
|
|
38
|
+
if (result.status !== 0) throw execError(`Command failed: ${command} ${args.join(" ")}${stderr ? `\n${stderr}` : ""}`, {
|
|
39
|
+
code: result.status ?? void 0,
|
|
40
|
+
status: result.status,
|
|
41
|
+
stdout,
|
|
42
|
+
stderr
|
|
43
|
+
});
|
|
44
|
+
return stdout;
|
|
45
|
+
}
|
|
46
|
+
/** The async twin of {@link execSync}: resolves `{ stdout, stderr }`. */
|
|
47
|
+
function exec(command, args, options = {}) {
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
const child = crossSpawn(command, [...args], {
|
|
50
|
+
cwd: options.cwd,
|
|
51
|
+
env: options.env,
|
|
52
|
+
stdio: options.stdio ?? [
|
|
53
|
+
"ignore",
|
|
54
|
+
"pipe",
|
|
55
|
+
"pipe"
|
|
56
|
+
]
|
|
57
|
+
});
|
|
58
|
+
let stdout = "";
|
|
59
|
+
let stderr = "";
|
|
60
|
+
let timedOut = false;
|
|
61
|
+
const timer = options.timeout === void 0 ? void 0 : setTimeout(() => {
|
|
62
|
+
timedOut = true;
|
|
63
|
+
child.kill();
|
|
64
|
+
}, options.timeout);
|
|
65
|
+
child.stdout?.setEncoding("utf8");
|
|
66
|
+
child.stderr?.setEncoding("utf8");
|
|
67
|
+
child.stdout?.on("data", (chunk) => {
|
|
68
|
+
stdout += chunk;
|
|
69
|
+
});
|
|
70
|
+
child.stderr?.on("data", (chunk) => {
|
|
71
|
+
stderr += chunk;
|
|
72
|
+
});
|
|
73
|
+
child.on("error", (error) => {
|
|
74
|
+
if (timer) clearTimeout(timer);
|
|
75
|
+
reject(execError(error.message, {
|
|
76
|
+
code: error.code,
|
|
77
|
+
stdout,
|
|
78
|
+
stderr
|
|
79
|
+
}));
|
|
80
|
+
});
|
|
81
|
+
child.on("close", (code) => {
|
|
82
|
+
if (timer) clearTimeout(timer);
|
|
83
|
+
if (code === 0 && !timedOut) {
|
|
84
|
+
resolve({
|
|
85
|
+
stdout,
|
|
86
|
+
stderr
|
|
87
|
+
});
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
reject(execError(timedOut ? `Command timed out: ${command}` : `Command failed: ${command} ${args.join(" ")}${stderr ? `\n${stderr}` : ""}`, {
|
|
91
|
+
code: code ?? void 0,
|
|
92
|
+
status: code,
|
|
93
|
+
stdout,
|
|
94
|
+
stderr
|
|
95
|
+
}));
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
export { execSync as n, spawnProcess as r, exec as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { D as readProjectId, E as listContainerNames, P as resolveLayout, S as STACK_API_PORT, T as foreignStackProjectId, a as seedBuckets, d as loadEnvLoad, f as loadEnvSession, o as supabase, s as supabaseCapture, v as isDryRun, w as foreignStackMessage } from "./run-CRBte7p0.js";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { writeFile } from "node:fs/promises";
|
|
4
4
|
//#region src/db/session-refresh.ts
|