fix(opencode): avoid snapshotting files over 2MB (#19043)

pull/16241/head^2
Dax 2026-03-25 00:43:48 -04:00 committed by GitHub
parent 4f9667c4bb
commit 0a80ef4278
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 77 additions and 10 deletions

View File

@ -34,6 +34,7 @@ export namespace Snapshot {
const log = Log.create({ service: "snapshot" }) const log = Log.create({ service: "snapshot" })
const prune = "7.days" const prune = "7.days"
const limit = 2 * 1024 * 1024
const core = ["-c", "core.longpaths=true", "-c", "core.symlinks=true"] const core = ["-c", "core.longpaths=true", "-c", "core.symlinks=true"]
const cfg = ["-c", "core.autocrlf=false", ...core] const cfg = ["-c", "core.autocrlf=false", ...core]
const quote = [...cfg, "-c", "core.quotepath=false"] const quote = [...cfg, "-c", "core.quotepath=false"]
@ -123,20 +124,69 @@ export namespace Snapshot {
return file return file
}) })
const sync = Effect.fnUntraced(function* () { const sync = Effect.fnUntraced(function* (list: string[] = []) {
const file = yield* excludes() const file = yield* excludes()
const target = path.join(state.gitdir, "info", "exclude") const target = path.join(state.gitdir, "info", "exclude")
const text = [
file ? (yield* read(file)).trimEnd() : "",
...list.map((item) => `/${item.replaceAll("\\", "/")}`),
]
.filter(Boolean)
.join("\n")
yield* fs.ensureDir(path.join(state.gitdir, "info")).pipe(Effect.orDie) yield* fs.ensureDir(path.join(state.gitdir, "info")).pipe(Effect.orDie)
if (!file) { yield* fs.writeFileString(target, text ? `${text}\n` : "").pipe(Effect.orDie)
yield* fs.writeFileString(target, "").pipe(Effect.orDie)
return
}
yield* fs.writeFileString(target, yield* read(file)).pipe(Effect.orDie)
}) })
const add = Effect.fnUntraced(function* () { const add = Effect.fnUntraced(function* () {
yield* sync() yield* sync()
yield* git([...cfg, ...args(["add", "."])], { cwd: state.directory }) const [diff, other] = yield* Effect.all(
[
git([...quote, ...args(["diff-files", "--name-only", "-z", "--", "."])], {
cwd: state.directory,
}),
git([...quote, ...args(["ls-files", "--others", "--exclude-standard", "-z", "--", "."])], {
cwd: state.directory,
}),
],
{ concurrency: 2 },
)
if (diff.code !== 0 || other.code !== 0) {
log.warn("failed to list snapshot files", {
diffCode: diff.code,
diffStderr: diff.stderr,
otherCode: other.code,
otherStderr: other.stderr,
})
return
}
const tracked = diff.text.split("\0").filter(Boolean)
const all = Array.from(new Set([...tracked, ...other.text.split("\0").filter(Boolean)]))
if (!all.length) return
const large = (yield* Effect.all(
all.map((item) =>
fs
.stat(path.join(state.directory, item))
.pipe(Effect.catch(() => Effect.void))
.pipe(
Effect.map((stat) => {
if (!stat || stat.type !== "File") return
const size = typeof stat.size === "bigint" ? Number(stat.size) : stat.size
return size > limit ? item : undefined
}),
),
),
{ concurrency: 8 },
)).filter((item): item is string => Boolean(item))
yield* sync(large)
const result = yield* git([...cfg, ...args(["add", "--sparse", "."])], { cwd: state.directory })
if (result.code !== 0) {
log.warn("failed to add snapshot files", {
exitCode: result.code,
stderr: result.stderr,
})
}
}) })
const cleanup = Effect.fnUntraced(function* () { const cleanup = Effect.fnUntraced(function* () {
@ -177,7 +227,7 @@ export namespace Snapshot {
const patch = Effect.fnUntraced(function* (hash: string) { const patch = Effect.fnUntraced(function* (hash: string) {
yield* add() yield* add()
const result = yield* git( const result = yield* git(
[...quote, ...args(["diff", "--no-ext-diff", "--name-only", hash, "--", "."])], [...quote, ...args(["diff", "--cached", "--no-ext-diff", "--name-only", hash, "--", "."])],
{ {
cwd: state.directory, cwd: state.directory,
}, },
@ -245,7 +295,7 @@ export namespace Snapshot {
const diff = Effect.fnUntraced(function* (hash: string) { const diff = Effect.fnUntraced(function* (hash: string) {
yield* add() yield* add()
const result = yield* git([...quote, ...args(["diff", "--no-ext-diff", hash, "--", "."])], { const result = yield* git([...quote, ...args(["diff", "--cached", "--no-ext-diff", hash, "--", "."])], {
cwd: state.worktree, cwd: state.worktree,
}) })
if (result.code !== 0) { if (result.code !== 0) {

View File

@ -181,7 +181,7 @@ test("symlink handling", async () => {
}) })
}) })
test("large file handling", async () => { test("file under size limit handling", async () => {
await using tmp = await bootstrap() await using tmp = await bootstrap()
await Instance.provide({ await Instance.provide({
directory: tmp.path, directory: tmp.path,
@ -196,6 +196,23 @@ test("large file handling", async () => {
}) })
}) })
test("large added files are skipped", async () => {
await using tmp = await bootstrap()
await Instance.provide({
directory: tmp.path,
fn: async () => {
const before = await Snapshot.track()
expect(before).toBeTruthy()
await Filesystem.write(`${tmp.path}/huge.txt`, new Uint8Array(2 * 1024 * 1024 + 1))
expect((await Snapshot.patch(before!)).files).toEqual([])
expect(await Snapshot.diff(before!)).toBe("")
expect(await Snapshot.track()).toBe(before)
},
})
})
test("nested directory revert", async () => { test("nested directory revert", async () => {
await using tmp = await bootstrap() await using tmp = await bootstrap()
await Instance.provide({ await Instance.provide({