@devdogsuga/devtools 0.1.4 → 0.1.6
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 +5 -5
- package/dist/build-info.json +3 -0
- package/dist/cli.js +0 -6
- package/dist/commands.js +1 -12
- package/dist/db/introspect.js +9 -0
- package/dist/db/local-env.d.ts +3 -3
- package/dist/db/local-env.js +1 -1
- package/dist/telemetry.d.ts +2 -0
- package/dist/telemetry.js +38 -22
- package/env.ts +5 -21
- package/package.json +4 -4
- package/dist/db/generate-migration.d.ts +0 -1
- package/dist/db/generate-migration.js +0 -67
package/README.md
CHANGED
|
@@ -26,11 +26,11 @@ default, in both `pnpm devtools` and `devtools-ci`:
|
|
|
26
26
|
naming the subcommand that threw (e.g. `db reset`, `deploy platform`).
|
|
27
27
|
Nothing about a successful run is ever sent.
|
|
28
28
|
- **When**: every run, unless `DEVTOOLS_TELEMETRY=0` is set (per-machine or
|
|
29
|
-
per-job opt-out) — or
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
`Sentry.init()` call happens: no network request, no console output.
|
|
29
|
+
per-job opt-out) — or the build has no DSN. The DSN is baked in when
|
|
30
|
+
Backstage's `publish.yaml` builds the package, from the repo's
|
|
31
|
+
`DEVTOOLS_SENTRY_DSN` Actions variable, so only published builds report;
|
|
32
|
+
local builds and `pnpm pack:local` tarballs don't. Either condition means
|
|
33
|
+
no `Sentry.init()` call happens: no network request, no console output.
|
|
34
34
|
- **What's scrubbed**: this CLI touches local `.env` files, so every event
|
|
35
35
|
passes through
|
|
36
36
|
`@devdogsuga/telemetry`'s shared scrubbers before it leaves the process —
|
package/dist/cli.js
CHANGED
|
@@ -26,7 +26,6 @@ import { connectRemoteProject, runStackCommand, } from "./stack.js";
|
|
|
26
26
|
import { runConfigPush } from "./db/config-push.js";
|
|
27
27
|
import { describeDbTarget, isLocalConnection, resolveDbConnection, } from "./db/connection.js";
|
|
28
28
|
import { runDbExec } from "./db/exec.js";
|
|
29
|
-
import { runGenerateMigration } from "./db/generate-migration.js";
|
|
30
29
|
import { runGenerateTypes } from "./db/generate-types.js";
|
|
31
30
|
import { runIntrospect } from "./db/introspect.js";
|
|
32
31
|
import { runNewMigration } from "./db/new-migration.js";
|
|
@@ -609,11 +608,6 @@ async function runDbCommand(rest) {
|
|
|
609
608
|
process.exitCode = code === 0 ? 0 : 1;
|
|
610
609
|
return;
|
|
611
610
|
}
|
|
612
|
-
if (msub === "generate") {
|
|
613
|
-
const code = await runGenerateMigration(flagValue(mrest, "--app"));
|
|
614
|
-
process.exitCode = code === 0 ? 0 : 1;
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
611
|
log.error(msub
|
|
618
612
|
? `devtools db migration: unknown subcommand "${msub}". Try ${subcommandList(["db", "migration"])}.`
|
|
619
613
|
: `devtools db migration: which of ${subcommandList(["db", "migration"])}?`);
|
package/dist/commands.js
CHANGED
|
@@ -839,7 +839,7 @@ const DECLARED_GROUPS = [
|
|
|
839
839
|
// ── repo — files in the repo, no live connection ───────────────
|
|
840
840
|
{
|
|
841
841
|
name: "migration",
|
|
842
|
-
summary: "
|
|
842
|
+
summary: "Empty timestamped migration files, hand-authored.",
|
|
843
843
|
scope: "repo",
|
|
844
844
|
subcommands: [
|
|
845
845
|
{
|
|
@@ -854,17 +854,6 @@ const DECLARED_GROUPS = [
|
|
|
854
854
|
},
|
|
855
855
|
],
|
|
856
856
|
},
|
|
857
|
-
{
|
|
858
|
-
name: "generate",
|
|
859
|
-
summary: "Generate a migration from an app's Drizzle schema drift.",
|
|
860
|
-
options: [
|
|
861
|
-
{
|
|
862
|
-
flag: "--app",
|
|
863
|
-
value: "<slug>",
|
|
864
|
-
summary: "Whose schema. Asked for when absent.",
|
|
865
|
-
},
|
|
866
|
-
],
|
|
867
|
-
},
|
|
868
857
|
],
|
|
869
858
|
},
|
|
870
859
|
// ── endpoint — the session's database ──────────────────────────
|
package/dist/db/introspect.js
CHANGED
|
@@ -28,6 +28,15 @@ const APP_CONFIGS = {
|
|
|
28
28
|
schemaSuffix: "InPlatform",
|
|
29
29
|
crossSchemaImport: 'import { usersInAuth as users, oauthClientsInAuth as oauthClients } from "~/supabase/drizzle/schema"',
|
|
30
30
|
},
|
|
31
|
+
"schedule-builder": {
|
|
32
|
+
configs: ["drizzle-introspection.config.ts", "drizzle.config.ts"],
|
|
33
|
+
schemaFile: "src/server/db/schema/generated/schema.ts",
|
|
34
|
+
schemaSuffix: "InScheduleBuilder",
|
|
35
|
+
// schedule_builder's own tables carry no foreign keys into another
|
|
36
|
+
// schema (its `userId` columns are bare uuids, not FK-constrained to
|
|
37
|
+
// auth.users), so there is nothing to re-inject here.
|
|
38
|
+
crossSchemaImport: null,
|
|
39
|
+
},
|
|
31
40
|
};
|
|
32
41
|
// ── drizzle-kit pull ──────────────────────────────────────────────────────────
|
|
33
42
|
function runDrizzlePull(appDir, configFile, env) {
|
package/dist/db/local-env.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The environment `db introspect`
|
|
3
|
-
*
|
|
2
|
+
* The environment `db introspect` hands to the `drizzle-kit` child process
|
|
3
|
+
* it spawns.
|
|
4
4
|
*
|
|
5
5
|
* ## How this relates to `db/connection.ts`
|
|
6
6
|
*
|
|
@@ -51,7 +51,7 @@ export interface ResolveLocalToolingEnvOptions {
|
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* Returns the environment a local-tooling child process (`drizzle-kit`,
|
|
54
|
-
* spawned by `db introspect`
|
|
54
|
+
* spawned by `db introspect`) should inherit.
|
|
55
55
|
*
|
|
56
56
|
* Normal path (the process went through `launch.ts`, as `pnpm devtools`
|
|
57
57
|
* always does): returns `process.env` unchanged — it already carries
|
package/dist/db/local-env.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { loadEnvLoad } from "../repo/peers.js";
|
|
2
2
|
/**
|
|
3
3
|
* Returns the environment a local-tooling child process (`drizzle-kit`,
|
|
4
|
-
* spawned by `db introspect`
|
|
4
|
+
* spawned by `db introspect`) should inherit.
|
|
5
5
|
*
|
|
6
6
|
* Normal path (the process went through `launch.ts`, as `pnpm devtools`
|
|
7
7
|
* always does): returns `process.env` unchanged — it already carries
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** The run-time `DEVTOOLS_SENTRY_DSN` if set, else the baked one. */
|
|
2
|
+
export declare function resolveDevtoolsDsn(env: NodeJS.ProcessEnv, baked: string): string;
|
|
1
3
|
/**
|
|
2
4
|
* `'ci'` in every GitHub Actions job (`CI` is set by the runner itself,
|
|
3
5
|
* before any workflow-authored env), `'local'` on a contributor's machine.
|
package/dist/telemetry.js
CHANGED
|
@@ -7,29 +7,44 @@
|
|
|
7
7
|
*
|
|
8
8
|
* ## Reporting is on by default
|
|
9
9
|
*
|
|
10
|
-
* Every other `*_SENTRY_DSN` in this
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* devtools` menu session, for instance).
|
|
10
|
+
* Every other `*_SENTRY_DSN` in this org is optional-and-empty until
|
|
11
|
+
* configured (see `@devdogsuga/telemetry`'s no-op-without-DSN contract), and
|
|
12
|
+
* so is this one — empty means `buildSentryOptions` returns `undefined` and
|
|
13
|
+
* `Sentry.init` never runs — but the DEFAULT here is reporting ON once a DSN
|
|
14
|
+
* exists, unlike a feature a contributor opts into. `DEVTOOLS_TELEMETRY=0` is
|
|
15
|
+
* the one escape hatch, checked before `Sentry.init` and before every capture
|
|
16
|
+
* call, so it also works as a kill switch after init already ran (a
|
|
17
|
+
* long-lived `pnpm devtools` menu session, for instance).
|
|
19
18
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
19
|
+
* ## Where the DSN comes from
|
|
20
|
+
*
|
|
21
|
+
* Baked in at build time, not read from the consumer's environment: devtools
|
|
22
|
+
* runs on contributors' machines, where no `.env` could be relied on to carry
|
|
23
|
+
* it. `scripts/write-build-info.mjs` writes `dist/build-info.json` from the
|
|
24
|
+
* build's `DEVTOOLS_SENTRY_DSN`, which Backstage's `publish.yaml` sets from
|
|
25
|
+
* the repo's Actions variable of the same name. Every other build bakes ""
|
|
26
|
+
* and reports nothing. `DEVTOOLS_SENTRY_DSN` in the run-time environment still
|
|
27
|
+
* wins, for pointing a local build at a test project.
|
|
27
28
|
*/
|
|
29
|
+
import { readFileSync } from "node:fs";
|
|
28
30
|
import * as Sentry from "@sentry/node";
|
|
29
31
|
import { buildSentryOptions } from "@devdogsuga/telemetry";
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
import { ownVersion } from "./repo/preflight.js";
|
|
33
|
+
/** The DSN baked into this build, or "" if there is none (a source run under
|
|
34
|
+
* tsx, or a build made without `DEVTOOLS_SENTRY_DSN`). */
|
|
35
|
+
function bakedSentryDsn() {
|
|
36
|
+
try {
|
|
37
|
+
const info = JSON.parse(readFileSync(new URL("./build-info.json", import.meta.url), "utf8"));
|
|
38
|
+
return typeof info.sentryDsn === "string" ? info.sentryDsn : "";
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** The run-time `DEVTOOLS_SENTRY_DSN` if set, else the baked one. */
|
|
45
|
+
export function resolveDevtoolsDsn(env, baked) {
|
|
46
|
+
return env.DEVTOOLS_SENTRY_DSN || baked;
|
|
47
|
+
}
|
|
33
48
|
let initialized = false;
|
|
34
49
|
/**
|
|
35
50
|
* `'ci'` in every GitHub Actions job (`CI` is set by the runner itself,
|
|
@@ -63,12 +78,13 @@ export function initDevtoolsTelemetry(command) {
|
|
|
63
78
|
initialized = true;
|
|
64
79
|
if (!devtoolsTelemetryEnabled())
|
|
65
80
|
return;
|
|
66
|
-
const dsn = process.env.DEVTOOLS_SENTRY_DSN || PLACEHOLDER_DEVTOOLS_SENTRY_DSN;
|
|
67
81
|
const options = buildSentryOptions({
|
|
68
82
|
service: "devtools",
|
|
69
83
|
environment: devtoolsEnvironment(),
|
|
70
|
-
dsn,
|
|
71
|
-
release
|
|
84
|
+
dsn: resolveDevtoolsDsn(process.env, bakedSentryDsn()),
|
|
85
|
+
// The published version, so an issue names the release it came from; a
|
|
86
|
+
// dlx run has no SENTRY_RELEASE of its own.
|
|
87
|
+
release: `devtools@${ownVersion()}`,
|
|
72
88
|
});
|
|
73
89
|
if (!options)
|
|
74
90
|
return;
|
package/env.ts
CHANGED
|
@@ -121,25 +121,10 @@ declare({
|
|
|
121
121
|
secrecy: "public",
|
|
122
122
|
commented: true,
|
|
123
123
|
}),
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
// devtools Sentry project exists; this variable, when set, always wins
|
|
129
|
-
// over that placeholder. `scope: "environment"` because the CI job that
|
|
130
|
-
// reports as `environment: "ci"` needs the same value a contributor's
|
|
131
|
-
// machine (`environment: "local"`) does -- there is no per-deploy-tier
|
|
132
|
-
// split the way `DEPLOY_ENV` has, only per-target routing.
|
|
133
|
-
DEVTOOLS_SENTRY_DSN: define(z.string().url().optional(), {
|
|
134
|
-
doc:
|
|
135
|
-
"Sentry ingest DSN for the devtools project. Optional -- empty " +
|
|
136
|
-
"skips Sentry.init entirely, which is the state before the org is " +
|
|
137
|
-
"onboarded and the state of local development. See " +
|
|
138
|
-
"src/telemetry.ts.",
|
|
139
|
-
scope: "environment",
|
|
140
|
-
secrecy: "public",
|
|
141
|
-
}),
|
|
142
|
-
// Reporting is ON by default once DEVTOOLS_SENTRY_DSN is set -- unlike
|
|
124
|
+
// No DEVTOOLS_SENTRY_DSN here: the DSN is baked in at publish time, not
|
|
125
|
+
// configured by the consumer (see src/telemetry.ts).
|
|
126
|
+
//
|
|
127
|
+
// Reporting is ON by default in a published build -- unlike
|
|
143
128
|
// every other telemetry surface in this workspace, which a consumer
|
|
144
129
|
// opts INTO by configuring a DSN, this one an operator opts OUT of.
|
|
145
130
|
// `"0"` is the one recognized value; anything else, including unset,
|
|
@@ -149,8 +134,7 @@ declare({
|
|
|
149
134
|
DEVTOOLS_TELEMETRY: define(z.string().optional(), {
|
|
150
135
|
doc:
|
|
151
136
|
'Set to "0" to opt out of devtools\' Sentry error reporting (on by ' +
|
|
152
|
-
"default
|
|
153
|
-
"src/telemetry.ts.",
|
|
137
|
+
"default in published builds). See src/telemetry.ts.",
|
|
154
138
|
scope: "developer",
|
|
155
139
|
secrecy: "public",
|
|
156
140
|
}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devdogsuga/devtools",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Contributor CLI for a DevDogsUGA checkout: database, env, images, emails, cron, deploy. Ships built JS; run via `pnpm dlx @devdogsuga/devtools` (or the `devtools`/`devtools-ci` bins once installed) from inside a DevDogsUGA clone.",
|
|
@@ -62,13 +62,13 @@
|
|
|
62
62
|
"@vitest/coverage-v8": "^4.1.11",
|
|
63
63
|
"react": "^19.2.8",
|
|
64
64
|
"vitest": "^4.1.11",
|
|
65
|
-
"@devdogsuga/
|
|
65
|
+
"@devdogsuga/brand": "0.1.1",
|
|
66
66
|
"@devdogsuga/env": "0.1.1",
|
|
67
67
|
"@devdogsuga/db": "0.1.1",
|
|
68
|
-
"@devdogsuga/
|
|
68
|
+
"@devdogsuga/config": "0.1.1"
|
|
69
69
|
},
|
|
70
70
|
"scripts": {
|
|
71
|
-
"build": "tsc",
|
|
71
|
+
"build": "tsc && node scripts/write-build-info.mjs",
|
|
72
72
|
"cli": "node ./bin/devtools.mjs",
|
|
73
73
|
"ci": "node ./dist/ci.js",
|
|
74
74
|
"ci:env": "node ./bin/devtools-ci.mjs",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function runGenerateMigration(appSlug?: string): Promise<number>;
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `db migration generate [--app <slug>]` — draft a migration from an app's
|
|
3
|
-
* Drizzle schema drift.
|
|
4
|
-
*
|
|
5
|
-
* Absorbs schedule-builder's `db:generate` package script (`with-env
|
|
6
|
-
* drizzle-kit generate --config drizzle-migrations.config.ts`), the same way
|
|
7
|
-
* `db introspect` absorbed platform's `db:pull`. It touches no database: it
|
|
8
|
-
* diffs the schema files against drizzle-kit's own snapshot and writes SQL
|
|
9
|
-
* under the app's `drizzle-generated/`, a draft to be carried by hand into a
|
|
10
|
-
* real migration under `supabase/migrations/` — never something that runs on
|
|
11
|
-
* its own. `DB_URL` is still required because drizzle-kit's Postgres
|
|
12
|
-
* introspection needs a live connection to compare against, matching what
|
|
13
|
-
* `with-env` supplied before — sourced via `resolveLocalToolingEnv`, the
|
|
14
|
-
* tier this process already entered rather than a raw `.env` parse. See
|
|
15
|
-
* `db/local-env.ts`'s header.
|
|
16
|
-
*/
|
|
17
|
-
import { spawn } from "node:child_process";
|
|
18
|
-
import { join } from "node:path";
|
|
19
|
-
import { select } from "@clack/prompts";
|
|
20
|
-
import { findRepoRoot } from "../repo/root.js";
|
|
21
|
-
import { unwrap } from "../ui.js";
|
|
22
|
-
import { resolveLocalToolingEnv } from "./local-env.js";
|
|
23
|
-
// ── Per-app config ────────────────────────────────────────────────────────────
|
|
24
|
-
/** Apps whose Drizzle schema drafts migrations via `drizzle-kit generate`. */
|
|
25
|
-
const APP_CONFIGS = {
|
|
26
|
-
"schedule-builder": "drizzle-migrations.config.ts",
|
|
27
|
-
};
|
|
28
|
-
async function pickApp() {
|
|
29
|
-
const names = Object.keys(APP_CONFIGS);
|
|
30
|
-
if (names.length === 1)
|
|
31
|
-
return names[0];
|
|
32
|
-
return unwrap(await select({
|
|
33
|
-
message: "Whose schema drift should I draft a migration from?",
|
|
34
|
-
options: names.map((value) => ({ value, label: value })),
|
|
35
|
-
}));
|
|
36
|
-
}
|
|
37
|
-
function runDrizzleGenerate(appDir, configFile, env) {
|
|
38
|
-
return new Promise((resolve) => {
|
|
39
|
-
const child = spawn("pnpm", ["exec", "drizzle-kit", "generate", "--config", configFile], { stdio: "inherit", cwd: appDir, env });
|
|
40
|
-
child.on("exit", (code) => resolve(code ?? 1));
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
// ── Entry ─────────────────────────────────────────────────────────────────────
|
|
44
|
-
export async function runGenerateMigration(appSlug) {
|
|
45
|
-
const app = appSlug ?? (await pickApp());
|
|
46
|
-
const configFile = APP_CONFIGS[app];
|
|
47
|
-
if (!configFile) {
|
|
48
|
-
const valid = Object.keys(APP_CONFIGS).join(", ");
|
|
49
|
-
process.stderr.write(`devtools db migration generate: unknown app "${app}". Expected: ${valid}.\n`);
|
|
50
|
-
return 1;
|
|
51
|
-
}
|
|
52
|
-
const appDir = join(findRepoRoot(), "apps", app);
|
|
53
|
-
// Matching `db introspect`'s approach: `pnpm devtools` enters a tier into
|
|
54
|
-
// `process.env` before dispatch (`launch.ts`), but drizzle-kit here runs as
|
|
55
|
-
// its own child process in the app's directory, so that entered
|
|
56
|
-
// environment needs to be injected explicitly rather than inherited
|
|
57
|
-
// implicitly.
|
|
58
|
-
const env = await resolveLocalToolingEnv();
|
|
59
|
-
if (!env["DB_URL"]) {
|
|
60
|
-
process.stderr.write("devtools db migration generate: DB_URL is not set. Run " +
|
|
61
|
-
"`pnpm devtools db start` first (for local — its connection string " +
|
|
62
|
-
"lives in .env.generated once the stack is up), or set DB_URL in " +
|
|
63
|
-
"the deploy tier's env file this process entered.\n");
|
|
64
|
-
return 1;
|
|
65
|
-
}
|
|
66
|
-
return runDrizzleGenerate(appDir, configFile, env);
|
|
67
|
-
}
|