fix(opencode): dispose booted worktrees before removal

This commit is contained in:
Dax Raad
2026-05-26 13:16:26 -04:00
parent 9b5fbbe398
commit 3077ff3f0c
3 changed files with 15 additions and 8 deletions
@@ -19,6 +19,7 @@ export interface Interface {
readonly load: (input: LoadInput) => Effect.Effect<InstanceContext>
readonly reload: (input: LoadInput) => Effect.Effect<InstanceContext>
readonly dispose: (ctx: InstanceContext) => Effect.Effect<void>
readonly disposeDirectory: (directory: string) => Effect.Effect<void>
readonly disposeAll: () => Effect.Effect<void>
readonly provide: <A, E, R>(input: LoadInput, effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
}
@@ -151,6 +152,15 @@ export const layer: Layer.Layer<Service, never, Project.Service | InstanceBootst
yield* disposeEntry(ctx.directory, entry, ctx).pipe(Effect.asVoid)
})
const disposeDirectory = Effect.fn("InstanceStore.disposeDirectory")(function* (input: string) {
const directory = AppFileSystem.resolve(input)
const entry = cache.get(directory)
if (!entry) return
const exit = yield* Deferred.await(entry.deferred).pipe(Effect.exit)
if (Exit.isFailure(exit)) return yield* removeEntry(directory, entry).pipe(Effect.asVoid)
yield* disposeEntry(directory, entry, exit.value).pipe(Effect.asVoid)
})
const disposeAllOnce = Effect.fnUntraced(function* () {
yield* Effect.logInfo("disposing all instances")
yield* Effect.forEach(
@@ -185,6 +195,7 @@ export const layer: Layer.Layer<Service, never, Project.Service | InstanceBootst
load,
reload,
dispose,
disposeDirectory,
disposeAll,
provide,
})
+3
View File
@@ -395,6 +395,9 @@ export const layer: Layer.Layer<
const directory = yield* canonical(input.directory)
// Bootstrapped worktrees may hold file handles, which must be released before removal on Windows.
if (directory !== (yield* canonical(ctx.worktree))) yield* store.disposeDirectory(directory)
const list = yield* git(["worktree", "list", "--porcelain"], { cwd: ctx.worktree })
if (list.code !== 0) {
return yield* new RemoveFailedError({ message: list.stderr || list.text || "Failed to read git worktrees" })
@@ -5,10 +5,8 @@ import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { Cause, Deferred, Effect, Exit, Fiber, Layer } from "effect"
import { GlobalBus, type GlobalEvent } from "../../src/bus/global"
import { Git } from "../../src/git"
import { InstanceRef } from "../../src/effect/instance-ref"
import { InstanceRuntime } from "../../src/project/instance-runtime"
import { Worktree } from "../../src/worktree"
import { disposeAllInstances, provideInstance, TestInstance } from "../fixture/fixture"
import { disposeAllInstances, TestInstance } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
const it = testEffect(
@@ -41,11 +39,6 @@ const waitReady = Effect.fn("WorktreeTest.waitReady")(function* () {
const removeCreatedWorktree = (directory: string) =>
Effect.gen(function* () {
const svc = yield* Worktree.Service
const ctx = yield* Effect.gen(function* () {
return yield* InstanceRef
}).pipe(provideInstance(directory))
if (!ctx) return yield* Effect.die(new Error("missing test instance"))
yield* Effect.promise(() => InstanceRuntime.disposeInstance(ctx))
const ok = yield* svc.remove({ directory })
if (!ok) return yield* Effect.fail(new Error(`failed to remove worktree ${directory}`))
})