refactor(core): consolidate filesystem services (#30447)

This commit is contained in:
Dax
2026-06-02 16:09:26 -04:00
committed by GitHub
parent b93963e462
commit 604a5f781f
153 changed files with 2553 additions and 4312 deletions

View File

@@ -1,7 +1,7 @@
import { describe, expect } from "bun:test"
import path from "path"
import { Effect, Exit, Layer } from "effect"
import { AppFileSystem } from "@opencode-ai/core/filesystem"
import { FSUtil } from "@opencode-ai/core/fs-util"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { Git } from "../../src/git"
import { Global } from "@opencode-ai/core/global"
@@ -11,11 +11,11 @@ import { testEffect } from "../lib/effect"
const dir = path.join(Global.Path.data, "storage")
const it = testEffect(Layer.mergeAll(Storage.defaultLayer, AppFileSystem.defaultLayer, CrossSpawnSpawner.defaultLayer))
const it = testEffect(Layer.mergeAll(Storage.defaultLayer, FSUtil.defaultLayer, CrossSpawnSpawner.defaultLayer))
const scope = Effect.fnUntraced(function* () {
const root = ["storage_test", crypto.randomUUID()]
const fs = yield* AppFileSystem.Service
const fs = yield* FSUtil.Service
const svc = yield* Storage.Service
yield* Effect.addFinalizer(() =>
fs.remove(path.join(dir, ...root), { recursive: true, force: true }).pipe(Effect.ignore),
@@ -24,10 +24,10 @@ const scope = Effect.fnUntraced(function* () {
})
// remap(root) rewrites any path under Global.Path.data to live under `root` instead.
// Used by remappedFs to build an AppFileSystem that Storage thinks is the real global
// Used by remappedFs to build an FSUtil that Storage thinks is the real global
// data dir but actually targets a tmp dir — letting migration tests stage legacy layouts.
// NOTE: only the 6 methods below are intercepted. If Storage starts using a different
// AppFileSystem method that touches Global.Path.data, add it here.
// FSUtil method that touches Global.Path.data, add it here.
function remap(root: string, file: string) {
if (file === Global.Path.data) return root
if (file.startsWith(Global.Path.data + path.sep)) return path.join(root, path.relative(Global.Path.data, file))
@@ -36,10 +36,10 @@ function remap(root: string, file: string) {
function remappedFs(root: string) {
return Layer.effect(
AppFileSystem.Service,
FSUtil.Service,
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
return AppFileSystem.Service.of({
const fs = yield* FSUtil.Service
return FSUtil.Service.of({
...fs,
isDir: (file) => fs.isDir(remap(root, file)),
readJson: (file) => fs.readJson(remap(root, file)),
@@ -50,11 +50,11 @@ function remappedFs(root: string) {
fs.glob(pattern, options?.cwd ? { ...options, cwd: remap(root, options.cwd) } : options),
})
}),
).pipe(Layer.provide(AppFileSystem.defaultLayer))
).pipe(Layer.provide(FSUtil.defaultLayer))
}
// Layer.fresh forces a new Storage instance — without it, Effect's in-test layer cache
// returns the outer testEffect's Storage (which uses the real AppFileSystem), not a new
// returns the outer testEffect's Storage (which uses the real FSUtil), not a new
// one built on top of remappedFs.
const remappedStorage = (root: string) =>
Layer.fresh(Storage.layer.pipe(Layer.provide(remappedFs(root)), Layer.provide(Git.defaultLayer)))
@@ -191,7 +191,7 @@ describe("Storage", () => {
it.live("migration 2 runs when marker contents are invalid", () =>
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
const fs = yield* FSUtil.Service
const tmp = yield* tmpdirScoped()
const storage = path.join(tmp, "storage")
const diffs = [
@@ -235,7 +235,7 @@ describe("Storage", () => {
it.live("migration 1 tolerates malformed legacy records", () =>
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
const fs = yield* FSUtil.Service
const tmp = yield* tmpdirScoped({ git: true })
const storage = path.join(tmp, "storage")
const legacy = path.join(tmp, "project", "legacy")
@@ -277,7 +277,7 @@ describe("Storage", () => {
it.live("failed migrations do not advance the marker", () =>
Effect.gen(function* () {
const fs = yield* AppFileSystem.Service
const fs = yield* FSUtil.Service
const tmp = yield* tmpdirScoped()
const storage = path.join(tmp, "storage")
const legacy = path.join(tmp, "project", "legacy")