From 9af52fd7547f6a8ba3b86a4874cb7b6d380f7610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sat, 15 Aug 2026 14:18:47 +0000 Subject: [PATCH] db:push regenerates the plugin schema barrel, so setup works on a clean machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `officer_db/src/schema.ts` ends with an UNCONDITIONAL `export * from './plugin-schemas.gen'`. That file is gitignored — it describes this machine, not the project — and until now its only writer was installPlugin. So on a fresh clone the file does not exist and `bun db:push` dies with MODULE_NOT_FOUND before creating a single table. That is exactly what officer-setup.sh section 8 runs, so setup failed at the schema step on any clean machine, and the only thing that would have written the file was installing a plugin — which needs a working database. A deadlock, shipped with the plugin system on 2026-08-15. Reproduced by deleting the file and running push, not inferred. `db:push` now runs scripts/gen-plugin-schemas.ts first. Wired into the script rather than added as a setup step because setup is not the only caller: pushSchema() shells out to the same command, a developer types it by hand, and `git clean -xfd` removes the file at any time. A step someone has to remember is one they forget once; push regenerating it means the barrel cannot be stale when drizzle reads it. Both paths verified: barrel deleted then push (regenerates, "No changes detected"), and zero plugins on disk (writes a header-only barrel that schema.ts still imports cleanly). Also verified, on a scratch database, the premise the whole barrel design rests on — that push drops what it cannot see. Full schema pushed, a row seeded into music_favorites, then an empty barrel pushed: music_* and headscale_servers were DROPPED along with the row. So "following the directory rather than the install table" is load-bearing, not decorative. Worth recording how close that came to being logged as the opposite. An earlier run with an empty barrel reported "No changes detected", which read as evidence that push does not drop. It was this fix working — the script had regenerated the barrel before drizzle ran. The scratch database is what told them apart. bunx tsgo clean. 797 tests, 787 pass, same 7 pre-existing failures. Scratch database dropped; live plugin tables and rows verified intact. --- package.json | 2 +- scripts/gen-plugin-schemas.ts | 40 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 scripts/gen-plugin-schemas.ts diff --git a/package.json b/package.json index e2e11a57..7e7f4378 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "build:dashboard": "bun run ./scripts/build/dashboard.ts", "build:landing": "bun run ./scripts/build/landing.ts", "db:gen": "cd src/databases/officer_db && bun run generate", - "db:push": "cd src/databases/officer_db && bun run push", + "db:push": "bun run scripts/gen-plugin-schemas.ts && cd src/databases/officer_db && bun run push", "db:migrate": "cd src/databases/officer_db && bun run migrate", "dev:emailer": "cd src/workspaces/emailer && bun run dev", "format": "{ git diff --name-only HEAD -- 'src/**/*.ts' 'src/**/*.tsx'; git ls-files --others --exclude-standard -- 'src/**/*.ts' 'src/**/*.tsx'; } | xargs -r prettier --write", diff --git a/scripts/gen-plugin-schemas.ts b/scripts/gen-plugin-schemas.ts new file mode 100644 index 00000000..dc9c2eb5 --- /dev/null +++ b/scripts/gen-plugin-schemas.ts @@ -0,0 +1,40 @@ +import { discoverPlugins } from '../src/servers/plugins/discover'; +import { generatePluginSchemas } from '../src/servers/plugins/schema'; + +// Write `plugin-schemas.gen.ts` from whatever plugins are on disk. Runs as the first half of `db:push`. +// +// ── The bug this closes ── +// +// `officer_db/src/schema.ts` ends with `export * from './plugin-schemas.gen'` — UNCONDITIONAL, because +// drizzle-kit needs one file listing every table. That generated file is gitignored (it describes this +// machine, not the project), and until 2026-08-15 its only writer was `installPlugin`. +// +// So on a fresh clone the file did not exist, and `bun db:push` died with MODULE_NOT_FOUND before +// creating a single table. That is exactly what `scripts/setup/officer-setup.sh` section 8 runs, so +// setup failed at the schema step on any clean machine — and the only thing that would have written the +// file was installing a plugin, which needs a working database. A deadlock, shipped. +// +// It is wired into the `db:push` SCRIPT rather than added as a setup step on purpose. Setup is not the +// only caller: `pushSchema()` shells out to the same command, a developer types it by hand, and +// `git clean -xfd` removes the file at any time. A step someone has to remember is one they only forget +// once — the barrel now cannot be stale when push reads it, because push regenerates it. +// +// Empty is a correct answer, and the common one: a machine with no plugins gets a barrel with no +// exports, which is what `schema.ts` needs in order to import it at all. + +const { plugins, broken } = await discoverPlugins(); + +// Reported, never thrown. A plugin with an unreadable manifest must not stop the platform's own tables +// from being created — the same rule discovery follows everywhere else. +for (const { appName, error } of broken) { + console.warn(`[plugin-schemas] skipping ${appName}: ${error}`); +} + +const withSchema = plugins.filter((p) => p.schema); +generatePluginSchemas(plugins); + +console.log( + withSchema.length + ? `[plugin-schemas] ${withSchema.length} plugin schema(s): ${withSchema.map((p) => p.appName).join(', ')}` + : '[plugin-schemas] no plugin schemas on disk — wrote an empty barrel', +);