59 lines
2.1 KiB
TypeScript
59 lines
2.1 KiB
TypeScript
export * as DatabaseMigration from "./migration"
|
|
|
|
import { sql } from "drizzle-orm"
|
|
import { Effect, Semaphore } from "effect"
|
|
import type { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
|
|
import { migrations } from "./migration.gen"
|
|
|
|
type Database = EffectDrizzleSqlite.EffectSQLiteDatabase
|
|
type Transaction = Parameters<Parameters<Database["transaction"]>[0]>[0]
|
|
const lock = Semaphore.makeUnsafe(1)
|
|
|
|
export type Migration = {
|
|
id: string
|
|
up: (tx: Transaction) => Effect.Effect<void, unknown>
|
|
}
|
|
|
|
export function apply(db: Database) {
|
|
return lock.withPermit(applyOnly(db, migrations))
|
|
}
|
|
|
|
export function applyOnly(db: Database, input: Migration[]) {
|
|
return Effect.gen(function* () {
|
|
yield* db.run(
|
|
sql`CREATE TABLE IF NOT EXISTS ${sql.identifier("migration")} (id TEXT PRIMARY KEY, time_completed INTEGER NOT NULL)`,
|
|
)
|
|
let completed = new Set(
|
|
(yield* db.all<{ id: string }>(sql`SELECT id FROM ${sql.identifier("migration")}`)).map((row) => row.id),
|
|
)
|
|
if (completed.size === 0) {
|
|
// Existing installs used Drizzle's migration journal. Seed the new
|
|
// journal once so TypeScript migrations don't replay old SQL.
|
|
if (
|
|
yield* db.get(sql`SELECT name FROM sqlite_master WHERE type = 'table' AND name = ${"__drizzle_migrations"}`)
|
|
) {
|
|
yield* db.run(sql`
|
|
INSERT OR IGNORE INTO ${sql.identifier("migration")} (id, time_completed)
|
|
SELECT name, ${Date.now()}
|
|
FROM ${sql.identifier("__drizzle_migrations")}
|
|
WHERE name IS NOT NULL
|
|
`)
|
|
completed = new Set(
|
|
(yield* db.all<{ id: string }>(sql`SELECT id FROM ${sql.identifier("migration")}`)).map((row) => row.id),
|
|
)
|
|
}
|
|
}
|
|
|
|
for (const migration of input) {
|
|
if (completed.has(migration.id)) continue
|
|
yield* db.transaction((tx) =>
|
|
Effect.gen(function* () {
|
|
if (!process.env.OPENCODE_SKIP_MIGRATIONS) yield* migration.up(tx)
|
|
yield* tx.run(
|
|
sql`INSERT INTO ${sql.identifier("migration")} (id, time_completed) VALUES (${migration.id}, ${Date.now()})`,
|
|
)
|
|
}),
|
|
)
|
|
}
|
|
})
|
|
}
|