fix(scripts): bound check-file-utils git lookup (#111582)

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: lijing <li.jing25@xydigit.com>
This commit is contained in:
coaiMax
2026-08-03 21:42:47 +08:00
committed by GitHub
parent 5541eef4f2
commit 1c5dd1a39b
2 changed files with 55 additions and 1 deletions
+4
View File
@@ -14,6 +14,8 @@ export const REPO_SCAN_SKIPPED_DIR_NAMES: ReadonlySet<string> = new Set([
"node_modules",
"vendor",
]);
// Bound the Git lookup before falling back to direct filesystem traversal.
const GIT_LS_FILES_TIMEOUT_MS = 30_000;
export function isCodeFile(filePath: string): boolean {
if (filePath.endsWith(".d.ts")) {
@@ -89,6 +91,8 @@ export function listRepoFilesSync(
return execFileSync("git", ["-C", repoRoot, "ls-files", "--", ...roots], {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"],
timeout: GIT_LS_FILES_TIMEOUT_MS,
killSignal: "SIGKILL",
})
.split(/\r?\n/u)
.filter(Boolean)
+51 -1
View File
@@ -1,15 +1,23 @@
// Check File Utils tests cover check file utils script behavior.
import fs from "node:fs";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
collectFilesSync,
isCodeFile,
listRepoFilesSync,
relativeToCwd,
toPosixPath,
} from "../../scripts/check-file-utils.js";
import { createScriptTestHarness } from "./test-helpers.js";
const execFileSyncMock = vi.hoisted(() => vi.fn(() => ""));
vi.mock("node:child_process", async (importOriginal) => {
const original = (await importOriginal()) as typeof import("node:child_process");
return { ...original, execFileSync: execFileSyncMock };
});
const { createTempDir } = createScriptTestHarness();
describe("scripts/check-file-utils isCodeFile", () => {
@@ -64,3 +72,45 @@ describe("scripts/check-file-utils relativeToCwd", () => {
);
});
});
describe("scripts/check-file-utils listRepoFilesSync", () => {
afterEach(() => {
execFileSyncMock.mockReset();
});
it("bounds git ls-files with a timeout and kill signal", () => {
execFileSyncMock.mockReturnValue("src/keep.ts\nsrc/skip.d.ts\n");
expect(
listRepoFilesSync("/fake/repo", {
includeFile: (filePath) => isCodeFile(filePath),
}),
).toEqual(["src/keep.ts"]);
expect(execFileSyncMock).toHaveBeenCalledWith(
"git",
expect.arrayContaining(["-C", "/fake/repo", "ls-files", "--"]),
expect.objectContaining({
timeout: 30_000,
killSignal: "SIGKILL",
}),
);
});
it("falls back to filesystem traversal when git ls-files times out", () => {
const error: NodeJS.ErrnoException & { signal?: string } = new Error("Command timed out");
error.code = "ETIMEDOUT";
error.signal = "SIGKILL";
execFileSyncMock.mockImplementation(() => {
throw error;
});
const rootDir = createTempDir("openclaw-check-file-utils-fallback-");
fs.mkdirSync(path.join(rootDir, "src"), { recursive: true });
fs.writeFileSync(path.join(rootDir, "src", "keep.ts"), "");
expect(
listRepoFilesSync(rootDir, {
includeFile: (filePath) => filePath.endsWith(".ts"),
}),
).toEqual(["src/keep.ts"]);
});
});