Просмотр исходного кода

refactor(repository): add cache service (#28184)

Shoubhit Dash 3 месяцев назад
Родитель
Сommit
96192495ae

+ 3 - 9
packages/opencode/src/reference/reference.ts

@@ -6,7 +6,6 @@ import { Config } from "@/config/config"
 import { ConfigReference } from "@/config/reference"
 import { InstanceState } from "@/effect/instance-state"
 import { RuntimeFlags } from "@/effect/runtime-flags"
-import { Git } from "@/git"
 import { parseRepositoryReference, repositoryCachePath, type RemoteReference } from "@/util/repository"
 import { RepositoryCache } from "./repository-cache"
 
@@ -130,8 +129,7 @@ export const layer = Layer.effect(
   Service,
   Effect.gen(function* () {
     const config = yield* Config.Service
-    const fs = yield* AppFileSystem.Service
-    const git = yield* Git.Service
+    const cache = yield* RepositoryCache.Service
     const scope = yield* Scope.Scope
     const flags = yield* RuntimeFlags.Service
 
@@ -154,10 +152,7 @@ export const layer = Layer.effect(
           gitReferences,
           Effect.fnUntraced(function* (reference) {
             const run = yield* Effect.cached(
-              RepositoryCache.ensure(
-                { reference: reference.reference, branch: reference.branch, refresh: true },
-                { fs, git },
-              ).pipe(
+              cache.ensure({ reference: reference.reference, branch: reference.branch, refresh: true }).pipe(
                 Effect.asVoid,
                 Effect.catchCause((cause) =>
                   Effect.logWarning("failed to materialize reference repository").pipe(
@@ -223,8 +218,7 @@ export const layer = Layer.effect(
 
 export const defaultLayer = layer.pipe(
   Layer.provide(Config.defaultLayer),
-  Layer.provide(AppFileSystem.defaultLayer),
-  Layer.provide(Git.defaultLayer),
+  Layer.provide(RepositoryCache.defaultLayer),
   Layer.provide(RuntimeFlags.defaultLayer),
 )
 

+ 39 - 7
packages/opencode/src/reference/repository-cache.ts

@@ -1,5 +1,5 @@
 import path from "path"
-import { Effect } from "effect"
+import { Context, Effect, Layer } from "effect"
 import { AppFileSystem } from "@opencode-ai/core/filesystem"
 import { Flock } from "@opencode-ai/core/util/flock"
 import { Git } from "@/git"
@@ -21,6 +21,18 @@ export type Result = {
   branch?: string
 }
 
+export type EnsureInput = {
+  reference: RemoteReference
+  refresh?: boolean
+  branch?: string
+}
+
+export interface Interface {
+  ensure: (input: EnsureInput) => Effect.Effect<Result, unknown>
+}
+
+export class Service extends Context.Service<Service, Interface>()("@opencode/RepositoryCache") {}
+
 function statusForRepository(input: { reuse: boolean; refresh?: boolean; branchMatches?: boolean }) {
   if (!input.reuse) return "cloned" as const
   if (input.branchMatches === false) return "refreshed" as const
@@ -43,12 +55,8 @@ function resetTarget(input: {
   return "HEAD"
 }
 
-export const ensure = Effect.fn("RepositoryCache.ensure")(function* (
-  input: {
-    reference: RemoteReference
-    refresh?: boolean
-    branch?: string
-  },
+const ensureWithServices = Effect.fn("RepositoryCache.ensureWithServices")(function* (
+  input: EnsureInput,
   services: {
     fs: AppFileSystem.Interface
     git: Git.Interface
@@ -144,4 +152,28 @@ export const ensure = Effect.fn("RepositoryCache.ensure")(function* (
   )
 })
 
+export const layer: Layer.Layer<Service, never, AppFileSystem.Service | Git.Service> = Layer.effect(
+  Service,
+  Effect.gen(function* () {
+    const fs = yield* AppFileSystem.Service
+    const git = yield* Git.Service
+
+    return Service.of({
+      ensure: Effect.fn("RepositoryCache.ensure")(function* (input) {
+        return yield* ensureWithServices(input, { fs, git })
+      }),
+    })
+  }),
+)
+
+export const defaultLayer: Layer.Layer<Service> = layer.pipe(
+  Layer.provide(AppFileSystem.defaultLayer),
+  Layer.provide(Git.defaultLayer),
+)
+
+export const ensure = Effect.fn("RepositoryCache.ensure")(function* (input: EnsureInput) {
+  const cache = yield* Service
+  return yield* cache.ensure(input)
+})
+
 export * as RepositoryCache from "./repository-cache"

+ 3 - 1
packages/opencode/src/tool/registry.ts

@@ -25,6 +25,7 @@ import { ProviderID, type ModelID } from "../provider/schema"
 import { WebSearchTool } from "./websearch"
 import { RepoCloneTool } from "./repo_clone"
 import { RepoOverviewTool } from "./repo_overview"
+import { RepositoryCache } from "@/reference/repository-cache"
 import * as Log from "@opencode-ai/core/util/log"
 import { LspTool } from "./lsp"
 import * as Truncate from "./truncate"
@@ -93,6 +94,7 @@ export const layer: Layer.Layer<
   | BackgroundJob.Service
   | Provider.Service
   | Git.Service
+  | RepositoryCache.Service
   | Reference.Service
   | LSP.Service
   | Instruction.Service
@@ -378,7 +380,7 @@ export const defaultLayer = Layer.suspend(() =>
       Layer.provide(Session.defaultLayer),
       Layer.provide(Layer.mergeAll(SessionStatus.defaultLayer, BackgroundJob.defaultLayer)),
       Layer.provide(Provider.defaultLayer),
-      Layer.provide(Git.defaultLayer),
+      Layer.provide(Layer.mergeAll(Git.defaultLayer, RepositoryCache.defaultLayer)),
       Layer.provide(Reference.defaultLayer),
       Layer.provide(LSP.defaultLayer),
       Layer.provide(Instruction.defaultLayer),

+ 3 - 9
packages/opencode/src/tool/repo_clone.ts

@@ -1,6 +1,4 @@
 import { Effect, Schema } from "effect"
-import { AppFileSystem } from "@opencode-ai/core/filesystem"
-import { Git } from "@/git"
 import DESCRIPTION from "./repo_clone.txt"
 import * as Tool from "./tool"
 import { parseRemoteRepositoryReference, repositoryCachePath, validateRepositoryBranch } from "@/util/repository"
@@ -28,11 +26,10 @@ type Metadata = {
   branch?: string
 }
 
-export const RepoCloneTool = Tool.define<typeof Parameters, Metadata, AppFileSystem.Service | Git.Service>(
+export const RepoCloneTool = Tool.define<typeof Parameters, Metadata, RepositoryCache.Service>(
   "repo_clone",
   Effect.gen(function* () {
-    const fs = yield* AppFileSystem.Service
-    const git = yield* Git.Service
+    const cache = yield* RepositoryCache.Service
 
     return {
       description: DESCRIPTION,
@@ -59,10 +56,7 @@ export const RepoCloneTool = Tool.define<typeof Parameters, Metadata, AppFileSys
             },
           })
 
-          const result = yield* RepositoryCache.ensure(
-            { reference, refresh: params.refresh, branch: params.branch },
-            { fs, git },
-          )
+          const result = yield* cache.ensure({ reference, refresh: params.refresh, branch: params.branch })
           return {
             title: repository,
             metadata: result,

+ 2 - 2
packages/opencode/test/reference/reference.test.ts

@@ -9,6 +9,7 @@ import { ConfigReference } from "../../src/config/reference"
 import { RuntimeFlags } from "../../src/effect/runtime-flags"
 import { Git } from "../../src/git"
 import { Reference } from "../../src/reference/reference"
+import { RepositoryCache } from "../../src/reference/repository-cache"
 import { disposeAllInstances, provideTmpdirInstance, tmpdirScoped } from "../fixture/fixture"
 import { testEffect } from "../lib/effect"
 
@@ -19,8 +20,7 @@ afterEach(async () => {
 const referenceLayer = (flags: Partial<RuntimeFlags.Info> = {}) =>
   Reference.layer.pipe(
     Layer.provide(Config.defaultLayer),
-    Layer.provide(AppFileSystem.defaultLayer),
-    Layer.provide(Git.defaultLayer),
+    Layer.provide(RepositoryCache.defaultLayer),
     Layer.provide(RuntimeFlags.layer(flags)),
   )
 

+ 2 - 0
packages/opencode/test/session/prompt.test.ts

@@ -48,6 +48,7 @@ import * as Database from "../../src/storage/db"
 import { Ripgrep } from "../../src/file/ripgrep"
 import { Format } from "../../src/format"
 import { Reference } from "../../src/reference/reference"
+import { RepositoryCache } from "../../src/reference/repository-cache"
 import { TestInstance } from "../fixture/fixture"
 import { awaitWithTimeout, pollWithTimeout, testEffect } from "../lib/effect"
 import { reply, TestLLMServer } from "../lib/llm-server"
@@ -189,6 +190,7 @@ function makeHttp(input?: { processor?: "blocking" }) {
     Layer.provide(Skill.defaultLayer),
     Layer.provide(FetchHttpClient.layer),
     Layer.provide(CrossSpawnSpawner.defaultLayer),
+    Layer.provide(RepositoryCache.defaultLayer),
     Layer.provide(Git.defaultLayer),
     Layer.provide(Reference.defaultLayer),
     Layer.provide(Ripgrep.defaultLayer),

+ 2 - 0
packages/opencode/test/session/snapshot-tool-race.test.ts

@@ -59,6 +59,7 @@ import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
 import { Ripgrep } from "../../src/file/ripgrep"
 import { Format } from "../../src/format"
 import { Reference } from "../../src/reference/reference"
+import { RepositoryCache } from "../../src/reference/repository-cache"
 import { SyncEvent } from "@/sync"
 import { RuntimeFlags } from "@/effect/runtime-flags"
 import { EventV2Bridge } from "@/event-v2-bridge"
@@ -138,6 +139,7 @@ function makeHttp() {
     Layer.provide(Skill.defaultLayer),
     Layer.provide(FetchHttpClient.layer),
     Layer.provide(CrossSpawnSpawner.defaultLayer),
+    Layer.provide(RepositoryCache.defaultLayer),
     Layer.provide(Git.defaultLayer),
     Layer.provide(Reference.defaultLayer),
     Layer.provide(Ripgrep.defaultLayer),

+ 2 - 2
packages/opencode/test/tool/glob.test.ts

@@ -12,6 +12,7 @@ import { Agent } from "../../src/agent/agent"
 import { TestInstance, tmpdirScoped } from "../fixture/fixture"
 import { testEffect } from "../lib/effect"
 import { Reference } from "@/reference/reference"
+import { RepositoryCache } from "@/reference/repository-cache"
 import { Config } from "@/config/config"
 import { RuntimeFlags } from "@/effect/runtime-flags"
 import { Git } from "@/git"
@@ -21,8 +22,7 @@ import type * as Tool from "../../src/tool/tool"
 const referenceLayer = (flags: Partial<RuntimeFlags.Info> = {}) =>
   Reference.layer.pipe(
     Layer.provide(Config.defaultLayer),
-    Layer.provide(AppFileSystem.defaultLayer),
-    Layer.provide(Git.defaultLayer),
+    Layer.provide(RepositoryCache.defaultLayer),
     Layer.provide(RuntimeFlags.layer(flags)),
   )
 

+ 2 - 2
packages/opencode/test/tool/grep.test.ts

@@ -14,6 +14,7 @@ import { Ripgrep } from "../../src/file/ripgrep"
 import { AppFileSystem } from "@opencode-ai/core/filesystem"
 import { testEffect } from "../lib/effect"
 import { Reference } from "@/reference/reference"
+import { RepositoryCache } from "@/reference/repository-cache"
 import { Permission } from "../../src/permission"
 import type * as Tool from "../../src/tool/tool"
 import { Config } from "@/config/config"
@@ -24,8 +25,7 @@ import { Filesystem } from "@/util/filesystem"
 const referenceLayer = (flags: Partial<RuntimeFlags.Info> = {}) =>
   Reference.layer.pipe(
     Layer.provide(Config.defaultLayer),
-    Layer.provide(AppFileSystem.defaultLayer),
-    Layer.provide(Git.defaultLayer),
+    Layer.provide(RepositoryCache.defaultLayer),
     Layer.provide(RuntimeFlags.layer(flags)),
   )
 

+ 2 - 2
packages/opencode/test/tool/read.test.ts

@@ -19,6 +19,7 @@ import { Filesystem } from "@/util/filesystem"
 import { disposeAllInstances, provideInstance, TestInstance, tmpdirScoped } from "../fixture/fixture"
 import { testEffect } from "../lib/effect"
 import { Reference } from "@/reference/reference"
+import { RepositoryCache } from "@/reference/repository-cache"
 
 const FIXTURES_DIR = path.join(import.meta.dir, "fixtures")
 
@@ -40,8 +41,7 @@ const ctx = {
 const referenceLayer = (flags: Partial<RuntimeFlags.Info> = {}) =>
   Reference.layer.pipe(
     Layer.provide(Config.defaultLayer),
-    Layer.provide(AppFileSystem.defaultLayer),
-    Layer.provide(Git.defaultLayer),
+    Layer.provide(RepositoryCache.defaultLayer),
     Layer.provide(RuntimeFlags.layer(flags)),
   )
 

+ 2 - 1
packages/opencode/test/tool/registry.test.ts

@@ -29,6 +29,7 @@ import { Ripgrep } from "@/file/ripgrep"
 import * as Truncate from "@/tool/truncate"
 import { InstanceState } from "@/effect/instance-state"
 import { Reference } from "@/reference/reference"
+import { RepositoryCache } from "@/reference/repository-cache"
 import { ProviderID, ModelID } from "@/provider/schema"
 import { ToolJsonSchema } from "@/tool/json-schema"
 import { MessageID, SessionID } from "@/session/schema"
@@ -51,7 +52,7 @@ const registryLayer = (flags: Partial<RuntimeFlags.Info> = {}) =>
       Layer.provide(Session.defaultLayer),
       Layer.provide(Layer.mergeAll(SessionStatus.defaultLayer, BackgroundJob.defaultLayer)),
       Layer.provide(Provider.defaultLayer),
-      Layer.provide(Git.defaultLayer),
+      Layer.provide(Layer.mergeAll(Git.defaultLayer, RepositoryCache.defaultLayer)),
       Layer.provide(Reference.defaultLayer),
       Layer.provide(LSP.defaultLayer),
       Layer.provide(Instruction.defaultLayer),

+ 2 - 0
packages/opencode/test/tool/repo_clone.test.ts

@@ -10,6 +10,7 @@ import { Global } from "@opencode-ai/core/global"
 import { MessageID, SessionID } from "../../src/session/schema"
 import { Truncate } from "../../src/tool/truncate"
 import { RepoCloneTool } from "../../src/tool/repo_clone"
+import { RepositoryCache } from "../../src/reference/repository-cache"
 import { disposeAllInstances, provideTmpdirInstance, tmpdirScoped } from "../fixture/fixture"
 import { testEffect } from "../lib/effect"
 
@@ -34,6 +35,7 @@ const it = testEffect(
     AppFileSystem.defaultLayer,
     CrossSpawnSpawner.defaultLayer,
     Git.defaultLayer,
+    RepositoryCache.defaultLayer,
     Truncate.defaultLayer,
   ),
 )