@devdogsuga/devtools 0.1.25 → 0.1.27
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 +72 -11
- package/bin/devtools.mjs +4 -4
- package/dist/cli-CZKkNHK5.js +6619 -0
- package/dist/failure-log-BBPGejQv.js +256 -0
- package/dist/launch.js +267 -281
- package/dist/local-env-D8HC2JK_.js +351 -0
- package/dist/local-env-DrBCXqD8.js +2 -0
- package/dist/menu-CQBsQRgH.js +1684 -0
- package/dist/peer-redirect-hooks.js +15 -0
- package/dist/peers-RBplwovA.js +229 -0
- package/dist/root-C2OmZDwD.js +94 -0
- package/dist/stack-Bx-55oi_.js +2 -0
- package/dist/stack-BynCgRVS.js +181 -0
- package/dist/supabase-project-0knM0mqd.js +426 -0
- package/dist/telemetry-C4foD8MD.js +249 -0
- package/dist/telemetry.js +2 -148
- package/package.json +10 -29
- package/bin/devtools-ci-bare.mjs +0 -48
- package/bin/devtools-ci.mjs +0 -31
- package/dist/args.d.ts +0 -26
- package/dist/args.js +0 -78
- package/dist/bws/bw.d.ts +0 -18
- package/dist/bws/bw.js +0 -70
- package/dist/bws/client.d.ts +0 -43
- package/dist/bws/client.js +0 -363
- package/dist/bws/environments.d.ts +0 -96
- package/dist/bws/environments.js +0 -111
- package/dist/bws/pace.d.ts +0 -25
- package/dist/bws/pace.js +0 -43
- package/dist/bws/retry.d.ts +0 -12
- package/dist/bws/retry.js +0 -39
- package/dist/bws/store.d.ts +0 -1
- package/dist/bws/store.js +0 -32
- package/dist/bws/token.d.ts +0 -31
- package/dist/bws/token.js +0 -88
- package/dist/bws/vault.d.ts +0 -29
- package/dist/bws/vault.js +0 -200
- package/dist/catalog.d.ts +0 -57
- package/dist/catalog.js +0 -57
- package/dist/cf/build.d.ts +0 -13
- package/dist/cf/build.js +0 -65
- package/dist/cf/commands.d.ts +0 -1
- package/dist/cf/commands.js +0 -172
- package/dist/cf/local-env.d.ts +0 -58
- package/dist/cf/local-env.js +0 -177
- package/dist/ci.d.ts +0 -1
- package/dist/ci.js +0 -328
- package/dist/cli.d.ts +0 -11
- package/dist/cli.js +0 -1094
- package/dist/commands.d.ts +0 -261
- package/dist/commands.js +0 -1318
- package/dist/completions.d.ts +0 -4
- package/dist/completions.js +0 -110
- package/dist/cron/commands.d.ts +0 -47
- package/dist/cron/commands.js +0 -372
- package/dist/cron/discovery.d.ts +0 -55
- package/dist/cron/discovery.js +0 -85
- package/dist/cron/schema.d.ts +0 -45
- package/dist/cron/schema.js +0 -35
- package/dist/db/config-push.d.ts +0 -8
- package/dist/db/config-push.js +0 -11
- package/dist/db/connection.d.ts +0 -79
- package/dist/db/connection.js +0 -114
- package/dist/db/exec.d.ts +0 -1
- package/dist/db/exec.js +0 -11
- package/dist/db/generate-types.d.ts +0 -1
- package/dist/db/generate-types.js +0 -4
- package/dist/db/generated-env.d.ts +0 -69
- package/dist/db/generated-env.js +0 -113
- package/dist/db/introspect.d.ts +0 -1
- package/dist/db/introspect.js +0 -122
- package/dist/db/local-env.d.ts +0 -65
- package/dist/db/local-env.js +0 -23
- package/dist/db/new-migration.d.ts +0 -8
- package/dist/db/new-migration.js +0 -69
- package/dist/db/pooler.d.ts +0 -23
- package/dist/db/pooler.js +0 -55
- package/dist/db/run.d.ts +0 -68
- package/dist/db/run.js +0 -156
- package/dist/db/seed-buckets.d.ts +0 -2
- package/dist/db/seed-buckets.js +0 -4
- package/dist/db/seed-production.d.ts +0 -8
- package/dist/db/seed-production.js +0 -79
- package/dist/db/seed-roles.d.ts +0 -7
- package/dist/db/seed-roles.js +0 -33
- package/dist/db/session-refresh.d.ts +0 -43
- package/dist/db/session-refresh.js +0 -40
- package/dist/deploy/migrations.d.ts +0 -20
- package/dist/deploy/migrations.js +0 -68
- package/dist/deploy/orphans.d.ts +0 -20
- package/dist/deploy/orphans.js +0 -177
- package/dist/deploy/preflight.d.ts +0 -53
- package/dist/deploy/preflight.js +0 -189
- package/dist/deploy/report.d.ts +0 -26
- package/dist/deploy/report.js +0 -70
- package/dist/deploy/require-planner.d.ts +0 -2
- package/dist/deploy/require-planner.js +0 -60
- package/dist/deploy/require-token.d.ts +0 -15
- package/dist/deploy/require-token.js +0 -78
- package/dist/deploy/secrets-file.d.ts +0 -17
- package/dist/deploy/secrets-file.js +0 -166
- package/dist/deploy/write-env.d.ts +0 -40
- package/dist/deploy/write-env.js +0 -395
- package/dist/docs/index-pages.d.ts +0 -38
- package/dist/docs/index-pages.js +0 -186
- package/dist/emails/commands.d.ts +0 -15
- package/dist/emails/commands.js +0 -130
- package/dist/emails/email-types.d.ts +0 -13
- package/dist/emails/email-types.js +0 -1
- package/dist/emails/fixtures.d.ts +0 -20
- package/dist/emails/fixtures.js +0 -20
- package/dist/env/audit.d.ts +0 -206
- package/dist/env/audit.js +0 -436
- package/dist/env/cloudflare.d.ts +0 -21
- package/dist/env/cloudflare.js +0 -61
- package/dist/env/commands.d.ts +0 -99
- package/dist/env/commands.js +0 -613
- package/dist/env/discovery.d.ts +0 -15
- package/dist/env/discovery.js +0 -239
- package/dist/env/document.d.ts +0 -119
- package/dist/env/document.js +0 -323
- package/dist/env/example.d.ts +0 -68
- package/dist/env/example.js +0 -605
- package/dist/env/selection.d.ts +0 -105
- package/dist/env/selection.js +0 -228
- package/dist/env-entry.d.ts +0 -49
- package/dist/env-entry.js +0 -204
- package/dist/environment-doctor.d.ts +0 -45
- package/dist/environment-doctor.js +0 -429
- package/dist/environment.d.ts +0 -92
- package/dist/environment.js +0 -185
- package/dist/fingerprint.d.ts +0 -10
- package/dist/fingerprint.js +0 -18
- package/dist/gen/campus-map.d.ts +0 -1
- package/dist/gen/campus-map.js +0 -473
- package/dist/gen/commands.d.ts +0 -1
- package/dist/gen/commands.js +0 -25
- package/dist/gen/hypno.d.ts +0 -1
- package/dist/gen/hypno.js +0 -134
- package/dist/gh/client.d.ts +0 -80
- package/dist/gh/client.js +0 -259
- package/dist/gh/environments.d.ts +0 -83
- package/dist/gh/environments.js +0 -178
- package/dist/gh/rulesets/actors.d.ts +0 -14
- package/dist/gh/rulesets/actors.js +0 -69
- package/dist/gh/rulesets/api.d.ts +0 -15
- package/dist/gh/rulesets/api.js +0 -104
- package/dist/gh/rulesets/commands.d.ts +0 -3
- package/dist/gh/rulesets/commands.js +0 -203
- package/dist/gh/rulesets/desired.d.ts +0 -112
- package/dist/gh/rulesets/desired.js +0 -271
- package/dist/gh/rulesets/diff.d.ts +0 -65
- package/dist/gh/rulesets/diff.js +0 -170
- package/dist/gh/rulesets/types.d.ts +0 -76
- package/dist/gh/rulesets/types.js +0 -13
- package/dist/gh/settings/api.d.ts +0 -40
- package/dist/gh/settings/api.js +0 -180
- package/dist/gh/settings/commands.d.ts +0 -1
- package/dist/gh/settings/commands.js +0 -254
- package/dist/gh/settings/desired.d.ts +0 -53
- package/dist/gh/settings/desired.js +0 -43
- package/dist/gh/settings/diff.d.ts +0 -57
- package/dist/gh/settings/diff.js +0 -161
- package/dist/gh/settings/types.d.ts +0 -92
- package/dist/gh/settings/types.js +0 -11
- package/dist/gh/settings/workflows.d.ts +0 -41
- package/dist/gh/settings/workflows.js +0 -128
- package/dist/grantRoot.d.ts +0 -42
- package/dist/grantRoot.js +0 -89
- package/dist/help.d.ts +0 -18
- package/dist/help.js +0 -169
- package/dist/images/commands.d.ts +0 -32
- package/dist/images/commands.js +0 -231
- package/dist/images/events.d.ts +0 -47
- package/dist/images/events.js +0 -116
- package/dist/images/graphics.d.ts +0 -70
- package/dist/images/graphics.js +0 -234
- package/dist/images/open-graph-types.d.ts +0 -75
- package/dist/images/open-graph-types.js +0 -1
- package/dist/images/prompts.d.ts +0 -34
- package/dist/images/prompts.js +0 -100
- package/dist/images/render.d.ts +0 -21
- package/dist/images/render.js +0 -61
- package/dist/images/select.d.ts +0 -64
- package/dist/images/select.js +0 -96
- package/dist/instance.d.ts +0 -69
- package/dist/instance.js +0 -102
- package/dist/invocation.d.ts +0 -49
- package/dist/invocation.js +0 -78
- package/dist/launch-ci.d.ts +0 -1
- package/dist/launch-ci.js +0 -90
- package/dist/launch.d.ts +0 -18
- package/dist/menu.d.ts +0 -43
- package/dist/menu.js +0 -300
- package/dist/moderation.d.ts +0 -70
- package/dist/moderation.js +0 -128
- package/dist/oauth/browser.d.ts +0 -1
- package/dist/oauth/browser.js +0 -13
- package/dist/oauth/config.d.ts +0 -24
- package/dist/oauth/config.js +0 -24
- package/dist/oauth/connect-url.d.ts +0 -17
- package/dist/oauth/connect-url.js +0 -23
- package/dist/oauth/db.d.ts +0 -37
- package/dist/oauth/db.js +0 -108
- package/dist/oauth/device.d.ts +0 -93
- package/dist/oauth/device.js +0 -198
- package/dist/oauth/discovery.d.ts +0 -28
- package/dist/oauth/discovery.js +0 -54
- package/dist/oauth/env-file.d.ts +0 -6
- package/dist/oauth/env-file.js +0 -35
- package/dist/oauth/exchange.d.ts +0 -43
- package/dist/oauth/exchange.js +0 -93
- package/dist/oauth/label.d.ts +0 -1
- package/dist/oauth/label.js +0 -25
- package/dist/oauth/loopback.d.ts +0 -52
- package/dist/oauth/loopback.js +0 -168
- package/dist/oauth/pkce.d.ts +0 -6
- package/dist/oauth/pkce.js +0 -34
- package/dist/oauth/target.d.ts +0 -54
- package/dist/oauth/target.js +0 -102
- package/dist/oauth/transport.d.ts +0 -41
- package/dist/oauth/transport.js +0 -32
- package/dist/oauth/wizard.d.ts +0 -40
- package/dist/oauth/wizard.js +0 -578
- package/dist/persona.d.ts +0 -18
- package/dist/persona.js +0 -221
- package/dist/pick.d.ts +0 -6
- package/dist/pick.js +0 -64
- package/dist/pipes.d.ts +0 -1
- package/dist/pipes.js +0 -25
- package/dist/planner/checks.d.ts +0 -26
- package/dist/planner/checks.js +0 -104
- package/dist/planner/commands.d.ts +0 -10
- package/dist/planner/commands.js +0 -367
- package/dist/planner/db.d.ts +0 -15
- package/dist/planner/db.js +0 -33
- package/dist/planner/role.d.ts +0 -33
- package/dist/planner/role.js +0 -68
- package/dist/repo/peer-redirect-hooks.d.ts +0 -16
- package/dist/repo/peer-redirect-hooks.js +0 -10
- package/dist/repo/peer-redirect.d.ts +0 -4
- package/dist/repo/peer-redirect.js +0 -26
- package/dist/repo/peers.d.ts +0 -39
- package/dist/repo/peers.js +0 -139
- package/dist/repo/resolve.d.ts +0 -41
- package/dist/repo/resolve.js +0 -152
- package/dist/repo/root.d.ts +0 -23
- package/dist/repo/root.js +0 -91
- package/dist/repo/source.d.ts +0 -14
- package/dist/repo/source.js +0 -54
- package/dist/repo/supabase-project.d.ts +0 -60
- package/dist/repo/supabase-project.js +0 -124
- package/dist/repo/tsx-loader.d.ts +0 -6
- package/dist/repo/tsx-loader.js +0 -41
- package/dist/run/pick.d.ts +0 -90
- package/dist/run/pick.js +0 -664
- package/dist/setup.d.ts +0 -1
- package/dist/setup.js +0 -280
- package/dist/stack.d.ts +0 -48
- package/dist/stack.js +0 -403
- package/dist/telemetry.d.ts +0 -59
- package/dist/tier.d.ts +0 -40
- package/dist/tier.js +0 -96
- package/dist/ui.d.ts +0 -40
- package/dist/ui.js +0 -71
- package/dist/version.d.ts +0 -7
- package/dist/version.js +0 -17
- package/dist/workers.d.ts +0 -5
- package/dist/workers.js +0 -33
- package/dist/workflows/commands.d.ts +0 -54
- package/dist/workflows/commands.js +0 -763
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import { a as cliName, r as noteRan } from "./failure-log-BBPGejQv.js";
|
|
2
|
+
import { r as findRepoRoot } from "./root-C2OmZDwD.js";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
//#region ../cli-core/src/dry-run.ts
|
|
8
|
+
const DRY_RUN_FLAG = "--dry-run";
|
|
9
|
+
/** Commands that forward everything after their name to another tool. */
|
|
10
|
+
const FORWARDING = /* @__PURE__ */ new Set([
|
|
11
|
+
"supabase",
|
|
12
|
+
"wrangler",
|
|
13
|
+
"drizzle-kit",
|
|
14
|
+
"psql",
|
|
15
|
+
"bw",
|
|
16
|
+
"run"
|
|
17
|
+
]);
|
|
18
|
+
let active = false;
|
|
19
|
+
function setDryRun(on) {
|
|
20
|
+
active = on;
|
|
21
|
+
}
|
|
22
|
+
function isDryRun() {
|
|
23
|
+
return active;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Decides whether `argv` asks for a dry run and returns the argv to dispatch.
|
|
27
|
+
*
|
|
28
|
+
* For a forwarding command a leading `--dry-run` is consumed (the tool never
|
|
29
|
+
* sees it) and a later one is the tool's. For any other command the argv is
|
|
30
|
+
* returned as it came, because several commands read `--dry-run` themselves.
|
|
31
|
+
*/
|
|
32
|
+
function resolveDryRun(argv) {
|
|
33
|
+
const firstPositional = argv.findIndex((arg) => !arg.startsWith("-"));
|
|
34
|
+
const lead = firstPositional === -1 ? argv.length : firstPositional;
|
|
35
|
+
const command = argv[firstPositional];
|
|
36
|
+
if (command !== void 0 && FORWARDING.has(command)) {
|
|
37
|
+
const rest = argv.filter((arg, i) => i >= lead || arg !== "--dry-run");
|
|
38
|
+
return {
|
|
39
|
+
dryRun: rest.length !== argv.length,
|
|
40
|
+
rest
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
dryRun: argv.includes(DRY_RUN_FLAG),
|
|
45
|
+
rest: [...argv]
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* How a command treats `--dry-run`, from the nearest node on its path that
|
|
50
|
+
* says. `undefined` means it did not say, and the dispatcher stops it.
|
|
51
|
+
*/
|
|
52
|
+
function dryRunKind(catalog, path) {
|
|
53
|
+
for (let end = path.length; end > 0; end -= 1) {
|
|
54
|
+
const kind = catalog.findCommand(path.slice(0, end))?.dryRun;
|
|
55
|
+
if (kind !== void 0) return kind;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** The command line a stopped command would have run. */
|
|
59
|
+
function wouldRunLine(argv) {
|
|
60
|
+
return `Would run: ${cliName()} ${argv.join(" ")}`;
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region ../cli-core/src/process-group.ts
|
|
64
|
+
/**
|
|
65
|
+
* Running a child tool so that stopping it stops everything it started.
|
|
66
|
+
*
|
|
67
|
+
* `pnpm run dev` forks a shell that forks vinext that forks workerd. Killing
|
|
68
|
+
* only the pnpm process (what a plain `child.kill()` does, and what a closed
|
|
69
|
+
* terminal tab or a `kill` from another window delivers) orphans the rest,
|
|
70
|
+
* and the orphan keeps its port. So the child gets its own process group, and
|
|
71
|
+
* every stop signal this process receives is sent to the whole group.
|
|
72
|
+
*
|
|
73
|
+
* Ctrl-C needs the same care in reverse: a child in its own group is not in
|
|
74
|
+
* the terminal's foreground group, so the terminal no longer delivers ^C to
|
|
75
|
+
* it. The signal handlers below forward it.
|
|
76
|
+
*
|
|
77
|
+
* The group is only swept after the direct child exits when that exit was a
|
|
78
|
+
* signal we forwarded; a child that exits on its own is left to have cleaned
|
|
79
|
+
* up after itself, so a tool that deliberately leaves a daemon running keeps it.
|
|
80
|
+
*/
|
|
81
|
+
const FORWARDED = [
|
|
82
|
+
"SIGINT",
|
|
83
|
+
"SIGTERM",
|
|
84
|
+
"SIGHUP"
|
|
85
|
+
];
|
|
86
|
+
const SIGNAL_NUMBERS = {
|
|
87
|
+
SIGHUP: 1,
|
|
88
|
+
SIGINT: 2,
|
|
89
|
+
SIGTERM: 15,
|
|
90
|
+
SIGKILL: 9
|
|
91
|
+
};
|
|
92
|
+
/** How long a group gets to leave after SIGTERM before SIGKILL. */
|
|
93
|
+
const GRACE_MS = 5e3;
|
|
94
|
+
/** Sends `signal` to every process in `pid`'s group. False if it is already gone. */
|
|
95
|
+
function signalGroup(pid, signal) {
|
|
96
|
+
try {
|
|
97
|
+
process.kill(process.platform === "win32" ? pid : -pid, signal);
|
|
98
|
+
return true;
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function runInGroup(command, args, opts = {}) {
|
|
104
|
+
if (isDryRun()) {
|
|
105
|
+
process.stderr.write(`Would run: ${formatCommand(command, args)}\n`);
|
|
106
|
+
return Promise.resolve({
|
|
107
|
+
code: 0,
|
|
108
|
+
signal: null
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return new Promise((resolve) => {
|
|
112
|
+
const child = spawn(command, [...args], {
|
|
113
|
+
stdio: opts.stdio ?? "inherit",
|
|
114
|
+
cwd: opts.cwd,
|
|
115
|
+
env: opts.env,
|
|
116
|
+
detached: process.platform !== "win32"
|
|
117
|
+
});
|
|
118
|
+
const pid = child.pid;
|
|
119
|
+
let forwarded = null;
|
|
120
|
+
let escalation;
|
|
121
|
+
const forward = (signal) => {
|
|
122
|
+
if (pid === void 0) return;
|
|
123
|
+
forwarded ??= signal;
|
|
124
|
+
signalGroup(pid, signal);
|
|
125
|
+
escalation ??= setTimeout(() => signalGroup(pid, "SIGKILL"), GRACE_MS);
|
|
126
|
+
escalation.unref();
|
|
127
|
+
};
|
|
128
|
+
if (opts.abort?.aborted) forward("SIGTERM");
|
|
129
|
+
else opts.abort?.addEventListener("abort", () => forward("SIGTERM"), { once: true });
|
|
130
|
+
const handlers = FORWARDED.map((signal) => {
|
|
131
|
+
const handler = () => forward(signal);
|
|
132
|
+
process.on(signal, handler);
|
|
133
|
+
return [signal, handler];
|
|
134
|
+
});
|
|
135
|
+
const onExit = () => {
|
|
136
|
+
if (pid !== void 0) signalGroup(pid, "SIGKILL");
|
|
137
|
+
};
|
|
138
|
+
process.on("exit", onExit);
|
|
139
|
+
const finish = (result) => {
|
|
140
|
+
noteRan(`${formatCommand(command, args)} (exit ${result.code})`);
|
|
141
|
+
for (const [signal, handler] of handlers) process.off(signal, handler);
|
|
142
|
+
process.off("exit", onExit);
|
|
143
|
+
if (escalation) clearTimeout(escalation);
|
|
144
|
+
if (forwarded !== null && pid !== void 0) signalGroup(pid, "SIGKILL");
|
|
145
|
+
resolve(result);
|
|
146
|
+
};
|
|
147
|
+
child.on("error", (error) => {
|
|
148
|
+
process.stderr.write(`${error.message}\n`);
|
|
149
|
+
finish({
|
|
150
|
+
code: 1,
|
|
151
|
+
signal: null
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
child.on("exit", (code, signal) => {
|
|
155
|
+
const bySignal = signal ? 128 + (SIGNAL_NUMBERS[signal] ?? 0) : 1;
|
|
156
|
+
finish({
|
|
157
|
+
code: code ?? bySignal,
|
|
158
|
+
signal
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
const URL_CREDENTIALS = /\/\/[^/@\s]*@/;
|
|
164
|
+
/**
|
|
165
|
+
* The command as a person would type it, for printing. A database URL carries
|
|
166
|
+
* its password, so the value after `--db-url` (and any URL with credentials)
|
|
167
|
+
* is replaced rather than echoed.
|
|
168
|
+
*/
|
|
169
|
+
function formatCommand(command, args) {
|
|
170
|
+
const shown = [];
|
|
171
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
172
|
+
const arg = args[i];
|
|
173
|
+
if (arg === "--db-url" && i + 1 < args.length) {
|
|
174
|
+
shown.push(arg, "<DB_URL>");
|
|
175
|
+
i += 1;
|
|
176
|
+
} else if (arg.startsWith("--db-url=")) shown.push("--db-url=<DB_URL>");
|
|
177
|
+
else shown.push(arg.replace(URL_CREDENTIALS, "//***@"));
|
|
178
|
+
}
|
|
179
|
+
return [command, ...shown].map((part) => /^[\w@%+=:,./<>*-]+$/.test(part) ? part : JSON.stringify(part)).join(" ");
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Prints the exact command that ran, AFTER it ran so the tool's own output
|
|
183
|
+
* cannot bury it. On stderr, so a tool whose stdout is a pipe stays clean.
|
|
184
|
+
*/
|
|
185
|
+
function reportRan(command, args, result) {
|
|
186
|
+
if (isDryRun()) return;
|
|
187
|
+
const status = result.code === 0 ? "" : ` (exit ${result.code})`;
|
|
188
|
+
process.stderr.write(`Ran: ${formatCommand(command, args)}${status}\n`);
|
|
189
|
+
}
|
|
190
|
+
//#endregion
|
|
191
|
+
//#region ../cli-core/src/db/run.ts
|
|
192
|
+
/**
|
|
193
|
+
* Shared helpers for running supabase CLI and pnpm commands from the repo root.
|
|
194
|
+
*
|
|
195
|
+
* The supabase CLI is a workspace devDependency (not a global install), so
|
|
196
|
+
* every invocation goes through `pnpm exec supabase`. The cwd is always
|
|
197
|
+
* findRepoRoot(), where `supabase/config.toml` lives.
|
|
198
|
+
*/
|
|
199
|
+
const runFile = promisify(execFile);
|
|
200
|
+
/** Spawn a pnpm command with inherited stdio; resolves to the exit code. Pass
|
|
201
|
+
* `env` to run the child against a loaded tier rather than process.env. The
|
|
202
|
+
* child runs in its own process group, so stopping this process stops
|
|
203
|
+
* whatever it started (see `process-group.ts`). */
|
|
204
|
+
async function run(args, env) {
|
|
205
|
+
const result = await runInGroup("pnpm", args, {
|
|
206
|
+
cwd: findRepoRoot(),
|
|
207
|
+
...env ? { env } : {}
|
|
208
|
+
});
|
|
209
|
+
if (reporting) reportRan("pnpm", args, result);
|
|
210
|
+
return result.code;
|
|
211
|
+
}
|
|
212
|
+
let reporting = false;
|
|
213
|
+
/**
|
|
214
|
+
* Makes every `run()` after this print the exact command it ran (on stderr,
|
|
215
|
+
* after it finishes). Off by default, so a deploy log or a piped command is
|
|
216
|
+
* not changed under its caller; the presets turn it on because showing the
|
|
217
|
+
* tool calls behind a button is their whole point.
|
|
218
|
+
*/
|
|
219
|
+
function reportRuns(enabled) {
|
|
220
|
+
reporting = enabled;
|
|
221
|
+
}
|
|
222
|
+
/** Spawn a pnpm command while echoing and retaining stderr for diagnostics.
|
|
223
|
+
* Pass `env` to run the child against a loaded tier rather than process.env. */
|
|
224
|
+
function runWithStderr(args, env) {
|
|
225
|
+
return new Promise((resolve) => {
|
|
226
|
+
const child = spawn("pnpm", args, {
|
|
227
|
+
stdio: [
|
|
228
|
+
"inherit",
|
|
229
|
+
"inherit",
|
|
230
|
+
"pipe"
|
|
231
|
+
],
|
|
232
|
+
cwd: findRepoRoot(),
|
|
233
|
+
...env ? { env } : {}
|
|
234
|
+
});
|
|
235
|
+
let stderr = "";
|
|
236
|
+
child.stderr.setEncoding("utf8");
|
|
237
|
+
child.stderr.on("data", (chunk) => {
|
|
238
|
+
stderr += chunk;
|
|
239
|
+
process.stderr.write(chunk);
|
|
240
|
+
});
|
|
241
|
+
child.on("error", (error) => {
|
|
242
|
+
const message = `${error.message}\n`;
|
|
243
|
+
stderr += message;
|
|
244
|
+
process.stderr.write(message);
|
|
245
|
+
resolve({
|
|
246
|
+
code: 1,
|
|
247
|
+
stderr
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
child.on("exit", (code) => resolve({
|
|
251
|
+
code: code ?? 1,
|
|
252
|
+
stderr
|
|
253
|
+
}));
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
/** Spawn a pnpm command, capture stdout, throw on non-zero exit. */
|
|
257
|
+
async function capture(args) {
|
|
258
|
+
const { stdout } = await runFile("pnpm", args, {
|
|
259
|
+
cwd: findRepoRoot(),
|
|
260
|
+
encoding: "utf8"
|
|
261
|
+
});
|
|
262
|
+
return stdout;
|
|
263
|
+
}
|
|
264
|
+
/** `pnpm exec supabase …` with inherited stdio. */
|
|
265
|
+
const supabase = (...args) => run([
|
|
266
|
+
"exec",
|
|
267
|
+
"supabase",
|
|
268
|
+
...args
|
|
269
|
+
]);
|
|
270
|
+
/** `pnpm exec supabase …` with captured stdout. */
|
|
271
|
+
const supabaseCapture = (...args) => capture([
|
|
272
|
+
"exec",
|
|
273
|
+
"supabase",
|
|
274
|
+
...args
|
|
275
|
+
]);
|
|
276
|
+
/**
|
|
277
|
+
* `supabase db push` over `dbUrl`, inherited stdio, resolving to the exit code.
|
|
278
|
+
*
|
|
279
|
+
* The one spelling of the migration push, shared by the contributor `db
|
|
280
|
+
* migrate` path (`stack.ts`'s `pushMigrations`) and CI's `deploy migrate`.
|
|
281
|
+
* `--yes` is the unattended apply CI wants; the contributor path omits it so a
|
|
282
|
+
* human still confirms. Neither variant regenerates types — that is
|
|
283
|
+
* `pushMigrations`' own second step, layered on top only where a checkout is
|
|
284
|
+
* meant to be rewritten. A CI apply must NOT write back into the repo, which is
|
|
285
|
+
* exactly why the bare push is factored out here rather than reused whole.
|
|
286
|
+
*/
|
|
287
|
+
function dbPush(dbUrl, opts = {}) {
|
|
288
|
+
const args = [
|
|
289
|
+
"db",
|
|
290
|
+
"push",
|
|
291
|
+
"--db-url",
|
|
292
|
+
dbUrl
|
|
293
|
+
];
|
|
294
|
+
if (opts.yes) args.push("--yes");
|
|
295
|
+
return supabase(...args);
|
|
296
|
+
}
|
|
297
|
+
/** Seed storage buckets on the local stack or a resolved tier's project. */
|
|
298
|
+
async function seedBuckets(conn) {
|
|
299
|
+
if (conn.kind === "local") return supabase("seed", "buckets", "--local", "--yes");
|
|
300
|
+
if (!conn.projectRef) {
|
|
301
|
+
process.stderr.write("devtools db: seed buckets against a hosted project needs PROJECT_REF in the session's env file.\n");
|
|
302
|
+
return 1;
|
|
303
|
+
}
|
|
304
|
+
return supabase("seed", "buckets", "--project-ref", conn.projectRef, "--linked");
|
|
305
|
+
}
|
|
306
|
+
//#endregion
|
|
307
|
+
//#region ../cli-core/src/repo/supabase-project.ts
|
|
308
|
+
/**
|
|
309
|
+
* This repo's Supabase project id — read from `supabase/config.toml` — and
|
|
310
|
+
* the container-name conventions the Supabase CLI derives from it.
|
|
311
|
+
*
|
|
312
|
+
* Shared by `environment.ts`'s adaptive-menu probe (does a stack answer for
|
|
313
|
+
* THIS project?) and the foreign-stack detection in `db/generated-env.ts`
|
|
314
|
+
* and `stack.ts` (does a DIFFERENT project's stack hold the shared ports? —
|
|
315
|
+
* the case where `supabase status -o env` fails not because nothing is
|
|
316
|
+
* running, but because what is running on 127.0.0.1:54321 belongs to
|
|
317
|
+
* another workspace, or to this same repo before/after a `project_id`
|
|
318
|
+
* rename). One project-id reader and one container-name parser, so both
|
|
319
|
+
* questions agree on what "this project" means.
|
|
320
|
+
*/
|
|
321
|
+
/**
|
|
322
|
+
* Reads `project_id` out of `<repoRoot>/supabase/config.toml`, or `null`
|
|
323
|
+
* when the file is missing or unparsable.
|
|
324
|
+
*
|
|
325
|
+
* Regex, not a TOML parser: it is one line, the shape has been stable
|
|
326
|
+
* across every CLI version this repo has seen, and a parser on a path some
|
|
327
|
+
* callers pay before their first real subprocess is a cost that stays
|
|
328
|
+
* invisible until it is why the tool feels slow.
|
|
329
|
+
*/
|
|
330
|
+
function readProjectId(repoRoot) {
|
|
331
|
+
try {
|
|
332
|
+
const path = join(repoRoot, "supabase", "config.toml");
|
|
333
|
+
return /^\s*project_id\s*=\s*"([^"]+)"/m.exec(readFileSync(path, "utf8"))?.[1] ?? null;
|
|
334
|
+
} catch {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* The Supabase CLI's container name prefix for a project id, or the bare
|
|
340
|
+
* `supabase_db_` fallback when the id could not be read.
|
|
341
|
+
*
|
|
342
|
+
* Falling back to the bare prefix means a machine whose `config.toml`
|
|
343
|
+
* cannot be read still detects *a* stack, at the cost of over-matching a
|
|
344
|
+
* differently-named project — deliberate, and unchanged from this
|
|
345
|
+
* function's original home in `environment.ts`: over-matching offers
|
|
346
|
+
* `stop`/a foreign-stack hint for a stack that turns out to be this
|
|
347
|
+
* project's own, which is harmless to see and decline, while
|
|
348
|
+
* under-matching hides a real signal entirely.
|
|
349
|
+
*/
|
|
350
|
+
function containerPrefix(projectId) {
|
|
351
|
+
return projectId ? `supabase_db_${projectId}` : "supabase_db_";
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* The local stack's API port, published by its Kong container. Mirrors
|
|
355
|
+
* `@devdogsuga/env/load`'s `LOCAL_STACK_PORT`, which this module cannot import
|
|
356
|
+
* statically (that package is an optional peer, loaded on demand).
|
|
357
|
+
*/
|
|
358
|
+
const STACK_API_PORT = 54321;
|
|
359
|
+
function defaultExec(file, args) {
|
|
360
|
+
try {
|
|
361
|
+
return execFileSync(file, args, {
|
|
362
|
+
encoding: "utf8",
|
|
363
|
+
stdio: [
|
|
364
|
+
"ignore",
|
|
365
|
+
"pipe",
|
|
366
|
+
"ignore"
|
|
367
|
+
],
|
|
368
|
+
timeout: 3e3
|
|
369
|
+
});
|
|
370
|
+
} catch {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* `docker ps --format '{{.Names}}'`, split into trimmed, non-empty names —
|
|
376
|
+
* or `null` when Docker could not be read at all (no daemon, not
|
|
377
|
+
* installed, timed out). With `publish`, only the containers publishing that
|
|
378
|
+
* host port. `execute` is injectable for tests, mirroring `environment.ts`'s
|
|
379
|
+
* own probes.
|
|
380
|
+
*/
|
|
381
|
+
function listContainerNames(publish, execute = defaultExec) {
|
|
382
|
+
const out = execute("docker", [
|
|
383
|
+
"ps",
|
|
384
|
+
...publish === void 0 ? [] : ["--filter", `publish=${publish}`],
|
|
385
|
+
"--format",
|
|
386
|
+
"{{.Names}}"
|
|
387
|
+
]);
|
|
388
|
+
if (out === null) return null;
|
|
389
|
+
return out.split("\n").map((name) => name.trim()).filter((name) => name.length > 0);
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Finds the `supabase_kong_<id>` container among `names` whose id differs
|
|
393
|
+
* from `projectId` — the signature of a stack started from a DIFFERENT
|
|
394
|
+
* Supabase project (this same repo before/after a rename, or another
|
|
395
|
+
* project entirely) holding the shared ports. Kong is the container that
|
|
396
|
+
* publishes the API port, so callers pass the names `listContainerNames`
|
|
397
|
+
* returns for `STACK_API_PORT`: that names the stack actually on 54321,
|
|
398
|
+
* not just any Supabase stack on the machine.
|
|
399
|
+
*
|
|
400
|
+
* Returns that foreign id, or `null` when every matching container belongs
|
|
401
|
+
* to this project, none match at all, or `projectId` itself could not be
|
|
402
|
+
* read — with no known id of our own, there is nothing to compare against,
|
|
403
|
+
* and guessing would risk telling a contributor their OWN stack is
|
|
404
|
+
* foreign.
|
|
405
|
+
*/
|
|
406
|
+
function foreignStackProjectId(names, projectId) {
|
|
407
|
+
if (projectId === null) return null;
|
|
408
|
+
for (const name of names) {
|
|
409
|
+
const match = /^supabase_kong_(.+)$/.exec(name);
|
|
410
|
+
if (!match) continue;
|
|
411
|
+
const id = match[1];
|
|
412
|
+
if (id !== projectId) return id;
|
|
413
|
+
}
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* The actionable message for a detected foreign stack — shared by
|
|
418
|
+
* `db/generated-env.ts` (a failed `supabase status -o env`) and `stack.ts`
|
|
419
|
+
* (a failed `supabase start`), so both name the same fix in the same
|
|
420
|
+
* words.
|
|
421
|
+
*/
|
|
422
|
+
function foreignStackMessage(foreignProjectId) {
|
|
423
|
+
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\`.`;
|
|
424
|
+
}
|
|
425
|
+
//#endregion
|
|
426
|
+
export { dryRunKind as _, listContainerNames as a, setDryRun as b, reportRuns as c, seedBuckets as d, supabase as f, runInGroup as g, reportRan as h, foreignStackProjectId as i, run as l, formatCommand 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, isDryRun as v, wouldRunLine as x, resolveDryRun as y };
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { a as cliName, n as noteError } from "./failure-log-BBPGejQv.js";
|
|
2
|
+
import { a as ownVersion, n as discoverRepoRoot } from "./root-C2OmZDwD.js";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import * as Sentry from "@sentry/node";
|
|
5
|
+
import { buildSentryOptions } from "@devdogsuga/telemetry";
|
|
6
|
+
//#region ../cli-core/src/mode.ts
|
|
7
|
+
/**
|
|
8
|
+
* Non-interactive mode: the one definition both published CLIs share.
|
|
9
|
+
*
|
|
10
|
+
* A run is non-interactive when nobody can answer a prompt: no TTY on stdin,
|
|
11
|
+
* or `CI=true`. In that mode there is no wizard and no banner, output is plain
|
|
12
|
+
* lines (errors on stderr), every confirmation needs `--yes`, the tier must be
|
|
13
|
+
* named (`--tier` or `DEPLOY_ENV`), and Sentry reports the environment `ci`.
|
|
14
|
+
* `--no-env` is the companion switch: it skips loading env files, for a job
|
|
15
|
+
* that supplies its own environment.
|
|
16
|
+
*/
|
|
17
|
+
/** Skip every confirmation. Also the only way past the hosted-tier gate with no terminal. */
|
|
18
|
+
const YES_FLAG = "--yes";
|
|
19
|
+
/** Do not load env files; the caller's environment is the environment. */
|
|
20
|
+
const NO_ENV_FLAG = "--no-env";
|
|
21
|
+
/** `CI=true` (or `1`), the value every CI runner sets. */
|
|
22
|
+
function isCiEnv(env = process.env) {
|
|
23
|
+
return env.CI === "true" || env.CI === "1";
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Whether this run has nobody to ask.
|
|
27
|
+
*
|
|
28
|
+
* Both inputs are injectable for tests; the defaults read the live process.
|
|
29
|
+
*/
|
|
30
|
+
function isNonInteractive(env = process.env, isTTY = process.stdin.isTTY === true) {
|
|
31
|
+
return !isTTY || isCiEnv(env);
|
|
32
|
+
}
|
|
33
|
+
/** Whether `--yes` was typed anywhere in `argv`. */
|
|
34
|
+
function hasYes(argv) {
|
|
35
|
+
return argv.includes(YES_FLAG);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Pulls `--no-env` out of `argv`, wherever it sits, leaving everything else in
|
|
39
|
+
* order. Like `--tier`, it is a global flag the launcher strips before any
|
|
40
|
+
* command sees the rest.
|
|
41
|
+
*/
|
|
42
|
+
function stripNoEnvFlag(argv) {
|
|
43
|
+
const rest = argv.filter((arg) => arg !== NO_ENV_FLAG);
|
|
44
|
+
return {
|
|
45
|
+
noEnv: rest.length !== argv.length,
|
|
46
|
+
rest
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Pulls a global `--tier <t>` out of `argv`, wherever it sits, leaving every
|
|
51
|
+
* other argument untouched and in its original order.
|
|
52
|
+
*
|
|
53
|
+
* A trailing `--tier` with nothing after it removes just the flag; the
|
|
54
|
+
* missing value then reaches tier resolution as `explicit: undefined`, which
|
|
55
|
+
* falls through to `DEPLOY_ENV`/the sole tier/the prompt exactly as if
|
|
56
|
+
* `--tier` had never been typed, rather than this function guessing. A
|
|
57
|
+
* following token that is itself a flag is treated the same way, NOT consumed
|
|
58
|
+
* as the value (the guard every other flag-value reader keeps): without it,
|
|
59
|
+
* `--tier --help` would swallow the flag as a bogus tier and refuse with
|
|
60
|
+
* "unknown tier" instead of reaching the help bypass.
|
|
61
|
+
*/
|
|
62
|
+
function stripTierFlag(argv) {
|
|
63
|
+
const rest = [...argv];
|
|
64
|
+
const index = rest.indexOf("--tier");
|
|
65
|
+
if (index === -1) return {
|
|
66
|
+
explicit: void 0,
|
|
67
|
+
rest
|
|
68
|
+
};
|
|
69
|
+
const value = rest[index + 1];
|
|
70
|
+
const missing = value === void 0 || value.startsWith("-");
|
|
71
|
+
rest.splice(index, missing ? 1 : 2);
|
|
72
|
+
return {
|
|
73
|
+
explicit: missing ? void 0 : value,
|
|
74
|
+
rest
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region ../cli-core/src/telemetry.ts
|
|
79
|
+
/**
|
|
80
|
+
* devtools' own Sentry wiring — the CLI is a consumer of
|
|
81
|
+
* `@devdogsuga/telemetry` like `apps/platform`, `apps/schedule-builder`, and
|
|
82
|
+
* `apps/sandbox`, but on `@sentry/node` rather than a framework SDK: a CLI
|
|
83
|
+
* process starts, runs one command, and exits, with none of a server's
|
|
84
|
+
* request lifecycle for a framework integration to hook into.
|
|
85
|
+
*
|
|
86
|
+
* ## Reporting is on by default
|
|
87
|
+
*
|
|
88
|
+
* Every other `*_SENTRY_DSN` in this org is optional-and-empty until
|
|
89
|
+
* configured (see `@devdogsuga/telemetry`'s no-op-without-DSN contract), and
|
|
90
|
+
* so is this one — empty means `buildSentryOptions` returns `undefined` and
|
|
91
|
+
* `Sentry.init` never runs — but the DEFAULT here is reporting ON once a DSN
|
|
92
|
+
* exists, unlike a feature a contributor opts into. `DEVTOOLS_TELEMETRY=0` is
|
|
93
|
+
* the one escape hatch, checked before `Sentry.init` and before every capture
|
|
94
|
+
* call, so it also works as a kill switch after init already ran (a
|
|
95
|
+
* long-lived `pnpm devtools` menu session, for instance).
|
|
96
|
+
*
|
|
97
|
+
* ## Where the DSN comes from
|
|
98
|
+
*
|
|
99
|
+
* Baked in at build time, not read from the consumer's environment: devtools
|
|
100
|
+
* runs on contributors' machines, where no `.env` could be relied on to carry
|
|
101
|
+
* it. `scripts/write-build-info.mjs` writes `dist/build-info.json` from the
|
|
102
|
+
* build's `DEVTOOLS_SENTRY_DSN`, which Backstage's `publish.yaml` sets from
|
|
103
|
+
* the repo's Actions variable of the same name. Every other build bakes ""
|
|
104
|
+
* and reports nothing. `DEVTOOLS_SENTRY_DSN` in the run-time environment still
|
|
105
|
+
* wins, for pointing a local build at a test project.
|
|
106
|
+
*/
|
|
107
|
+
/** The DSN baked into this build, or "" if there is none (a source run under
|
|
108
|
+
* tsx, or a build made without `DEVTOOLS_SENTRY_DSN`). */
|
|
109
|
+
function bakedSentryDsn() {
|
|
110
|
+
try {
|
|
111
|
+
const info = JSON.parse(readFileSync(new URL("./build-info.json", import.meta.url), "utf8"));
|
|
112
|
+
return typeof info.sentryDsn === "string" ? info.sentryDsn : "";
|
|
113
|
+
} catch {
|
|
114
|
+
return "";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** The run-time `DEVTOOLS_SENTRY_DSN` if set, else the baked one. */
|
|
118
|
+
function resolveDevtoolsDsn(env, baked) {
|
|
119
|
+
return (env.DEVTOOLS_SENTRY_DSN ?? "") || baked;
|
|
120
|
+
}
|
|
121
|
+
let initialized = false;
|
|
122
|
+
/** The id of the last event this process sent to Sentry, if any. */
|
|
123
|
+
let lastEventId;
|
|
124
|
+
/**
|
|
125
|
+
* The Sentry event id of the last error or failure this run reported, or
|
|
126
|
+
* `undefined` when telemetry is off or sent nothing. Printed with the failure
|
|
127
|
+
* log so a maintainer can find the event.
|
|
128
|
+
*/
|
|
129
|
+
function lastSentryEventId() {
|
|
130
|
+
return lastEventId;
|
|
131
|
+
}
|
|
132
|
+
/** Records the id only when a client exists: without one nothing was sent. */
|
|
133
|
+
function sent(id) {
|
|
134
|
+
if (Sentry.getClient()) lastEventId = id;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* `'ci'` whenever the run is non-interactive (no TTY, or `CI=true`, which the
|
|
138
|
+
* runner sets itself before any workflow-authored env), `'local'` on a
|
|
139
|
+
* contributor's terminal.
|
|
140
|
+
* One of `@devdogsuga/telemetry`'s `ENVIRONMENTS`.
|
|
141
|
+
*/
|
|
142
|
+
function devtoolsEnvironment() {
|
|
143
|
+
return isNonInteractive() ? "ci" : "local";
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* `DEVTOOLS_TELEMETRY=0` (any other value, including unset, leaves reporting
|
|
147
|
+
* on) is the only opt-out. Checked by both `initDevtoolsTelemetry` (skips
|
|
148
|
+
* `Sentry.init` outright) and `captureDevtoolsError` (so setting it mid-run,
|
|
149
|
+
* or between two invocations in the same process, still works).
|
|
150
|
+
*/
|
|
151
|
+
function devtoolsTelemetryEnabled() {
|
|
152
|
+
return process.env.DEVTOOLS_TELEMETRY !== "0";
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Whether anyone is actually using devtools in this process. Package-registry
|
|
156
|
+
* scanners install every published version within minutes and run it in a
|
|
157
|
+
* throwaway sandbox (random `DESKTOP-xxxxxx` hosts, Firecracker VMs, a fuzzer
|
|
158
|
+
* that turns `process.exit` into a throw). Left unfiltered, each release filed
|
|
159
|
+
* the same two issues from them. They run with no DevDogsUGA checkout and no
|
|
160
|
+
* terminal. A person has at least one of the two (`setup` runs outside a
|
|
161
|
+
* checkout, but in a terminal), and CI always has a checkout.
|
|
162
|
+
*/
|
|
163
|
+
function hasRealCaller(inRepo, isTTY) {
|
|
164
|
+
return inRepo || isTTY;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Initializes `@sentry/node` for this CLI process. Safe to call more than
|
|
168
|
+
* once (idempotent) and safe to call with no DSN configured (no-ops via
|
|
169
|
+
* `buildSentryOptions`, see its header).
|
|
170
|
+
*
|
|
171
|
+
* `command` becomes a `command` tag on every event this process reports, so
|
|
172
|
+
* an issue in Sentry names the subcommand it came from (`db reset`,
|
|
173
|
+
* `deploy platform`, …) without anyone opening the job log first.
|
|
174
|
+
*/
|
|
175
|
+
function initDevtoolsTelemetry(command) {
|
|
176
|
+
if (initialized) return;
|
|
177
|
+
initialized = true;
|
|
178
|
+
if (!devtoolsTelemetryEnabled()) return;
|
|
179
|
+
if (!hasRealCaller(discoverRepoRoot() !== null, process.stdin.isTTY === true)) return;
|
|
180
|
+
const options = buildSentryOptions({
|
|
181
|
+
service: "devtools",
|
|
182
|
+
environment: devtoolsEnvironment(),
|
|
183
|
+
dsn: resolveDevtoolsDsn(process.env, bakedSentryDsn()),
|
|
184
|
+
release: `${cliName()}@${ownVersion()}`
|
|
185
|
+
});
|
|
186
|
+
if (!options) return;
|
|
187
|
+
Sentry.init(options);
|
|
188
|
+
Sentry.getCurrentScope().setTag("command", command);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Reports an error nothing below the entry points caught — `launch.ts`'s
|
|
192
|
+
* `dispatch`, `launch-ci.ts`, and the bins — then flushes before the process
|
|
193
|
+
* exits. Node's event loop dies with `process.exit`, taking any in-flight
|
|
194
|
+
* request to Sentry's ingest endpoint with it, so the flush must be awaited
|
|
195
|
+
* BEFORE that call — never after.
|
|
196
|
+
*
|
|
197
|
+
* A short timeout: a CLI exiting on error should not hang perceptibly longer
|
|
198
|
+
* because Sentry's ingest is slow or unreachable.
|
|
199
|
+
*/
|
|
200
|
+
async function captureDevtoolsError(err) {
|
|
201
|
+
noteError(err);
|
|
202
|
+
if (!devtoolsTelemetryEnabled()) return;
|
|
203
|
+
sent(Sentry.captureException(err));
|
|
204
|
+
await Sentry.flush(2e3);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Reports an error a command caught and explained to the reader itself
|
|
208
|
+
* (`explainError` in `ui.ts`, `backstage deploy`'s catch), then carried on
|
|
209
|
+
* to a normal exit. Not flushed: the SDK's in-flight request keeps the event
|
|
210
|
+
* loop alive until it lands, so a natural exit waits for it on its own. A
|
|
211
|
+
* path that ends in `process.exit` instead must use `captureDevtoolsError`.
|
|
212
|
+
*/
|
|
213
|
+
function reportDevtoolsError(err) {
|
|
214
|
+
noteError(err);
|
|
215
|
+
if (!devtoolsTelemetryEnabled()) return;
|
|
216
|
+
sent(Sentry.captureException(err));
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Reports a failure that has no error object — a subprocess or deploy step
|
|
220
|
+
* that exited non-zero. Same no-flush contract as `reportDevtoolsError`.
|
|
221
|
+
*/
|
|
222
|
+
function reportDevtoolsFailure(message, extra = {}) {
|
|
223
|
+
noteError(new Error(message));
|
|
224
|
+
if (!devtoolsTelemetryEnabled()) return;
|
|
225
|
+
sent(Sentry.captureMessage(message, {
|
|
226
|
+
level: "error",
|
|
227
|
+
extra
|
|
228
|
+
}));
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Reports a warning-level message under a FIXED fingerprint, then flushes.
|
|
232
|
+
*
|
|
233
|
+
* For the deprecated aliases: every use groups into one Sentry issue per
|
|
234
|
+
* fingerprint, so the issue's last-seen time says when the alias stopped
|
|
235
|
+
* being used and is safe to remove. Flushed because the callers go on to
|
|
236
|
+
* `process.exit`, which would otherwise drop the request in flight. The
|
|
237
|
+
* timeout is short: the command being run is what the person came for.
|
|
238
|
+
*/
|
|
239
|
+
async function captureDevtoolsDeprecation(message, fingerprint, tags) {
|
|
240
|
+
if (!devtoolsTelemetryEnabled()) return;
|
|
241
|
+
Sentry.captureMessage(message, {
|
|
242
|
+
level: "warning",
|
|
243
|
+
fingerprint: [fingerprint],
|
|
244
|
+
tags
|
|
245
|
+
});
|
|
246
|
+
await Sentry.flush(1e3);
|
|
247
|
+
}
|
|
248
|
+
//#endregion
|
|
249
|
+
export { hasRealCaller as a, reportDevtoolsError as c, hasYes as d, isNonInteractive as f, devtoolsTelemetryEnabled as i, reportDevtoolsFailure as l, stripTierFlag as m, captureDevtoolsError as n, initDevtoolsTelemetry as o, stripNoEnvFlag as p, devtoolsEnvironment as r, lastSentryEventId as s, captureDevtoolsDeprecation as t, resolveDevtoolsDsn as u };
|