Files
openclaw/test/git-hooks-pre-commit.test-support.ts
Peter Steinberger df4f083924 fix(git-hooks): format ordinary commits with orphan rebase refs (#137997)
* fix(git-hooks): format ordinary commits with orphan rebase refs

* test(git-hooks): group operation-state boundary coverage

* test(gateway): settle synthetic restart before teardown
2026-09-04 01:26:56 -07:00

159 lines
4.5 KiB
TypeScript

import { execFileSync } from "node:child_process";
import {
copyFileSync,
existsSync,
mkdirSync,
readFileSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import path from "node:path";
import { makeTempDir as makeTempRepoRoot } from "./helpers/temp-dir.js";
const baseGitEnv = {
GIT_CONFIG_NOSYSTEM: "1",
GIT_CONFIG_GLOBAL: "/dev/null",
GIT_TERMINAL_PROMPT: "0",
GIT_CONFIG_COUNT: "4",
GIT_CONFIG_KEY_0: "user.name",
GIT_CONFIG_VALUE_0: "Hook Test",
GIT_CONFIG_KEY_1: "user.email",
GIT_CONFIG_VALUE_1: "hook@example.invalid",
GIT_CONFIG_KEY_2: "commit.gpgSign",
GIT_CONFIG_VALUE_2: "false",
GIT_CONFIG_KEY_3: "init.templateDir",
GIT_CONFIG_VALUE_3: "",
};
export const rulePath = ".git/private rules.txt";
export const ruleSetting = "hooks.blockedLiteralsFile";
export const literals = ["GUARD_SYNTHETIC_ALPHA", "GUARD_SYNTHETIC_BETA_[x].*42"] as const;
export const run = (cwd: string, cmd: string, args: string[] = [], env?: NodeJS.ProcessEnv) => {
return execFileSync(cmd, args, {
cwd,
encoding: "utf8",
stdio: "pipe",
timeout: 20_000,
maxBuffer: 24 * 1024 * 1024,
env: {
PATH: process.env.PATH,
HOME: cwd,
TMPDIR: cwd,
LC_ALL: "C",
TZ: "UTC",
...baseGitEnv,
...env,
},
}).trim();
};
type FailedCommand = {
status: number;
stderr: string;
stdout: string;
};
export const runFailure = (
cwd: string,
cmd: string,
args: string[] = [],
env?: NodeJS.ProcessEnv,
): FailedCommand => {
try {
run(cwd, cmd, args, env);
} catch (error) {
if (error instanceof Error && "status" in error) {
const failure = error as Error & { status?: number; stderr?: string; stdout?: string };
return {
status: failure.status ?? 1,
stderr: failure.stderr ?? "",
stdout: failure.stdout ?? "",
};
}
throw error;
}
throw new Error("expected command to fail");
};
export function writeExecutable(dir: string, name: string, contents: string): void {
writeFileSync(path.join(dir, name), contents, {
encoding: "utf8",
mode: 0o755,
});
}
export function installPreCommitFixture(dir: string): string {
mkdirSync(path.join(dir, "git-hooks"), { recursive: true });
mkdirSync(path.join(dir, "scripts", "pre-commit"), { recursive: true });
symlinkSync(
path.join(process.cwd(), "git-hooks", "pre-commit"),
path.join(dir, "git-hooks", "pre-commit"),
);
for (const name of [
"run-node-tool.sh",
"filter-staged-files.mjs",
"guard-staged-content.mjs",
"format-staged.sh",
]) {
copyFileSync(
path.join(process.cwd(), "scripts/pre-commit", name),
path.join(dir, "scripts/pre-commit", name),
);
}
const privateRules = path.resolve(
dir,
run(dir, "git", ["rev-parse", "--git-path", "private rules.txt"]),
);
writeFileSync(privateRules, `${literals.join("\n")}\n`, { mode: 0o600 });
run(dir, "git", ["config", "--local", ruleSetting, privateRules]);
mkdirSync(path.join(dir, "node_modules/.bin"), { recursive: true });
// Stdin mode must echo the blob or the hook treats the empty output as formatter failure.
writeExecutable(
path.join(dir, "node_modules/.bin"),
"oxfmt",
'#!/bin/sh\ncase "$*" in *--stdin-filepath=*) cat ;; esac\nexit 0\n',
);
const fakeBinDir = path.join(dir, "bin");
mkdirSync(fakeBinDir, { recursive: true });
return fakeBinDir;
}
export function createContentGuardFixture(tempDirs: string[]): string {
const dir = makeTempRepoRoot(tempDirs, "openclaw-content-guard-");
run(dir, "git", ["init", "-q", "--initial-branch=main"]);
installPreCommitFixture(dir);
return dir;
}
export function stageContent(dir: string, name: string, content: string | Buffer): void {
mkdirSync(path.dirname(path.join(dir, name)), { recursive: true });
writeFileSync(path.join(dir, name), content);
run(dir, "git", ["--literal-pathspecs", "add", "-f", "--", name]);
}
export const commitArgs = ["-c", "core.hooksPath=git-hooks", "commit", "-qm", "guard proof"];
export function installFormattingRecorder(dir: string, body = ""): string {
const logPath = path.join(dir, "hook-tool.log");
writeExecutable(
path.join(dir, "node_modules/.bin"),
"oxfmt",
`#!/usr/bin/env bash
set -euo pipefail
printf 'oxfmt %s\n' "$*" >> hook-tool.log
case "$*" in *--stdin-filepath=*) cat ;; esac
${body}
`,
);
return logPath;
}
export function readFormatterLog(logPath: string): string[] {
if (!existsSync(logPath)) {
return [];
}
return readFileSync(logPath, "utf8").split("\n").filter(Boolean);
}