Skip to content

Commit e96eead

Browse files
authored
refactor(vcs): replace async git() with ChildProcessSpawner (anomalyco#19361)
1 parent b242a8d commit e96eead

1 file changed

Lines changed: 26 additions & 13 deletions

File tree

  • packages/opencode/src/project

packages/opencode/src/project/vcs.ts

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { Effect, Layer, ServiceMap, Stream } from "effect"
2+
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
23
import { Bus } from "@/bus"
34
import { BusEvent } from "@/bus/bus-event"
5+
import * as CrossSpawnSpawner from "@/effect/cross-spawn-spawner"
46
import { InstanceState } from "@/effect/instance-state"
57
import { makeRuntime } from "@/effect/run-service"
68
import { FileWatcher } from "@/file/watcher"
79
import { Log } from "@/util/log"
8-
import { git } from "@/util/git"
9-
import { Instance } from "./instance"
1010
import z from "zod"
1111

1212
export namespace Vcs {
@@ -41,36 +41,49 @@ export namespace Vcs {
4141

4242
export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Vcs") {}
4343

44-
export const layer: Layer.Layer<Service, never, Bus.Service> = Layer.effect(
44+
export const layer: Layer.Layer<Service, never, Bus.Service | ChildProcessSpawner.ChildProcessSpawner> = Layer.effect(
4545
Service,
4646
Effect.gen(function* () {
4747
const bus = yield* Bus.Service
48+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
49+
50+
const git = Effect.fnUntraced(
51+
function* (args: string[], opts: { cwd: string }) {
52+
const handle = yield* spawner.spawn(
53+
ChildProcess.make("git", args, { cwd: opts.cwd, extendEnv: true, stdin: "ignore" }),
54+
)
55+
const text = yield* Stream.mkString(Stream.decodeText(handle.stdout))
56+
const code = yield* handle.exitCode
57+
return { code, text }
58+
},
59+
Effect.scoped,
60+
Effect.catch(() => Effect.succeed({ code: ChildProcessSpawner.ExitCode(1), text: "" })),
61+
)
62+
4863
const state = yield* InstanceState.make<State>(
4964
Effect.fn("Vcs.state")((ctx) =>
5065
Effect.gen(function* () {
5166
if (ctx.project.vcs !== "git") {
5267
return { current: undefined }
5368
}
5469

55-
const get = async () => {
56-
const result = await git(["rev-parse", "--abbrev-ref", "HEAD"], {
57-
cwd: ctx.worktree,
58-
})
59-
if (result.exitCode !== 0) return undefined
60-
const text = result.text().trim()
70+
const getBranch = Effect.fnUntraced(function* () {
71+
const result = yield* git(["rev-parse", "--abbrev-ref", "HEAD"], { cwd: ctx.worktree })
72+
if (result.code !== 0) return undefined
73+
const text = result.text.trim()
6174
return text || undefined
62-
}
75+
})
6376

6477
const value = {
65-
current: yield* Effect.promise(() => get()),
78+
current: yield* getBranch(),
6679
}
6780
log.info("initialized", { branch: value.current })
6881

6982
yield* bus.subscribe(FileWatcher.Event.Updated).pipe(
7083
Stream.filter((evt) => evt.properties.file.endsWith("HEAD")),
7184
Stream.runForEach(() =>
7285
Effect.gen(function* () {
73-
const next = yield* Effect.promise(() => get())
86+
const next = yield* getBranch()
7487
if (next !== value.current) {
7588
log.info("branch changed", { from: value.current, to: next })
7689
value.current = next
@@ -97,7 +110,7 @@ export namespace Vcs {
97110
}),
98111
)
99112

100-
export const defaultLayer = layer.pipe(Layer.provide(Bus.layer))
113+
export const defaultLayer = layer.pipe(Layer.provide(Bus.layer), Layer.provide(CrossSpawnSpawner.defaultLayer))
101114

102115
const { runPromise } = makeRuntime(Service, defaultLayer)
103116

0 commit comments

Comments
 (0)