Compare commits
1 commit
d377756988
...
5e42ee8869
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e42ee8869 |
3 changed files with 70 additions and 7 deletions
10
README.md
10
README.md
|
|
@ -37,10 +37,12 @@ DATABASE_URL=file:./data/dimensionlab.sqlite
|
||||||
|
|
||||||
SQLite files under `data/` are ignored. Drizzle schema lives in
|
SQLite files under `data/` are ignored. Drizzle schema lives in
|
||||||
`src/lib/server/db/schema.ts`; tracked migrations live in `drizzle/`. Runtime
|
`src/lib/server/db/schema.ts`; tracked migrations live in `drizzle/`. Runtime
|
||||||
startup applies the checked-in dashboard migrations before reads or writes. The
|
startup applies the checked-in dashboard migrations before reads or writes. If
|
||||||
current driver is `bun:sqlite`, which keeps this repo installable in the Bun
|
the app is launched from outside the repo tree, set `DASHBOARD_MIGRATIONS_DIR`
|
||||||
workflow. The store boundary is isolated so a later Postgres driver can replace
|
to the tracked migrations directory. The current driver is `bun:sqlite`, which
|
||||||
the SQLite connection without changing the dashboard model or renderer.
|
keeps this repo installable in the Bun workflow. The store boundary is isolated
|
||||||
|
so a later Postgres driver can replace the SQLite connection without changing
|
||||||
|
the dashboard model or renderer.
|
||||||
Stored dashboard documents pass through a version migration boundary before
|
Stored dashboard documents pass through a version migration boundary before
|
||||||
reads or writes; the MVP supports `dashboard.v1` and fails unsupported versions
|
reads or writes; the MVP supports `dashboard.v1` and fails unsupported versions
|
||||||
with an explicit migration error.
|
with an explicit migration error.
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,11 @@ import {
|
||||||
|
|
||||||
const stores: DashboardStore[] = [];
|
const stores: DashboardStore[] = [];
|
||||||
const tempRoots: string[] = [];
|
const tempRoots: string[] = [];
|
||||||
|
const originalCwd = process.cwd();
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.useRealTimers();
|
vi.useRealTimers();
|
||||||
|
process.chdir(originalCwd);
|
||||||
stores.splice(0).forEach((store) => store.close());
|
stores.splice(0).forEach((store) => store.close());
|
||||||
tempRoots.splice(0).forEach((path) => rmSync(path, { force: true, recursive: true }));
|
tempRoots.splice(0).forEach((path) => rmSync(path, { force: true, recursive: true }));
|
||||||
});
|
});
|
||||||
|
|
@ -165,6 +167,25 @@ describe("dashboard persistence store", () => {
|
||||||
seed.id,
|
seed.id,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("applies migrations when launched outside the repository root", async () => {
|
||||||
|
const root = await mkdtemp(join(tmpdir(), "dimensionlab-dashboard-cwd-"));
|
||||||
|
tempRoots.push(root);
|
||||||
|
process.chdir(root);
|
||||||
|
vi.resetModules();
|
||||||
|
const { createDashboardStore: createStore } = await import("./dashboard-store");
|
||||||
|
const store = createStore({
|
||||||
|
databaseUrl: `file:${join(root, "dashboard.sqlite")}`,
|
||||||
|
});
|
||||||
|
stores.push(store);
|
||||||
|
|
||||||
|
const seed = store.seedDashboardIfEmpty(genericDashboardFixture, {
|
||||||
|
actor: "test-seed",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(seed.document.metadata.title).toBe("Operations Console");
|
||||||
|
expect(store.getActiveDashboard()?.currentRevisionId).toBe(seed.id);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function createTestStore() {
|
async function createTestStore() {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,52 @@
|
||||||
import { resolve } from "node:path";
|
import { existsSync } from "node:fs";
|
||||||
|
import { dirname, join, resolve } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
import { migrate } from "drizzle-orm/bun-sqlite/migrator";
|
import { migrate } from "drizzle-orm/bun-sqlite/migrator";
|
||||||
import type { BunSQLiteDatabase } from "drizzle-orm/bun-sqlite";
|
import type { BunSQLiteDatabase } from "drizzle-orm/bun-sqlite";
|
||||||
|
|
||||||
export const DASHBOARD_MIGRATIONS_FOLDER = resolve("drizzle");
|
const MIGRATION_JOURNAL_PATH = join("meta", "_journal.json");
|
||||||
|
|
||||||
export function applyDashboardMigrations(
|
export function applyDashboardMigrations(
|
||||||
database: BunSQLiteDatabase<Record<string, unknown>>,
|
database: BunSQLiteDatabase<Record<string, unknown>>,
|
||||||
migrationsFolder = DASHBOARD_MIGRATIONS_FOLDER,
|
migrationsFolder = resolveDashboardMigrationsFolder(),
|
||||||
) {
|
) {
|
||||||
migrate(database, { migrationsFolder });
|
migrate(database, { migrationsFolder });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveDashboardMigrationsFolder(
|
||||||
|
explicitFolder = process.env.DASHBOARD_MIGRATIONS_DIR,
|
||||||
|
): string {
|
||||||
|
if (explicitFolder) return assertMigrationFolder(resolve(explicitFolder));
|
||||||
|
|
||||||
|
const cwdFolder = findMigrationFolder(process.cwd());
|
||||||
|
if (cwdFolder) return cwdFolder;
|
||||||
|
|
||||||
|
const moduleFolder = findMigrationFolder(dirname(fileURLToPath(import.meta.url)));
|
||||||
|
if (moduleFolder) return moduleFolder;
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
"Unable to locate dashboard Drizzle migrations. Set DASHBOARD_MIGRATIONS_DIR.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function findMigrationFolder(startPath: string): string | null {
|
||||||
|
let currentPath = resolve(startPath);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const candidate = join(currentPath, "drizzle");
|
||||||
|
if (hasMigrationJournal(candidate)) return candidate;
|
||||||
|
|
||||||
|
const parentPath = dirname(currentPath);
|
||||||
|
if (parentPath === currentPath) return null;
|
||||||
|
currentPath = parentPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertMigrationFolder(folder: string): string {
|
||||||
|
if (hasMigrationJournal(folder)) return folder;
|
||||||
|
throw new Error(`Dashboard migrations not found in ${folder}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasMigrationJournal(folder: string): boolean {
|
||||||
|
return existsSync(join(folder, MIGRATION_JOURNAL_PATH));
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue