@devdogsuga/devtools 0.1.26 → 0.1.28
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
|
@@ -1,429 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `pnpm devtools doctor [--app <slug>] [--report]`
|
|
3
|
-
*
|
|
4
|
-
* The new environment checker. Entirely read-only: it inspects this machine
|
|
5
|
-
* and this checkout's `.env`, and for a hosted Supabase project, makes a
|
|
6
|
-
* couple of network calls to say whether it is reachable — never writes
|
|
7
|
-
* anything. `setup.ts`'s hosted wizard runs the same checks at the end of
|
|
8
|
-
* its own run (see its last step), so a contributor who set up through the
|
|
9
|
-
* wizard and one who ran `doctor` cold see the same report.
|
|
10
|
-
*
|
|
11
|
-
* Every check carries a stable FAQ id from the docs contract's list (see
|
|
12
|
-
* `docs/_shared/getting-started/troubleshooting.md` once written) so a
|
|
13
|
-
* failure message can point at
|
|
14
|
-
* `https://devdogsuga.org/docs/<app>/getting-started/troubleshooting#<id>`
|
|
15
|
-
* rather than explaining itself twice.
|
|
16
|
-
*
|
|
17
|
-
* Pure classification lives in small exported functions so this file's own
|
|
18
|
-
* tests do not have to fake `child_process`/`fs` to prove the logic; the
|
|
19
|
-
* orchestrator (`runEnvironmentDoctor`) is the only part that actually
|
|
20
|
-
* touches the machine.
|
|
21
|
-
*/
|
|
22
|
-
import { execFileSync } from "node:child_process";
|
|
23
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
24
|
-
import { join } from "node:path";
|
|
25
|
-
import { log, note } from "@clack/prompts";
|
|
26
|
-
import { parse as parseDotenv } from "dotenv";
|
|
27
|
-
import { discoverRepoRoot } from "./repo/root.js";
|
|
28
|
-
import { validateSessionPoolerUrl } from "./db/pooler.js";
|
|
29
|
-
const TROUBLESHOOTING_APPS = [
|
|
30
|
-
"schedule-builder",
|
|
31
|
-
"study-group-finder",
|
|
32
|
-
"platform",
|
|
33
|
-
];
|
|
34
|
-
function faqUrl(app, id) {
|
|
35
|
-
return `https://devdogsuga.org/docs/${app}/getting-started/troubleshooting#${id}`;
|
|
36
|
-
}
|
|
37
|
-
// ── Pure checks ──────────────────────────────────────────────────────────────
|
|
38
|
-
/** The floor is 22.12: pnpm 11 needs `node:sqlite`, stable there. `.nvmrc`
|
|
39
|
-
* pins 24, which is what a `fnm use` picks up; a machine below the floor is
|
|
40
|
-
* a WARN, not an error — plenty of contributors' shells still resolve an
|
|
41
|
-
* old global `node` first. */
|
|
42
|
-
export function checkNodeVersion(nodeVersion) {
|
|
43
|
-
const [major = 0, minor = 0] = nodeVersion.split(".").map(Number);
|
|
44
|
-
const ok = major > 22 || (major === 22 && minor >= 12);
|
|
45
|
-
return {
|
|
46
|
-
id: "node-version",
|
|
47
|
-
status: ok ? "ok" : "warn",
|
|
48
|
-
summary: ok
|
|
49
|
-
? `Node ${nodeVersion}`
|
|
50
|
-
: `Node ${nodeVersion} is below the floor (>= 22.12)`,
|
|
51
|
-
fix: ok
|
|
52
|
-
? undefined
|
|
53
|
-
: "Install fnm, then `fnm use` in the repo (reads .nvmrc).",
|
|
54
|
-
faqId: ok ? undefined : "node-version",
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
export function checkFnmHookPresent(shellProfile) {
|
|
58
|
-
const present = shellProfile !== null &&
|
|
59
|
-
(shellProfile.includes("fnm env") || shellProfile.includes("fnm_env"));
|
|
60
|
-
return {
|
|
61
|
-
id: "fnm-not-found",
|
|
62
|
-
status: present ? "ok" : "warn",
|
|
63
|
-
summary: present
|
|
64
|
-
? "fnm hook found in shell profile"
|
|
65
|
-
: "No fnm hook found in your shell profile",
|
|
66
|
-
fix: present
|
|
67
|
-
? undefined
|
|
68
|
-
: "Add fnm's shell hook (`fnm env --use-on-cd --shell <bash|zsh|powershell> | ...`) and restart your terminal.",
|
|
69
|
-
faqId: present ? undefined : "fnm-not-found",
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
export function checkPnpmVersion(pnpmVersion, pinned) {
|
|
73
|
-
if (!pnpmVersion) {
|
|
74
|
-
return {
|
|
75
|
-
id: "pnpm-missing",
|
|
76
|
-
status: "warn",
|
|
77
|
-
summary: "pnpm is not on PATH",
|
|
78
|
-
fix: "`npm install -g pnpm` (never `corepack enable` — this repo does not use corepack).",
|
|
79
|
-
faqId: "pnpm-missing",
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
const pinnedVersion = pinned.replace(/^pnpm@/, "").split("+")[0];
|
|
83
|
-
const ok = pnpmVersion === pinnedVersion;
|
|
84
|
-
return {
|
|
85
|
-
id: "pnpm-version",
|
|
86
|
-
status: ok ? "ok" : "warn",
|
|
87
|
-
summary: ok
|
|
88
|
-
? `pnpm ${pnpmVersion}`
|
|
89
|
-
: `pnpm ${pnpmVersion} does not match the pinned ${pinnedVersion}`,
|
|
90
|
-
fix: ok
|
|
91
|
-
? undefined
|
|
92
|
-
: "pnpm self-switches to the pinned version on its first run in the repo (pmOnFail=download). Run any pnpm command here again.",
|
|
93
|
-
faqId: ok ? undefined : "pnpm-version",
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
/** `git config core.autocrlf` — `input` or `false` is correct on every
|
|
97
|
-
* platform this repo documents (WSL2, native Windows, macOS, Linux); `true`
|
|
98
|
-
* rewrites LF to CRLF on checkout and breaks anything that shells out to a
|
|
99
|
-
* script with a shebang. */
|
|
100
|
-
export function checkAutocrlf(value) {
|
|
101
|
-
const ok = value === "input" || value === "false" || value === null;
|
|
102
|
-
return {
|
|
103
|
-
id: "crlf-line-endings",
|
|
104
|
-
status: ok ? "ok" : "warn",
|
|
105
|
-
summary: ok
|
|
106
|
-
? "core.autocrlf is not rewriting line endings"
|
|
107
|
-
: `core.autocrlf=${value} rewrites LF to CRLF on checkout`,
|
|
108
|
-
fix: ok
|
|
109
|
-
? undefined
|
|
110
|
-
: "`git config --global core.autocrlf input` (or `false`), then re-clone or `git rm --cached -r . && git reset --hard`.",
|
|
111
|
-
faqId: ok ? undefined : "crlf-line-endings",
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
/** WSL2's own filesystem, never `/mnt/c/...` — a Windows-drive checkout
|
|
115
|
-
* makes every file operation cross the 9p boundary and Docker bind mounts
|
|
116
|
-
* silently fail to see edits. */
|
|
117
|
-
export function checkRepoUnderMntC(repoRoot) {
|
|
118
|
-
const ok = !repoRoot.startsWith("/mnt/");
|
|
119
|
-
return {
|
|
120
|
-
id: "repo-under-mnt-c",
|
|
121
|
-
status: ok ? "ok" : "warn",
|
|
122
|
-
summary: ok
|
|
123
|
-
? "Repo is on the Linux filesystem"
|
|
124
|
-
: `Repo is under ${repoRoot} — a Windows drive mounted into WSL2`,
|
|
125
|
-
fix: ok
|
|
126
|
-
? undefined
|
|
127
|
-
: "Clone into your Linux home (~), never /mnt/c/..., and reopen with the VS Code WSL extension.",
|
|
128
|
-
faqId: ok ? undefined : "repo-under-mnt-c",
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
/** Windows only. `RemoteSigned` (or looser) is what lets fnm's PowerShell
|
|
132
|
-
* hook and pnpm's shims actually run. */
|
|
133
|
-
export function checkPowerShellExecutionPolicy(policy) {
|
|
134
|
-
const restricted = policy === "Restricted" || policy === "AllSigned";
|
|
135
|
-
return {
|
|
136
|
-
id: "powershell-execution-policy",
|
|
137
|
-
status: restricted ? "warn" : "ok",
|
|
138
|
-
summary: restricted
|
|
139
|
-
? `Execution policy is ${policy}, which blocks locally-created scripts`
|
|
140
|
-
: `Execution policy is ${policy ?? "unrestricted"}`,
|
|
141
|
-
fix: restricted
|
|
142
|
-
? "`Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`, then restart your terminal."
|
|
143
|
-
: undefined,
|
|
144
|
-
faqId: restricted ? "powershell-execution-policy" : undefined,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* The checkout's development env the way every other command loads it:
|
|
149
|
-
* `.env.generated` (the local stack's connection block, written by `db
|
|
150
|
-
* start`) first and winning, then `.env`. Reading `.env` alone reported a
|
|
151
|
-
* working local-Docker checkout as missing PUBLISHABLE_KEY, and took the
|
|
152
|
-
* blank hosted template's `https://$PROJECT_REF.supabase.co` for a real
|
|
153
|
-
* hosted project that "did not respond".
|
|
154
|
-
*/
|
|
155
|
-
export function readCheckoutEnv(repoRoot) {
|
|
156
|
-
const read = (name) => {
|
|
157
|
-
const path = join(repoRoot, name);
|
|
158
|
-
return existsSync(path) ? parseDotenv(readFileSync(path, "utf8")) : {};
|
|
159
|
-
};
|
|
160
|
-
const generated = Object.fromEntries(Object.entries(read(".env.generated")).filter(([, value]) => value !== ""));
|
|
161
|
-
return { ...read(".env"), ...generated };
|
|
162
|
-
}
|
|
163
|
-
export function checkEnvKeysPresent(env, keys) {
|
|
164
|
-
const missing = keys.filter((k) => !env[k]);
|
|
165
|
-
return {
|
|
166
|
-
id: "env-incomplete",
|
|
167
|
-
status: missing.length === 0 ? "ok" : "warn",
|
|
168
|
-
summary: missing.length === 0
|
|
169
|
-
? ".env has every key this run needs"
|
|
170
|
-
: `.env is missing: ${missing.join(", ")}`,
|
|
171
|
-
fix: missing.length === 0
|
|
172
|
-
? undefined
|
|
173
|
-
: "`pnpm devtools setup` (or `pnpm devtools env init`) fills in the blanks.",
|
|
174
|
-
faqId: missing.length === 0 ? undefined : "env-incomplete",
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
export { validateSessionPoolerUrl };
|
|
178
|
-
// ── Orchestrator ─────────────────────────────────────────────────────────────
|
|
179
|
-
function has(cmd, args) {
|
|
180
|
-
try {
|
|
181
|
-
return execFileSync(cmd, args, {
|
|
182
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
183
|
-
encoding: "utf8",
|
|
184
|
-
}).trim();
|
|
185
|
-
}
|
|
186
|
-
catch {
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
function gitConfig(key) {
|
|
191
|
-
return has("git", ["config", "--get", key]);
|
|
192
|
-
}
|
|
193
|
-
function readNvmrc(repoRoot) {
|
|
194
|
-
try {
|
|
195
|
-
return readFileSync(join(repoRoot, ".nvmrc"), "utf8").trim();
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
198
|
-
return null;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
function readPackageManagerPin(repoRoot) {
|
|
202
|
-
try {
|
|
203
|
-
const pkg = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8"));
|
|
204
|
-
return pkg.packageManager ?? null;
|
|
205
|
-
}
|
|
206
|
-
catch {
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
function readShellProfile() {
|
|
211
|
-
const home = process.env.HOME;
|
|
212
|
-
if (!home)
|
|
213
|
-
return null;
|
|
214
|
-
for (const file of [
|
|
215
|
-
".bashrc",
|
|
216
|
-
".zshrc",
|
|
217
|
-
".config/fish/config.fish",
|
|
218
|
-
".bash_profile",
|
|
219
|
-
]) {
|
|
220
|
-
const path = join(home, file);
|
|
221
|
-
if (existsSync(path)) {
|
|
222
|
-
try {
|
|
223
|
-
return readFileSync(path, "utf8");
|
|
224
|
-
}
|
|
225
|
-
catch {
|
|
226
|
-
// try the next one
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
return null;
|
|
231
|
-
}
|
|
232
|
-
function renderCheck(app, check) {
|
|
233
|
-
const icon = check.status === "ok" ? "OK " : check.status === "skip" ? "SKIP" : "WARN";
|
|
234
|
-
const lines = [`${icon} ${check.summary}`];
|
|
235
|
-
if (check.status === "warn") {
|
|
236
|
-
if (check.fix)
|
|
237
|
-
lines.push(` fix: ${check.fix}`);
|
|
238
|
-
if (check.faqId)
|
|
239
|
-
lines.push(` ${faqUrl(app, check.faqId)}`);
|
|
240
|
-
}
|
|
241
|
-
return lines.join("\n");
|
|
242
|
-
}
|
|
243
|
-
export async function runEnvironmentDoctor(opts) {
|
|
244
|
-
const app = opts.app && TROUBLESHOOTING_APPS.includes(opts.app) ? opts.app : "platform";
|
|
245
|
-
const repoRoot = discoverRepoRoot();
|
|
246
|
-
const checks = [];
|
|
247
|
-
const nodeCheck = checkNodeVersion(process.versions.node);
|
|
248
|
-
checks.push(nodeCheck);
|
|
249
|
-
// Only worth raising when Node is wrong: nvm, Volta or a system Node that
|
|
250
|
-
// already satisfies the floor is fine, and flagging a missing fnm hook there
|
|
251
|
-
// is a false alarm.
|
|
252
|
-
if (nodeCheck.status !== "ok") {
|
|
253
|
-
checks.push(checkFnmHookPresent(readShellProfile()));
|
|
254
|
-
}
|
|
255
|
-
else {
|
|
256
|
-
const pinnedMajor = repoRoot ? readNvmrc(repoRoot)?.split(".")[0] : null;
|
|
257
|
-
const major = process.versions.node.split(".")[0];
|
|
258
|
-
if (pinnedMajor && /^\d+$/.test(pinnedMajor) && pinnedMajor !== major) {
|
|
259
|
-
checks.push({
|
|
260
|
-
id: "node-version",
|
|
261
|
-
status: "warn",
|
|
262
|
-
summary: `Node ${process.versions.node} works, but the repo pins Node ${pinnedMajor} (.nvmrc)`,
|
|
263
|
-
fix: "Run `fnm install && fnm use` in the repo.",
|
|
264
|
-
faqId: "node-version",
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
const pnpmVersion = has("pnpm", ["--version"]);
|
|
269
|
-
const pin = repoRoot ? readPackageManagerPin(repoRoot) : null;
|
|
270
|
-
checks.push(pin
|
|
271
|
-
? checkPnpmVersion(pnpmVersion, pin)
|
|
272
|
-
: {
|
|
273
|
-
id: "pnpm-version",
|
|
274
|
-
status: pnpmVersion ? "ok" : "warn",
|
|
275
|
-
summary: pnpmVersion ? `pnpm ${pnpmVersion}` : "pnpm is not on PATH",
|
|
276
|
-
fix: pnpmVersion
|
|
277
|
-
? undefined
|
|
278
|
-
: "`npm install -g pnpm` (never `corepack enable`).",
|
|
279
|
-
faqId: pnpmVersion ? undefined : "pnpm-missing",
|
|
280
|
-
});
|
|
281
|
-
checks.push(checkAutocrlf(gitConfig("core.autocrlf")));
|
|
282
|
-
if (repoRoot)
|
|
283
|
-
checks.push(checkRepoUnderMntC(repoRoot));
|
|
284
|
-
if (process.platform === "win32") {
|
|
285
|
-
checks.push(checkPowerShellExecutionPolicy(has("powershell", ["-NoProfile", "-Command", "Get-ExecutionPolicy"])));
|
|
286
|
-
}
|
|
287
|
-
// Local Docker stack — only relevant when the checkout has a local
|
|
288
|
-
// `.env.generated`, the signature of `db start` having run at least once.
|
|
289
|
-
if (repoRoot && existsSync(join(repoRoot, ".env.generated"))) {
|
|
290
|
-
const dockerUp = has("docker", ["info"]) !== null;
|
|
291
|
-
checks.push({
|
|
292
|
-
id: "docker-not-running",
|
|
293
|
-
status: dockerUp ? "ok" : "warn",
|
|
294
|
-
summary: dockerUp
|
|
295
|
-
? "Docker is running"
|
|
296
|
-
: "Docker daemon is not answering",
|
|
297
|
-
fix: dockerUp
|
|
298
|
-
? undefined
|
|
299
|
-
: "Start Docker Desktop, or Docker Engine in WSL2. Never Colima.",
|
|
300
|
-
faqId: dockerUp ? undefined : "docker-not-running",
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
if (!opts.app || opts.app === "study-group-finder") {
|
|
304
|
-
const flutter = has("flutter", ["--version"]) !== null;
|
|
305
|
-
checks.push({
|
|
306
|
-
id: "flutter-missing",
|
|
307
|
-
status: flutter ? "ok" : "skip",
|
|
308
|
-
summary: flutter ? "Flutter is installed" : "Flutter is not installed",
|
|
309
|
-
fix: flutter
|
|
310
|
-
? undefined
|
|
311
|
-
: "Install the Flutter SDK for study-group-finder.",
|
|
312
|
-
faqId: flutter ? undefined : "flutter-missing",
|
|
313
|
-
});
|
|
314
|
-
if (flutter) {
|
|
315
|
-
const devices = has("adb", ["devices"]);
|
|
316
|
-
const hasDevice = Boolean(devices
|
|
317
|
-
?.split("\n")
|
|
318
|
-
.slice(1)
|
|
319
|
-
.some((line) => line.trim().endsWith("device")));
|
|
320
|
-
checks.push({
|
|
321
|
-
id: "adb-no-devices",
|
|
322
|
-
status: hasDevice ? "ok" : "skip",
|
|
323
|
-
summary: hasDevice
|
|
324
|
-
? "adb sees a device or emulator"
|
|
325
|
-
: "adb sees no device or emulator",
|
|
326
|
-
fix: hasDevice
|
|
327
|
-
? undefined
|
|
328
|
-
: "Start an Android Studio emulator (or plug in a device). On WSL2 with a Windows emulator, run `adb kill-server` on Windows first.",
|
|
329
|
-
faqId: hasDevice ? undefined : "adb-no-devices",
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
let env = {};
|
|
334
|
-
if (repoRoot) {
|
|
335
|
-
env = readCheckoutEnv(repoRoot);
|
|
336
|
-
checks.push(checkEnvKeysPresent(env, ["API_URL", "PUBLISHABLE_KEY"]));
|
|
337
|
-
}
|
|
338
|
-
// ── Hosted checks — only when .env looks like a hosted project ───────────
|
|
339
|
-
const isHosted = Boolean(env.API_URL &&
|
|
340
|
-
!env.API_URL.includes("127.0.0.1") &&
|
|
341
|
-
!env.API_URL.includes("localhost"));
|
|
342
|
-
if (isHosted && env.API_URL) {
|
|
343
|
-
let reachable = false;
|
|
344
|
-
let probeStatus = null;
|
|
345
|
-
try {
|
|
346
|
-
const res = await fetch(`${env.API_URL}/auth/v1/settings`, {
|
|
347
|
-
signal: AbortSignal.timeout(5000),
|
|
348
|
-
});
|
|
349
|
-
probeStatus = res.status;
|
|
350
|
-
reachable = res.status < 500;
|
|
351
|
-
}
|
|
352
|
-
catch {
|
|
353
|
-
reachable = false;
|
|
354
|
-
}
|
|
355
|
-
checks.push({
|
|
356
|
-
id: "supabase-unreachable",
|
|
357
|
-
status: reachable ? "ok" : "warn",
|
|
358
|
-
summary: reachable
|
|
359
|
-
? "The hosted project answers"
|
|
360
|
-
: "The hosted project's API URL did not respond",
|
|
361
|
-
fix: reachable
|
|
362
|
-
? undefined
|
|
363
|
-
: "Check the API URL in .env, and that the project is not paused.",
|
|
364
|
-
faqId: reachable ? undefined : "supabase-unreachable",
|
|
365
|
-
});
|
|
366
|
-
if (probeStatus === 503 || probeStatus === 522 || probeStatus === 540) {
|
|
367
|
-
checks.push({
|
|
368
|
-
id: "supabase-paused",
|
|
369
|
-
status: "warn",
|
|
370
|
-
summary: "The project looks paused",
|
|
371
|
-
fix: "Unpause it from the Supabase dashboard (free tier pauses after ~1 week idle).",
|
|
372
|
-
faqId: "supabase-paused",
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
if (reachable && env.SECRET_KEY) {
|
|
376
|
-
let keysValid = false;
|
|
377
|
-
try {
|
|
378
|
-
const res = await fetch(`${env.API_URL}/auth/v1/admin/users?page=1&per_page=1`, {
|
|
379
|
-
headers: {
|
|
380
|
-
Authorization: `Bearer ${env.SECRET_KEY}`,
|
|
381
|
-
apikey: env.SECRET_KEY,
|
|
382
|
-
},
|
|
383
|
-
signal: AbortSignal.timeout(5000),
|
|
384
|
-
});
|
|
385
|
-
keysValid = res.status !== 401 && res.status !== 403;
|
|
386
|
-
}
|
|
387
|
-
catch {
|
|
388
|
-
keysValid = false;
|
|
389
|
-
}
|
|
390
|
-
checks.push({
|
|
391
|
-
id: "supabase-keys-invalid",
|
|
392
|
-
status: keysValid ? "ok" : "warn",
|
|
393
|
-
summary: keysValid
|
|
394
|
-
? "SECRET_KEY is accepted"
|
|
395
|
-
: "SECRET_KEY was rejected",
|
|
396
|
-
fix: keysValid
|
|
397
|
-
? undefined
|
|
398
|
-
: "Copy the service_role secret key again from Project Settings -> API.",
|
|
399
|
-
faqId: keysValid ? undefined : "supabase-keys-invalid",
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
if (env.DB_URL) {
|
|
404
|
-
const validation = validateSessionPoolerUrl(env.DB_URL);
|
|
405
|
-
checks.push({
|
|
406
|
-
id: validation.faqId ?? "db-url-direct-connection",
|
|
407
|
-
status: validation.ok ? "ok" : "warn",
|
|
408
|
-
summary: validation.ok
|
|
409
|
-
? "DB_URL is a Session pooler string"
|
|
410
|
-
: (validation.message ?? "DB_URL is not a Session pooler string"),
|
|
411
|
-
fix: validation.ok ? undefined : validation.message,
|
|
412
|
-
faqId: validation.faqId,
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
note(checks.map((c) => renderCheck(app, c)).join("\n\n"), opts.app ? `Doctor — ${opts.app}` : "Doctor");
|
|
416
|
-
const warnings = checks.filter((c) => c.status === "warn").length;
|
|
417
|
-
if (warnings === 0)
|
|
418
|
-
log.success("Everything checked out.");
|
|
419
|
-
else
|
|
420
|
-
log.warn(`${warnings} check${warnings === 1 ? "" : "s"} need attention.`);
|
|
421
|
-
if (opts.report) {
|
|
422
|
-
const redacted = [
|
|
423
|
-
`OS: ${process.platform} ${process.arch}`,
|
|
424
|
-
`Node: ${process.versions.node}`,
|
|
425
|
-
...checks.map((c) => `${c.status.toUpperCase().padEnd(4)} ${c.id}: ${c.summary}`),
|
|
426
|
-
].join("\n");
|
|
427
|
-
note(redacted, "Paste this in Discord");
|
|
428
|
-
}
|
|
429
|
-
}
|
package/dist/environment.d.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import type { Condition } from "./commands.js";
|
|
2
|
-
import { findRepoRoot } from "./repo/root.js";
|
|
3
|
-
/**
|
|
4
|
-
* The repo root, which is where `supabase/config.toml` and `.env` live.
|
|
5
|
-
*
|
|
6
|
-
* Used to be a fixed `join(import.meta.dirname, "..", "..", "..")` — that
|
|
7
|
-
* only worked because devtools lived inside the repo it operated on. Now
|
|
8
|
-
* published as its own package, every former reader of that constant calls
|
|
9
|
-
* `findRepoRoot()` (re-exported here for callers that already import from
|
|
10
|
-
* this module) — see `repo/root.ts` for the marker and why it is a lazy,
|
|
11
|
-
* throwing function rather than a constant.
|
|
12
|
-
*/
|
|
13
|
-
export { findRepoRoot };
|
|
14
|
-
/** A fact that may be unreadable. See the header: unknown is not "no". */
|
|
15
|
-
export type Known = "yes" | "no" | "unknown";
|
|
16
|
-
export interface Environment {
|
|
17
|
-
/** The Docker daemon answers. Without it there is no Supabase on this machine. */
|
|
18
|
-
docker: Known;
|
|
19
|
-
/** The local Supabase stack's containers are up. */
|
|
20
|
-
stack: Known;
|
|
21
|
-
/** The root `.env` exists, so `setup` has been run at least once. */
|
|
22
|
-
envFile: Known;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* The environment that adapts nothing.
|
|
26
|
-
*
|
|
27
|
-
* What the tests use, and what any caller that would rather not probe can
|
|
28
|
-
* pass. Because nothing is hidden or flagged on `"unknown"`, this is exactly
|
|
29
|
-
* the pre-adaptation menu, which makes it a safe fallback rather than a
|
|
30
|
-
* special case to remember.
|
|
31
|
-
*/
|
|
32
|
-
export declare const UNKNOWN_ENVIRONMENT: Environment;
|
|
33
|
-
/**
|
|
34
|
-
* Reads the machine.
|
|
35
|
-
*
|
|
36
|
-
* Two subprocesses at worst, one when Docker is down, plus an `existsSync`.
|
|
37
|
-
* The budget is "cheaper than the first keystroke", because this runs before
|
|
38
|
-
* the wizard's first screen and anything slower would be paid on every
|
|
39
|
-
* invocation to save a few of them later.
|
|
40
|
-
*
|
|
41
|
-
* It does NOT call `supabase status`, which is the authoritative answer but
|
|
42
|
-
* takes the better part of a second: it shells out through `pnpm exec` and
|
|
43
|
-
* then talks to every service in the stack. `docker ps` answers the only
|
|
44
|
-
* question the menu has (is it up?) in a fraction of that, and the commands
|
|
45
|
-
* that need real credentials go through `resolveInstance` (`instance.ts`),
|
|
46
|
-
* which reads the session's already-entered env at the moment it matters.
|
|
47
|
-
*/
|
|
48
|
-
export declare function probeEnvironment(execute?: (file: string, args: string[]) => string | null): Environment;
|
|
49
|
-
/**
|
|
50
|
-
* Whether a `Condition` from the command tree currently holds.
|
|
51
|
-
*
|
|
52
|
-
* The tree declares conditions as strings so that it stays inert data the docs
|
|
53
|
-
* build can render (see `commands.ts`); this is the one place that knows what
|
|
54
|
-
* they mean. `"unknown"` propagates rather than collapsing to false, and both
|
|
55
|
-
* callers below read it as "do nothing".
|
|
56
|
-
*/
|
|
57
|
-
export declare function holds(condition: Condition, env: Environment): Known;
|
|
58
|
-
/**
|
|
59
|
-
* Whether the wizard should offer this command at all.
|
|
60
|
-
*
|
|
61
|
-
* False only for a command whose `when` is definitively unmet: `stop`
|
|
62
|
-
* against a stack that is already stopped. This is reserved for commands that
|
|
63
|
-
* would be *meaningless*, never merely inconvenient: one that would fail with
|
|
64
|
-
* a good error message stays in the menu and carries `needs` instead, because
|
|
65
|
-
* a contributor who cannot find a command they know exists is worse off than
|
|
66
|
-
* one who runs it and is told why it did not work.
|
|
67
|
-
*
|
|
68
|
-
* Hiding is a wizard-only decision. `--help` still lists these, the dispatcher
|
|
69
|
-
* still accepts them, and the generated reference still documents them. The
|
|
70
|
-
* menu is a guide to right now, and those three are the reference.
|
|
71
|
-
*/
|
|
72
|
-
export declare function isOffered(node: {
|
|
73
|
-
when?: Condition;
|
|
74
|
-
}, env: Environment): boolean;
|
|
75
|
-
/**
|
|
76
|
-
* Why this command will not work right now, or `null` if nothing is in the way.
|
|
77
|
-
*
|
|
78
|
-
* Appended to the entry's hint, so the menu says "needs Docker, which is not
|
|
79
|
-
* running" on the line itself, rather than after the reader has chosen it and
|
|
80
|
-
* waited for a connection attempt to fail.
|
|
81
|
-
*/
|
|
82
|
-
export declare function blockedBecause(node: {
|
|
83
|
-
needs?: Condition;
|
|
84
|
-
}, env: Environment): string | null;
|
|
85
|
-
/**
|
|
86
|
-
* The three facts, as the wizard's opening note and as `status --local`.
|
|
87
|
-
*
|
|
88
|
-
* Printed before the first question rather than discovered through failures:
|
|
89
|
-
* "Docker running no" at the top of the screen is the whole explanation for
|
|
90
|
-
* why the database commands below it are flagged, and it costs one glance.
|
|
91
|
-
*/
|
|
92
|
-
export declare function describeEnvironment(env: Environment): string;
|