mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-09 15:59:30 +00:00
fix(git-hooks): skip sequencer pre-commit formatting (#95842)
* fix(git-hooks): skip sequencer pre-commit formatting * chore: rerun CI * fix(git-hooks): skip revert sequencer formatting --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
parent
08b7fddf80
commit
af906225fa
2 changed files with 144 additions and 3 deletions
|
|
@ -16,6 +16,18 @@ if [[ ! -f "$FILTER_FILES" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
GIT_DIR="$(git rev-parse --git-dir 2>/dev/null || true)"
|
||||
if [[ -n "$GIT_DIR" ]] && \
|
||||
{ [[ -f "$GIT_DIR/MERGE_HEAD" ]] || \
|
||||
[[ -f "$GIT_DIR/CHERRY_PICK_HEAD" ]] || \
|
||||
[[ -f "$GIT_DIR/REVERT_HEAD" ]] || \
|
||||
[[ -f "$GIT_DIR/REBASE_HEAD" ]] || \
|
||||
[[ -d "$GIT_DIR/rebase-merge" ]] || \
|
||||
[[ -d "$GIT_DIR/rebase-apply" ]]; }; then
|
||||
# Sequencer commits stage the operation result, not just the user's local edits.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Security: avoid option-injection from malicious file names (e.g. "--all", "--force").
|
||||
# Robustness: NUL-delimited file list handles spaces/newlines safely.
|
||||
# Compatibility: use read loops instead of `mapfile` so this runs on macOS Bash 3.x.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Git hook tests validate pre-commit hook behavior and scripts.
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { existsSync, mkdirSync, symlinkSync, writeFileSync } from "node:fs";
|
||||
import { existsSync, mkdirSync, readFileSync, symlinkSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { cleanupTempDirs, makeTempRepoRoot } from "./helpers/temp-repo.js";
|
||||
|
|
@ -39,8 +39,8 @@ const runFailure = (
|
|||
const failure = error as Error & { status?: number; stderr?: string; stdout?: string };
|
||||
return {
|
||||
status: failure.status ?? 1,
|
||||
stderr: String(failure.stderr ?? ""),
|
||||
stdout: String(failure.stdout ?? ""),
|
||||
stderr: failure.stderr ?? "",
|
||||
stdout: failure.stdout ?? "",
|
||||
};
|
||||
}
|
||||
throw error;
|
||||
|
|
@ -83,6 +83,34 @@ function installPreCommitFixture(dir: string): string {
|
|||
return fakeBinDir;
|
||||
}
|
||||
|
||||
function installFormattingRecorder(dir: string): string {
|
||||
const logPath = path.join(dir, "hook-tool.log");
|
||||
writeFileSync(
|
||||
path.join(dir, "scripts", "pre-commit", "filter-staged-files.mjs"),
|
||||
`const files = process.argv.slice(3).filter((arg) => arg !== "--");
|
||||
for (const file of files) {
|
||||
if (file.endsWith(".ts")) {
|
||||
process.stdout.write(file);
|
||||
process.stdout.write("\0");
|
||||
}
|
||||
}
|
||||
`,
|
||||
"utf8",
|
||||
);
|
||||
writeFileSync(
|
||||
path.join(dir, "scripts", "pre-commit", "run-node-tool.sh"),
|
||||
`#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
printf '%s\n' "$*" >> ${JSON.stringify(logPath)}
|
||||
`,
|
||||
{
|
||||
encoding: "utf8",
|
||||
mode: 0o755,
|
||||
},
|
||||
);
|
||||
return logPath;
|
||||
}
|
||||
|
||||
function installRunNodeToolFixture(dir: string): void {
|
||||
mkdirSync(path.join(dir, "scripts", "pre-commit"), { recursive: true });
|
||||
symlinkSync(
|
||||
|
|
@ -101,6 +129,13 @@ function splitNonEmptyLines(output: string): string[] {
|
|||
return lines;
|
||||
}
|
||||
|
||||
function readFormatterLog(logPath: string): string[] {
|
||||
if (!existsSync(logPath)) {
|
||||
return [];
|
||||
}
|
||||
return splitNonEmptyLines(readFileSync(logPath, "utf8"));
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
cleanupTempDirs(tempDirs);
|
||||
});
|
||||
|
|
@ -128,6 +163,100 @@ describe("git-hooks/pre-commit (integration)", () => {
|
|||
expect(staged).toEqual(["--all"]);
|
||||
});
|
||||
|
||||
it("skips formatting staged files while a merge commit is in progress", () => {
|
||||
const dir = makeTempRepoRoot(tempDirs, "openclaw-pre-commit-merge-");
|
||||
run(dir, "git", ["init", "-q", "--initial-branch=main"]);
|
||||
installPreCommitFixture(dir);
|
||||
const logPath = installFormattingRecorder(dir);
|
||||
|
||||
writeFileSync(path.join(dir, "changed.ts"), "export const value = 1;\n", "utf8");
|
||||
run(dir, "git", ["add", "--", "changed.ts"]);
|
||||
run(dir, "git", [
|
||||
"-c",
|
||||
"user.name=Test User",
|
||||
"-c",
|
||||
"user.email=test@example.invalid",
|
||||
"commit",
|
||||
"-q",
|
||||
"-m",
|
||||
"initial",
|
||||
]);
|
||||
run(dir, "git", ["checkout", "-q", "-b", "side"]);
|
||||
writeFileSync(path.join(dir, "changed.ts"), "export const value = 2;\n", "utf8");
|
||||
run(dir, "git", ["add", "--", "changed.ts"]);
|
||||
run(dir, "git", [
|
||||
"-c",
|
||||
"user.name=Test User",
|
||||
"-c",
|
||||
"user.email=test@example.invalid",
|
||||
"commit",
|
||||
"-q",
|
||||
"-m",
|
||||
"side change",
|
||||
]);
|
||||
run(dir, "git", ["checkout", "-q", "main"]);
|
||||
run(dir, "git", [
|
||||
"-c",
|
||||
"user.name=Test User",
|
||||
"-c",
|
||||
"user.email=test@example.invalid",
|
||||
"merge",
|
||||
"--no-commit",
|
||||
"--no-ff",
|
||||
"side",
|
||||
]);
|
||||
|
||||
expect(existsSync(path.join(dir, ".git", "MERGE_HEAD"))).toBe(true);
|
||||
expect(run(dir, "git", ["diff", "--cached", "--name-only"])).toBe("changed.ts");
|
||||
|
||||
run(dir, "bash", ["git-hooks/pre-commit"]);
|
||||
|
||||
expect(readFormatterLog(logPath)).toEqual([]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["cherry-pick", "CHERRY_PICK_HEAD", "file"],
|
||||
["revert", "REVERT_HEAD", "file"],
|
||||
["rebase head", "REBASE_HEAD", "file"],
|
||||
["merge rebase state", "rebase-merge", "dir"],
|
||||
["apply rebase state", "rebase-apply", "dir"],
|
||||
])("skips formatting staged files while %s metadata is present", (_label, gitPath, kind) => {
|
||||
const dir = makeTempRepoRoot(tempDirs, "openclaw-pre-commit-sequencer-");
|
||||
run(dir, "git", ["init", "-q", "--initial-branch=main"]);
|
||||
installPreCommitFixture(dir);
|
||||
const logPath = installFormattingRecorder(dir);
|
||||
|
||||
writeFileSync(path.join(dir, "changed.ts"), "export const value = 1;\n", "utf8");
|
||||
run(dir, "git", ["add", "--", "changed.ts"]);
|
||||
|
||||
const metadataPath = path.join(dir, ".git", gitPath);
|
||||
if (kind === "dir") {
|
||||
mkdirSync(metadataPath, { recursive: true });
|
||||
} else {
|
||||
writeFileSync(metadataPath, "sequencer state\n", "utf8");
|
||||
}
|
||||
|
||||
run(dir, "bash", ["git-hooks/pre-commit"]);
|
||||
|
||||
expect(readFormatterLog(logPath)).toEqual([]);
|
||||
});
|
||||
|
||||
it("still formats staged files during a normal commit", () => {
|
||||
const dir = makeTempRepoRoot(tempDirs, "openclaw-pre-commit-normal-");
|
||||
run(dir, "git", ["init", "-q", "--initial-branch=main"]);
|
||||
installPreCommitFixture(dir);
|
||||
const logPath = installFormattingRecorder(dir);
|
||||
|
||||
writeFileSync(path.join(dir, "changed.ts"), "export const value = 1;\n", "utf8");
|
||||
run(dir, "git", ["add", "--", "changed.ts"]);
|
||||
|
||||
run(dir, "bash", ["git-hooks/pre-commit"]);
|
||||
|
||||
expect(readFormatterLog(logPath)).toEqual([
|
||||
"oxfmt --write --no-error-on-unmatched-pattern changed.ts",
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not run the changed-scope check for non-doc staged changes", () => {
|
||||
const dir = makeTempRepoRoot(tempDirs, "openclaw-pre-commit-no-check-changed-");
|
||||
run(dir, "git", ["init", "-q", "--initial-branch=main"]);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue