




























11// Git hook tests validate pre-commit hook behavior and scripts.
22import { execFileSync } from "node:child_process";
3-import { mkdirSync, symlinkSync, writeFileSync } from "node:fs";
3+import { existsSync, mkdirSync, symlinkSync, writeFileSync } from "node:fs";
44import path from "node:path";
55import { afterEach, describe, expect, it } from "vitest";
66import { cleanupTempDirs, makeTempRepoRoot } from "./helpers/temp-repo.js";
@@ -20,6 +20,35 @@ const run = (cwd: string, cmd: string, args: string[] = [], env?: NodeJS.Process
2020}).trim();
2121};
222223+type FailedCommand = {
24+status: number;
25+stderr: string;
26+stdout: string;
27+};
28+29+const runFailure = (
30+cwd: string,
31+cmd: string,
32+args: string[] = [],
33+env?: NodeJS.ProcessEnv,
34+): FailedCommand => {
35+try {
36+run(cwd, cmd, args, env);
37+} catch (error) {
38+if (error instanceof Error && "status" in error) {
39+const failure = error as Error & { status?: number; stderr?: string; stdout?: string };
40+return {
41+status: failure.status ?? 1,
42+stderr: String(failure.stderr ?? ""),
43+stdout: String(failure.stdout ?? ""),
44+};
45+}
46+throw error;
47+}
48+49+throw new Error("expected command to fail");
50+};
51+2352function writeExecutable(dir: string, name: string, contents: string): void {
2453writeFileSync(path.join(dir, name), contents, {
2554encoding: "utf8",
@@ -54,6 +83,14 @@ function installPreCommitFixture(dir: string): string {
5483return fakeBinDir;
5584}
568586+function installRunNodeToolFixture(dir: string): void {
87+mkdirSync(path.join(dir, "scripts", "pre-commit"), { recursive: true });
88+symlinkSync(
89+path.join(process.cwd(), "scripts", "pre-commit", "run-node-tool.sh"),
90+path.join(dir, "scripts", "pre-commit", "run-node-tool.sh"),
91+);
92+}
93+5794function splitNonEmptyLines(output: string): string[] {
5895const lines: string[] = [];
5996for (const line of output.split("\n")) {
@@ -163,3 +200,56 @@ describe("git-hooks/pre-commit (integration)", () => {
163200expect(run(dir, "git", ["diff", "--cached", "--name-only"])).toBe("tracked.txt");
164201});
165202});
203+204+describe("scripts/pre-commit/run-node-tool.sh", () => {
205+it("runs the installed local tool without invoking pnpm", () => {
206+const dir = makeTempRepoRoot(tempDirs, "openclaw-run-node-tool-local-");
207+installRunNodeToolFixture(dir);
208+writeFileSync(path.join(dir, "pnpm-lock.yaml"), "lockfileVersion: '9.0'\n", "utf8");
209+210+const fakeBinDir = path.join(dir, "bin");
211+const toolBinDir = path.join(dir, "node_modules", ".bin");
212+mkdirSync(fakeBinDir, { recursive: true });
213+mkdirSync(toolBinDir, { recursive: true });
214+writeExecutable(
215+fakeBinDir,
216+"pnpm",
217+"#!/usr/bin/env bash\necho 'pnpm should not run from run-node-tool' >&2\nexit 99\n",
218+);
219+writeExecutable(toolBinDir, "oxfmt", "#!/usr/bin/env bash\nprintf 'local:%s\\n' \"$*\"\n");
220+221+expect(
222+run(dir, "bash", ["scripts/pre-commit/run-node-tool.sh", "oxfmt", "--write", "a.ts"], {
223+PATH: `${fakeBinDir}:${process.env.PATH ?? ""}`,
224+}),
225+).toBe("local:--write a.ts");
226+});
227+228+it("fails before pnpm can hydrate dependencies when node_modules is missing", () => {
229+const dir = makeTempRepoRoot(tempDirs, "openclaw-run-node-tool-missing-deps-");
230+installRunNodeToolFixture(dir);
231+writeFileSync(path.join(dir, "pnpm-lock.yaml"), "lockfileVersion: '9.0'\n", "utf8");
232+233+const fakeBinDir = path.join(dir, "bin");
234+const markerPath = path.join(dir, "pnpm-called");
235+mkdirSync(fakeBinDir, { recursive: true });
236+writeExecutable(
237+fakeBinDir,
238+"pnpm",
239+`#!/usr/bin/env bash\ntouch ${JSON.stringify(markerPath)}\nexit 99\n`,
240+);
241+242+const result = runFailure(
243+dir,
244+"bash",
245+["scripts/pre-commit/run-node-tool.sh", "oxfmt", "--write", "a.ts"],
246+{ PATH: `${fakeBinDir}:${process.env.PATH ?? ""}` },
247+);
248+249+expect(result.status).toBe(1);
250+expect(result.stderr).toContain(
251+"Missing repo dependencies: cannot run oxfmt without node_modules.",
252+);
253+expect(existsSync(markerPath)).toBe(false);
254+});
255+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。