fix(opencode): avoid snapshotting files over 2MB (#19043)
parent
4f9667c4bb
commit
0a80ef4278
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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({
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue