summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAiden Cline <[email protected]>2026-04-11 22:07:34 -0500
committerGitHub <[email protected]>2026-04-11 22:07:34 -0500
commitfc01cad2b842a4025e7a04dc79130e843b1d56b7 (patch)
treeaf7b5694b69e4e22dc6c4a249d92f3d29a6ead4b
parentc1ddc0ea2d6733925569a6a8f937ce9aa6b04cdd (diff)
downloadopencode-fc01cad2b842a4025e7a04dc79130e843b1d56b7.tar.gz
opencode-fc01cad2b842a4025e7a04dc79130e843b1d56b7.zip
fix: ensure logger cleanup properly orders list before deleting files (#22101)
-rw-r--r--packages/opencode/src/util/log.ts17
-rw-r--r--packages/opencode/test/util/log.test.ts44
2 files changed, 51 insertions, 10 deletions
diff --git a/packages/opencode/src/util/log.ts b/packages/opencode/src/util/log.ts
index 2ca4c0a3d..1b962e38e 100644
--- a/packages/opencode/src/util/log.ts
+++ b/packages/opencode/src/util/log.ts
@@ -3,7 +3,6 @@ import fs from "fs/promises"
import { createWriteStream } from "fs"
import { Global } from "../global"
import z from "zod"
-import { Glob } from "./glob"
export namespace Log {
export const Level = z.enum(["DEBUG", "INFO", "WARN", "ERROR"]).meta({ ref: "LogLevel", description: "Log level" })
@@ -15,6 +14,8 @@ export namespace Log {
WARN: 2,
ERROR: 3,
}
+ const keep = 10
+ const rx = /^\d{4}-\d{2}-\d{2}T\d{6}\.log$/
let level: Level = "INFO"
@@ -78,15 +79,11 @@ export namespace Log {
}
async function cleanup(dir: string) {
- const files = await Glob.scan("????-??-??T??????.log", {
- cwd: dir,
- absolute: true,
- include: "file",
- })
- if (files.length <= 5) return
-
- const filesToDelete = files.slice(0, -10)
- await Promise.all(filesToDelete.map((file) => fs.unlink(file).catch(() => {})))
+ const files = (await fs.readdir(dir).catch(() => [])).filter((file) => rx.test(file)).sort()
+ if (files.length <= keep) return
+
+ const doomed = files.slice(0, -keep)
+ await Promise.all(doomed.map((file) => fs.unlink(path.join(dir, file)).catch(() => {})))
}
function formatError(error: Error, depth = 0): string {
diff --git a/packages/opencode/test/util/log.test.ts b/packages/opencode/test/util/log.test.ts
new file mode 100644
index 000000000..33e64fcd0
--- /dev/null
+++ b/packages/opencode/test/util/log.test.ts
@@ -0,0 +1,44 @@
+import { afterEach, expect, test } from "bun:test"
+import fs from "fs/promises"
+import path from "path"
+import { Global } from "../../src/global"
+import { Log } from "../../src/util/log"
+import { tmpdir } from "../fixture/fixture"
+
+const log = Global.Path.log
+
+afterEach(() => {
+ Global.Path.log = log
+})
+
+async function files(dir: string) {
+ let last = ""
+ let same = 0
+
+ for (let i = 0; i < 50; i++) {
+ const list = (await fs.readdir(dir)).sort()
+ const next = JSON.stringify(list)
+ same = next === last ? same + 1 : 0
+ if (same >= 2 && list.length === 11) return list
+ last = next
+ await Bun.sleep(10)
+ }
+
+ return (await fs.readdir(dir)).sort()
+}
+
+test("init cleanup keeps the newest timestamped logs", async () => {
+ await using tmp = await tmpdir()
+ Global.Path.log = tmp.path
+
+ const list = Array.from({ length: 12 }, (_, i) => `2000-01-${String(i + 1).padStart(2, "0")}T000000.log`)
+
+ await Promise.all(list.map((file) => fs.writeFile(path.join(tmp.path, file), file)))
+
+ await Log.init({ print: false, dev: false })
+
+ const next = await files(tmp.path)
+
+ expect(next).not.toContain(list[0]!)
+ expect(next).toContain(list.at(-1)!)
+})