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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user