refactor(core): move database schema ownership (#29068)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
Dax
2026-05-30 21:08:38 -04:00
committed by GitHub
parent 6bcb9cb9bb
commit 7f571d36ea
390 changed files with 11127 additions and 9164 deletions

View File

@@ -1,9 +1,8 @@
import { $ } from "bun"
import * as Observability from "@opencode-ai/core/effect/observability"
import * as fs from "fs/promises"
import os from "os"
import path from "path"
import { Effect, Context, Layer, ManagedRuntime } from "effect"
import { Effect, Context, Layer } from "effect"
import type * as PlatformError from "effect/PlatformError"
import type * as Scope from "effect/Scope"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
@@ -18,35 +17,31 @@ import { TestLLMServer } from "../lib/llm-server"
const noopBootstrap = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void }))
export const testInstanceStoreLayer = InstanceStore.defaultLayer.pipe(Layer.provide(noopBootstrap))
const testInstanceRuntime = ManagedRuntime.make(testInstanceStoreLayer.pipe(Layer.provideMerge(Observability.layer)))
const runTestInstanceStore = <A>(fn: (store: InstanceStore.Interface) => Effect.Effect<A>) =>
testInstanceRuntime.runPromise(InstanceStore.Service.use(fn))
export async function provideTestInstance<R>(input: {
directory: string
init?: Effect.Effect<void>
fn: (ctx: InstanceContext) => R
}) {
const ctx = await runTestInstanceStore((store) => store.load({ directory: input.directory }))
const ctx = await InstanceRuntime.load({ directory: input.directory })
try {
if (input.init) await testInstanceRuntime.runPromise(input.init.pipe(Effect.provideService(InstanceRef, ctx)))
if (input.init) await Effect.runPromise(input.init.pipe(Effect.provideService(InstanceRef, ctx)))
return await input.fn(ctx)
} finally {
await runTestInstanceStore((store) => store.dispose(ctx))
await InstanceRuntime.disposeInstance(ctx)
}
}
export async function withTestInstance<R>(input: { directory: string; fn: (ctx: InstanceContext) => R }) {
return input.fn(await runTestInstanceStore((store) => store.load({ directory: input.directory })))
return input.fn(await InstanceRuntime.load({ directory: input.directory }))
}
export async function reloadTestInstance(input: { directory: string }) {
return runTestInstanceStore((store) => store.reload(input))
return InstanceRuntime.reloadInstance(input)
}
export async function disposeAllInstances() {
await Promise.all([InstanceRuntime.disposeAllInstances(), runTestInstanceStore((store) => store.disposeAll())])
await InstanceRuntime.disposeAllInstances()
}
// Strip null bytes from paths (defensive fix for CI environment issues)
@@ -119,9 +114,10 @@ export async function tmpdir<T>(options?: TmpDirOptions<T>) {
}
/** Effectful scoped tmpdir. Cleaned up when the scope closes. Make sure these stay in sync */
export function tmpdirScoped(options?: {
export function tmpdirScoped<E = never, R = never>(options?: {
git?: boolean
config?: Partial<Config.Info> | (() => Partial<Config.Info>)
init?: (directory: string) => Effect.Effect<void, E, R>
}) {
return Effect.gen(function* () {
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
@@ -158,19 +154,16 @@ export function tmpdirScoped(options?: {
)
}
if (options?.init) yield* options.init(dir)
return dir
})
}
export const provideInstance =
(directory: string) =>
<A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
Effect.contextWith((services: Context.Context<R>) =>
Effect.promise<A>(async () => {
const ctx = await runTestInstanceStore((store) => store.load({ directory }))
return Effect.runPromiseWith(services)(self.pipe(Effect.provideService(InstanceRef, ctx)))
}),
)
<A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R | InstanceStore.Service> =>
InstanceStore.Service.use((store) => store.provide({ directory }, self))
export const provideInstanceEffect =
(directory: string) =>
@@ -188,21 +181,8 @@ export function provideTmpdirInstance<A, E, R>(
) {
return Effect.gen(function* () {
const path = yield* tmpdirScoped(options)
let provided = false
yield* Effect.addFinalizer(() =>
provided
? Effect.promise(() =>
runTestInstanceStore((store) =>
store.load({ directory: path }).pipe(Effect.flatMap((ctx) => store.dispose(ctx))),
),
).pipe(Effect.ignore)
: Effect.void,
)
provided = true
return yield* self(path).pipe(provideInstance(path))
})
}).pipe(Effect.provide(testInstanceStoreLayer))
}
export class TestInstance extends Context.Service<TestInstance, { readonly directory: string }>()("@test/Instance") {}
@@ -214,7 +194,11 @@ export const requireInstance = Effect.gen(function* () {
})
export const withTmpdirInstance =
(options?: { git?: boolean; config?: Partial<Config.Info> | (() => Partial<Config.Info>) }) =>
<E2 = never, R2 = never>(options?: {
git?: boolean
config?: Partial<Config.Info> | (() => Partial<Config.Info>)
init?: (directory: string) => Effect.Effect<void, E2, R2>
}) =>
<A, E, R>(self: Effect.Effect<A, E, R>) =>
Effect.gen(function* () {
const directory = yield* tmpdirScoped(options)