effect(git): migrate to AppProcess.run (#27190)
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
import { AppProcess } from "@opencode-ai/core/process"
|
||||||
import { Effect, Layer, Context, Stream } from "effect"
|
import { Effect, Layer, Context, Stream } from "effect"
|
||||||
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
|
import { ChildProcess } from "effect/unstable/process"
|
||||||
|
|
||||||
const cfg = [
|
const cfg = [
|
||||||
"--no-optional-locks",
|
"--no-optional-locks",
|
||||||
@@ -102,49 +102,31 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/Gi
|
|||||||
export const layer = Layer.effect(
|
export const layer = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
|
const appProcess = yield* AppProcess.Service
|
||||||
const encoder = new TextEncoder()
|
const encoder = new TextEncoder()
|
||||||
const stdin = (text: string) => Stream.make(encoder.encode(text))
|
const stdin = (text: string) => Stream.make(encoder.encode(text))
|
||||||
|
|
||||||
const run = Effect.fn("Git.run")(
|
const run = Effect.fn("Git.run")(
|
||||||
function* (args: string[], opts: Options) {
|
function* (args: string[], opts: Options) {
|
||||||
const proc = ChildProcess.make("git", [...cfg, ...args], {
|
const result = yield* appProcess.run(
|
||||||
cwd: opts.cwd,
|
ChildProcess.make("git", [...cfg, ...args], {
|
||||||
env: opts.env,
|
cwd: opts.cwd,
|
||||||
extendEnv: true,
|
env: opts.env,
|
||||||
stdin: opts.stdin ?? "ignore",
|
extendEnv: true,
|
||||||
stdout: "pipe",
|
stdin: opts.stdin ?? "ignore",
|
||||||
stderr: "pipe",
|
stdout: "pipe",
|
||||||
})
|
stderr: "pipe",
|
||||||
const handle = yield* spawner.spawn(proc)
|
}),
|
||||||
const collect = (stream: typeof handle.stdout) =>
|
{ maxOutputBytes: opts.maxOutputBytes },
|
||||||
Stream.runFold(
|
)
|
||||||
stream,
|
|
||||||
() => ({ chunks: [] as Uint8Array[], bytes: 0, truncated: false }),
|
|
||||||
(acc, chunk) => {
|
|
||||||
if (opts.maxOutputBytes === undefined) {
|
|
||||||
acc.chunks.push(chunk)
|
|
||||||
acc.bytes += chunk.length
|
|
||||||
return acc
|
|
||||||
}
|
|
||||||
|
|
||||||
const remaining = opts.maxOutputBytes - acc.bytes
|
|
||||||
if (remaining > 0) acc.chunks.push(remaining >= chunk.length ? chunk : chunk.slice(0, remaining))
|
|
||||||
acc.bytes += chunk.length
|
|
||||||
acc.truncated = acc.truncated || acc.bytes > opts.maxOutputBytes
|
|
||||||
return acc
|
|
||||||
},
|
|
||||||
).pipe(Effect.map((x) => ({ buffer: Buffer.concat(x.chunks), truncated: x.truncated })))
|
|
||||||
const [stdout, stderr] = yield* Effect.all([collect(handle.stdout), collect(handle.stderr)], { concurrency: 2 })
|
|
||||||
return {
|
return {
|
||||||
exitCode: yield* handle.exitCode,
|
exitCode: result.exitCode,
|
||||||
text: () => stdout.buffer.toString("utf8"),
|
text: () => result.stdout.toString("utf8"),
|
||||||
stdout: stdout.buffer,
|
stdout: result.stdout,
|
||||||
stderr: stderr.buffer,
|
stderr: result.stderr,
|
||||||
truncated: stdout.truncated || stderr.truncated,
|
truncated: result.truncated,
|
||||||
} satisfies Result
|
} satisfies Result
|
||||||
},
|
},
|
||||||
Effect.scoped,
|
|
||||||
Effect.catch((err) => Effect.succeed(fail(err))),
|
Effect.catch((err) => Effect.succeed(fail(err))),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -360,6 +342,6 @@ export const layer = Layer.effect(
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
export const defaultLayer = layer.pipe(Layer.provide(CrossSpawnSpawner.defaultLayer))
|
export const defaultLayer = layer.pipe(Layer.provide(AppProcess.defaultLayer))
|
||||||
|
|
||||||
export * as Git from "."
|
export * as Git from "."
|
||||||
|
|||||||
Reference in New Issue
Block a user