Przeglądaj źródła

fix(core): preserve session metadata migration identity (#30176)

Aiden Cline 3 miesięcy temu
rodzic
commit
b258a55a6c

+ 1 - 0
packages/core/migration/20260511173437_session-metadata/migration.sql

@@ -0,0 +1 @@
+ALTER TABLE `session` ADD `metadata` text;

+ 0 - 0
packages/core/migration/20260530232709_lovely_romulus/snapshot.json → packages/core/migration/20260511173437_session-metadata/snapshot.json


+ 0 - 1
packages/core/migration/20260530232709_lovely_romulus/migration.sql

@@ -1 +0,0 @@
-ALTER TABLE `session` ADD `metadata` text;

+ 1 - 1
packages/core/src/database/migration.gen.ts

@@ -22,6 +22,6 @@ export const migrations = (
     import("./migration/20260507164347_add_workspace_time"),
     import("./migration/20260507164347_add_workspace_time"),
     import("./migration/20260510033149_session_usage"),
     import("./migration/20260510033149_session_usage"),
     import("./migration/20260511000411_data_migration_state"),
     import("./migration/20260511000411_data_migration_state"),
-    import("./migration/20260530232709_lovely_romulus"),
+    import("./migration/20260511173437_session-metadata"),
   ])
   ])
 ).map((module) => module.default) satisfies DatabaseMigration.Migration[]
 ).map((module) => module.default) satisfies DatabaseMigration.Migration[]

+ 16 - 0
packages/core/src/database/migration/20260511173437_session-metadata.ts

@@ -0,0 +1,16 @@
+import { Effect } from "effect"
+import type { DatabaseMigration } from "../migration"
+
+export default {
+  id: "20260511173437_session-metadata",
+  up(tx) {
+    return Effect.gen(function* () {
+      // This column briefly shipped again under 20260530232709_lovely_romulus.
+      if (
+        (yield* tx.all<{ name: string }>(`PRAGMA table_info(\`session\`)`)).some((column) => column.name === "metadata")
+      )
+        return
+      yield* tx.run(`ALTER TABLE \`session\` ADD \`metadata\` text;`)
+    })
+  },
+} satisfies DatabaseMigration.Migration

+ 0 - 11
packages/core/src/database/migration/20260530232709_lovely_romulus.ts

@@ -1,11 +0,0 @@
-import { Effect } from "effect"
-import type { DatabaseMigration } from "../migration"
-
-export default {
-  id: "20260530232709_lovely_romulus",
-  up(tx) {
-    return Effect.gen(function* () {
-      yield* tx.run(`ALTER TABLE \`session\` ADD \`metadata\` text;`)
-    })
-  },
-} satisfies DatabaseMigration.Migration

+ 39 - 0
packages/core/test/database-migration.test.ts

@@ -7,6 +7,7 @@ import { Effect } from "effect"
 import { sql } from "drizzle-orm"
 import { sql } from "drizzle-orm"
 import { DatabaseMigration } from "@opencode-ai/core/database/migration"
 import { DatabaseMigration } from "@opencode-ai/core/database/migration"
 import sessionUsageMigration from "@opencode-ai/core/database/migration/20260510033149_session_usage"
 import sessionUsageMigration from "@opencode-ai/core/database/migration/20260510033149_session_usage"
+import sessionMetadataMigration from "@opencode-ai/core/database/migration/20260511173437_session-metadata"
 import type { SqlClient as SqlClientService } from "effect/unstable/sql/SqlClient"
 import type { SqlClient as SqlClientService } from "effect/unstable/sql/SqlClient"
 
 
 const run = <A, E>(effect: Effect.Effect<A, E, SqlClientService>) =>
 const run = <A, E>(effect: Effect.Effect<A, E, SqlClientService>) =>
@@ -89,6 +90,44 @@ describe("DatabaseMigration", () => {
     )
     )
   })
   })
 
 
+  test("does not replay a migrated session metadata column", async () => {
+    await run(
+      Effect.gen(function* () {
+        const db = yield* makeDb
+        yield* db.run(sql`CREATE TABLE session (id text PRIMARY KEY, metadata text)`)
+        yield* db.run(
+          sql`CREATE TABLE __drizzle_migrations (id INTEGER PRIMARY KEY, hash text NOT NULL, created_at numeric, name text, applied_at TEXT)`,
+        )
+        yield* db.run(sql`
+          INSERT INTO __drizzle_migrations (hash, created_at, name, applied_at)
+          VALUES ('hash', 1, '20260511173437_session-metadata', ${new Date().toISOString()})
+        `)
+
+        yield* DatabaseMigration.applyOnly(db, [sessionMetadataMigration])
+
+        expect(yield* db.all(sql`SELECT id FROM migration`)).toEqual([{ id: "20260511173437_session-metadata" }])
+      }),
+    )
+  })
+
+  test("accepts the temporary replacement session metadata migration id", async () => {
+    await run(
+      Effect.gen(function* () {
+        const db = yield* makeDb
+        yield* db.run(sql`CREATE TABLE session (id text PRIMARY KEY, metadata text)`)
+        yield* db.run(sql`CREATE TABLE migration (id TEXT PRIMARY KEY, time_completed INTEGER NOT NULL)`)
+        yield* db.run(sql`INSERT INTO migration (id, time_completed) VALUES ('20260530232709_lovely_romulus', 1)`)
+
+        yield* DatabaseMigration.applyOnly(db, [sessionMetadataMigration])
+
+        expect(yield* db.all(sql`SELECT id FROM migration ORDER BY id`)).toEqual([
+          { id: "20260511173437_session-metadata" },
+          { id: "20260530232709_lovely_romulus" },
+        ])
+      }),
+    )
+  })
+
   test("skips drizzle import when migration table already has state", async () => {
   test("skips drizzle import when migration table already has state", async () => {
     await run(
     await run(
       Effect.gen(function* () {
       Effect.gen(function* () {