opencode/packages/server/test/fixture/worktree-plugin/index.ts

47 lines
1.9 KiB
TypeScript

import { Plugin, Worktree } from "@opencode/plugin"
export default Plugin.define({
id: "test.worktree",
async setup(ctx) {
const id = typeof ctx.options.strategy === "string" ? ctx.options.strategy : "test-copy"
await ctx.worktree.transform((editor) =>
editor.add({
id,
async create(input, { signal }) {
await git(
input.sourceDirectory,
["worktree", "add", "--detach", "--", input.directory, input.branch ?? "HEAD"],
signal,
)
await ctx.storage.set(`tree:${input.directory}`, input.sourceDirectory)
return { directory: input.directory }
},
async remove(input, { signal }) {
const source = await ctx.storage.get(`tree:${input.directory}`)
if (typeof source !== "string") throw new Worktree.OperationError({ message: "Worktree source not found" })
// Windows cannot remove the working directory of the Git process itself.
await git(
source,
["worktree", "remove", ...(input.force ? ["--force"] : []), input.directory],
signal,
!input.force,
)
await ctx.storage.remove(`tree:${input.directory}`)
},
async list(sourceDirectory, { signal }) {
signal.throwIfAborted()
const rows = await ctx.storage.scan({ prefix: "tree:" })
return rows.entries
.filter((row) => row.value === sourceDirectory)
.map((row) => ({ directory: row.key.slice(5), type: "worktree" as const }))
},
}),
)
},
})
async function git(directory: string, args: string[], signal: AbortSignal, forceRequired = false) {
const child = Bun.spawn(["git", "-C", directory, ...args], { stdout: "pipe", stderr: "pipe", signal })
const [code, stderr] = await Promise.all([child.exited, new Response(child.stderr).text()])
if (code !== 0) throw new Worktree.OperationError({ message: stderr, forceRequired })
}