mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-17 12:56:41 +02:00
Convert LoadInput.init to Effect + extract InstanceBootstrap as a Service (#25376)
This commit is contained in:
@@ -5,6 +5,7 @@ import fs from "fs/promises"
|
||||
import { Filesystem } from "@/util/filesystem"
|
||||
import { File } from "../../src/file"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { containsPath } from "../../src/project/instance-context"
|
||||
import { provideInstance, tmpdir } from "../fixture/fixture"
|
||||
|
||||
const run = <A, E>(eff: Effect.Effect<A, E, File.Service>) =>
|
||||
@@ -121,15 +122,15 @@ describe("File.list path traversal protection", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("Instance.containsPath", () => {
|
||||
describe("containsPath", () => {
|
||||
test("returns true for path inside directory", async () => {
|
||||
await using tmp = await tmpdir({ git: true })
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: () => {
|
||||
expect(Instance.containsPath(path.join(tmp.path, "foo.txt"), Instance.current)).toBe(true)
|
||||
expect(Instance.containsPath(path.join(tmp.path, "src", "file.ts"), Instance.current)).toBe(true)
|
||||
expect(containsPath(path.join(tmp.path, "foo.txt"), Instance.current)).toBe(true)
|
||||
expect(containsPath(path.join(tmp.path, "src", "file.ts"), Instance.current)).toBe(true)
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -143,11 +144,11 @@ describe("Instance.containsPath", () => {
|
||||
directory: subdir,
|
||||
fn: () => {
|
||||
// .opencode at worktree root, but we're running from packages/lib
|
||||
expect(Instance.containsPath(path.join(tmp.path, ".opencode", "state"), Instance.current)).toBe(true)
|
||||
expect(containsPath(path.join(tmp.path, ".opencode", "state"), Instance.current)).toBe(true)
|
||||
// sibling package should also be accessible
|
||||
expect(Instance.containsPath(path.join(tmp.path, "packages", "other", "file.ts"), Instance.current)).toBe(true)
|
||||
expect(containsPath(path.join(tmp.path, "packages", "other", "file.ts"), Instance.current)).toBe(true)
|
||||
// worktree root itself
|
||||
expect(Instance.containsPath(tmp.path, Instance.current)).toBe(true)
|
||||
expect(containsPath(tmp.path, Instance.current)).toBe(true)
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -158,8 +159,8 @@ describe("Instance.containsPath", () => {
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: () => {
|
||||
expect(Instance.containsPath("/etc/passwd", Instance.current)).toBe(false)
|
||||
expect(Instance.containsPath("/tmp/other-project", Instance.current)).toBe(false)
|
||||
expect(containsPath("/etc/passwd", Instance.current)).toBe(false)
|
||||
expect(containsPath("/tmp/other-project", Instance.current)).toBe(false)
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -170,7 +171,7 @@ describe("Instance.containsPath", () => {
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: () => {
|
||||
expect(Instance.containsPath(path.join(tmp.path, "..", "escape.txt"), Instance.current)).toBe(false)
|
||||
expect(containsPath(path.join(tmp.path, "..", "escape.txt"), Instance.current)).toBe(false)
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -182,8 +183,8 @@ describe("Instance.containsPath", () => {
|
||||
directory: tmp.path,
|
||||
fn: () => {
|
||||
expect(Instance.directory).toBe(Instance.worktree)
|
||||
expect(Instance.containsPath(path.join(tmp.path, "file.txt"), Instance.current)).toBe(true)
|
||||
expect(Instance.containsPath("/etc/passwd", Instance.current)).toBe(false)
|
||||
expect(containsPath(path.join(tmp.path, "file.txt"), Instance.current)).toBe(true)
|
||||
expect(containsPath("/etc/passwd", Instance.current)).toBe(false)
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -195,9 +196,9 @@ describe("Instance.containsPath", () => {
|
||||
directory: tmp.path,
|
||||
fn: () => {
|
||||
// worktree is "/" for non-git projects, but containsPath should NOT allow all paths
|
||||
expect(Instance.containsPath(path.join(tmp.path, "file.txt"), Instance.current)).toBe(true)
|
||||
expect(Instance.containsPath("/etc/passwd", Instance.current)).toBe(false)
|
||||
expect(Instance.containsPath("/tmp/other", Instance.current)).toBe(false)
|
||||
expect(containsPath(path.join(tmp.path, "file.txt"), Instance.current)).toBe(true)
|
||||
expect(containsPath("/etc/passwd", Instance.current)).toBe(false)
|
||||
expect(containsPath("/tmp/other", Instance.current)).toBe(false)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { afterEach, describe, expect } from "bun:test"
|
||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||
import { Effect, Fiber, Layer } from "effect"
|
||||
import { InstanceRef } from "../../src/effect/instance-ref"
|
||||
import { registerDisposer } from "../../src/effect/instance-registry"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { InstanceStore } from "../../src/project/instance-store"
|
||||
@@ -26,7 +27,7 @@ describe("InstanceStore", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("runs load init inside the loaded legacy instance context", () =>
|
||||
it.live("runs load init with InstanceRef provided", () =>
|
||||
Effect.gen(function* () {
|
||||
const dir = yield* tmpdirScoped({ git: true })
|
||||
const store = yield* InstanceStore.Service
|
||||
@@ -34,9 +35,9 @@ describe("InstanceStore", () => {
|
||||
|
||||
yield* store.load({
|
||||
directory: dir,
|
||||
init: async () => {
|
||||
initializedDirectory = Instance.directory
|
||||
},
|
||||
init: Effect.gen(function* () {
|
||||
initializedDirectory = (yield* InstanceRef)?.directory
|
||||
}),
|
||||
})
|
||||
|
||||
expect(initializedDirectory).toBe(dir)
|
||||
@@ -52,15 +53,15 @@ describe("InstanceStore", () => {
|
||||
|
||||
const first = yield* store.load({
|
||||
directory: dir,
|
||||
init: async () => {
|
||||
init: Effect.sync(() => {
|
||||
initialized++
|
||||
},
|
||||
}),
|
||||
})
|
||||
const second = yield* store.load({
|
||||
directory: dir,
|
||||
init: async () => {
|
||||
init: Effect.sync(() => {
|
||||
initialized++
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
expect(second).toBe(first)
|
||||
@@ -79,11 +80,11 @@ describe("InstanceStore", () => {
|
||||
const first = yield* store
|
||||
.load({
|
||||
directory: dir,
|
||||
init: async () => {
|
||||
init: Effect.promise(async () => {
|
||||
initialized++
|
||||
started.resolve()
|
||||
await release.promise
|
||||
},
|
||||
}),
|
||||
})
|
||||
.pipe(Effect.forkScoped)
|
||||
|
||||
@@ -92,9 +93,9 @@ describe("InstanceStore", () => {
|
||||
const second = yield* store
|
||||
.load({
|
||||
directory: dir,
|
||||
init: async () => {
|
||||
init: Effect.sync(() => {
|
||||
initialized++
|
||||
},
|
||||
}),
|
||||
})
|
||||
.pipe(Effect.forkScoped)
|
||||
|
||||
@@ -116,10 +117,10 @@ describe("InstanceStore", () => {
|
||||
const failed = yield* store
|
||||
.load({
|
||||
directory: dir,
|
||||
init: async () => {
|
||||
init: Effect.sync(() => {
|
||||
attempts++
|
||||
throw new Error("init failed")
|
||||
},
|
||||
}),
|
||||
})
|
||||
.pipe(
|
||||
Effect.as(false),
|
||||
@@ -130,9 +131,9 @@ describe("InstanceStore", () => {
|
||||
|
||||
const ctx = yield* store.load({
|
||||
directory: dir,
|
||||
init: async () => {
|
||||
init: Effect.sync(() => {
|
||||
attempts++
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
expect(ctx.directory).toBe(dir)
|
||||
@@ -170,10 +171,10 @@ describe("InstanceStore", () => {
|
||||
const reload = yield* store
|
||||
.reload({
|
||||
directory: dir,
|
||||
init: async () => {
|
||||
init: Effect.promise(async () => {
|
||||
reloading.resolve()
|
||||
await releaseReload.promise
|
||||
},
|
||||
}),
|
||||
})
|
||||
.pipe(Effect.forkScoped)
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { registerAdapter } from "../../src/control-plane/adapters"
|
||||
import type { WorkspaceAdapter } from "../../src/control-plane/types"
|
||||
import { Workspace } from "../../src/control-plane/workspace"
|
||||
import { InstanceRef, WorkspaceRef } from "../../src/effect/instance-ref"
|
||||
import { InstanceBootstrap } from "../../src/project/bootstrap"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { InstanceStore } from "../../src/project/instance-store"
|
||||
import { Project } from "../../src/project/project"
|
||||
@@ -41,6 +42,7 @@ const it = testEffect(
|
||||
testStateLayer,
|
||||
NodeHttpServer.layerTest,
|
||||
NodeServices.layer,
|
||||
InstanceBootstrap.defaultLayer,
|
||||
InstanceStore.defaultLayer,
|
||||
Project.defaultLayer,
|
||||
Workspace.defaultLayer,
|
||||
|
||||
Reference in New Issue
Block a user