mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-14 20:21:39 +03:00
fix(core): stop logging errors when optional watched folders are missing (#60955)
This commit is contained in:
committed by
GitHub
Unverified
parent
a1b616b3b9
commit
ed892fbd37
@@ -5,6 +5,7 @@ import { createWrapper } from "@parcel/watcher/wrapper.js"
|
||||
import type ParcelWatcher from "@parcel/watcher"
|
||||
import { makeGlobalNode } from "@opencode/util/effect/app-node"
|
||||
import { FSUtil } from "@opencode/util/fs-util"
|
||||
import { isRecord } from "@opencode/util/record"
|
||||
import { Cause, Context, Effect, Layer, PubSub, RcMap, Stream } from "effect"
|
||||
import { lazy } from "../util/lazy.js"
|
||||
import { watch } from "node:fs"
|
||||
@@ -205,14 +206,16 @@ export const nativeLayer = Layer.succeed(
|
||||
if (input.type === "file" || input.type === "entries") {
|
||||
const directory = input.type === "file" ? path.dirname(input.target) : input.target
|
||||
const names = new Set(input.type === "file" ? [path.basename(input.target)] : input.names)
|
||||
return Effect.try(() =>
|
||||
watch(directory, { recursive: false }, (_event, file) => {
|
||||
if (file && !names.has(file)) return
|
||||
for (const name of file ? [file] : names) {
|
||||
input.publish({ path: path.join(directory, name), type: "update" })
|
||||
}
|
||||
}),
|
||||
).pipe(
|
||||
return Effect.try({
|
||||
try: () =>
|
||||
watch(directory, { recursive: false }, (_event, file) => {
|
||||
if (file && !names.has(file)) return
|
||||
for (const name of file ? [file] : names) {
|
||||
input.publish({ path: path.join(directory, name), type: "update" })
|
||||
}
|
||||
}),
|
||||
catch: (error) => error,
|
||||
}).pipe(
|
||||
Effect.map((subscription): Subscription => {
|
||||
subscription.on("error", (error: unknown) =>
|
||||
Effect.runFork(Effect.logError("watcher callback failed", { path: directory, error })),
|
||||
@@ -221,7 +224,9 @@ export const nativeLayer = Layer.succeed(
|
||||
}),
|
||||
// A missing directory (ENOENT) ends subscriber streams instead of crashing the watch.
|
||||
Effect.catch((error) =>
|
||||
Effect.logError("failed to watch directory", { path: directory, error }).pipe(Effect.as(undefined)),
|
||||
isRecord(error) && error.code === "ENOENT"
|
||||
? Effect.undefined
|
||||
: Effect.logError("failed to watch directory", { path: directory, error }).pipe(Effect.as(undefined)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { $ } from "bun"
|
||||
import { describe, expect } from "bun:test"
|
||||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
import { Deferred, Duration, Effect, Fiber, Layer, Option, Schedule, Stream } from "effect"
|
||||
import { Deferred, Duration, Effect, Fiber, Layer, Logger, Option, Schedule, Stream } from "effect"
|
||||
import { Config } from "@opencode/core/config"
|
||||
import { ConfigLocationWatcherPlugin } from "@opencode/core/config/plugin/location-watcher"
|
||||
import { AppNodeBuilder } from "@opencode/core/effect/app-node-builder"
|
||||
@@ -54,15 +54,38 @@ function countingNative() {
|
||||
}
|
||||
|
||||
describe("Watcher lifecycle", () => {
|
||||
it.live("ends the stream when the watched directory is missing", () =>
|
||||
withTmp((directory) =>
|
||||
it.live("ends the stream without logging an error when the watched directory is missing", () => {
|
||||
const errors: unknown[] = []
|
||||
const logger = Logger.make(({ logLevel, message }) => {
|
||||
if (logLevel === "Error") errors.push(message)
|
||||
})
|
||||
return withTmp((directory) =>
|
||||
Effect.gen(function* () {
|
||||
const watcher = yield* Watcher.Service
|
||||
const updates = yield* watcher.subscribe({ path: path.join(directory, "missing", "a.ts"), type: "file" })
|
||||
expect(Option.isNone(yield* Stream.runHead(updates))).toBe(true)
|
||||
}).pipe(Effect.provide(AppNodeBuilder.build(Watcher.node))),
|
||||
),
|
||||
)
|
||||
expect(errors).toEqual([])
|
||||
}).pipe(Effect.provide(AppNodeBuilder.build(Watcher.node)), Effect.provide(Logger.layer([logger]))),
|
||||
)
|
||||
})
|
||||
|
||||
it.live("still logs an error when watching fails for a reason other than ENOENT", () => {
|
||||
const errors: unknown[] = []
|
||||
const logger = Logger.make(({ logLevel, message }) => {
|
||||
if (logLevel === "Error") errors.push(message)
|
||||
})
|
||||
return withTmp((directory) =>
|
||||
Effect.gen(function* () {
|
||||
const watcher = yield* Watcher.Service
|
||||
const updates = yield* watcher.subscribe({
|
||||
path: path.join(directory, "bad\0dir", "a.ts"),
|
||||
type: "file",
|
||||
})
|
||||
expect(Option.isNone(yield* Stream.runHead(updates))).toBe(true)
|
||||
expect(errors).toHaveLength(1)
|
||||
}).pipe(Effect.provide(AppNodeBuilder.build(Watcher.node)), Effect.provide(Logger.layer([logger]))),
|
||||
)
|
||||
})
|
||||
|
||||
it.effect("signals readiness after acquisition and buffers updates published by the ready callback", () =>
|
||||
Effect.gen(function* () {
|
||||
|
||||
Reference in New Issue
Block a user