feat(FN-5089): complete Step 1 — add commit-msg hook installer
Fusion-Task-Id: FN-5089 Fusion-Task-Lineage: ad7ea32a-c024-45bb-b68e-b65665b9b7c6
This commit is contained in:
committed by
gsxdsm
parent
17ace6fef0
commit
96eb610800
@@ -6,6 +6,7 @@ import { promisify } from "node:util";
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
export const DEFAULT_ALLOWED_BRANCH_PATTERNS = ["^fusion/step-\\d+-[a-z0-9-]+$"] as const;
|
||||
const COMMIT_MSG_HOOK_MARKER = "# fusion-managed-commit-msg-hook";
|
||||
|
||||
function toShellCasePattern(pattern: string): string {
|
||||
return pattern
|
||||
@@ -61,6 +62,42 @@ async function resolveGitPath(worktreePath: string, gitPath: string): Promise<st
|
||||
}
|
||||
}
|
||||
|
||||
export function buildCommitMsgTrailerHook(
|
||||
taskId: string,
|
||||
options: {
|
||||
taskPrefix?: string;
|
||||
trailerName?: string;
|
||||
} = {}
|
||||
): string {
|
||||
const taskPrefix = (options.taskPrefix ?? "FN").trim() || "FN";
|
||||
const trailerName = (options.trailerName ?? "Fusion-Task-Id").trim() || "Fusion-Task-Id";
|
||||
|
||||
return `#!/bin/sh
|
||||
set -eu
|
||||
${COMMIT_MSG_HOOK_MARKER}
|
||||
# fusion-task-id-seed: ${taskId}
|
||||
|
||||
TASK_FILE=$(git rev-parse --git-path fusion-task-id)
|
||||
[ -f "$TASK_FILE" ] || exit 0
|
||||
TASK_ID=$(cat "$TASK_FILE")
|
||||
[ -n "$TASK_ID" ] || exit 0
|
||||
|
||||
PREFIX=${JSON.stringify(taskPrefix)}
|
||||
case "$TASK_ID" in
|
||||
${taskPrefix}-*) ;;
|
||||
*) TASK_ID="$PREFIX-$(printf '%s' "$TASK_ID" | sed -E "s/^${taskPrefix}-//i")" ;;
|
||||
esac
|
||||
|
||||
TRAILER_NAME=${JSON.stringify(trailerName)}
|
||||
|
||||
git interpret-trailers \
|
||||
--in-place \
|
||||
--if-exists doNothing \
|
||||
--trailer "$TRAILER_NAME: $TASK_ID" \
|
||||
"$1"
|
||||
`;
|
||||
}
|
||||
|
||||
async function writeFileAtomic(targetPath: string, content: string, mode?: number): Promise<void> {
|
||||
await execAsync(`mkdir -p ${JSON.stringify(dirname(targetPath))}`);
|
||||
const tmpPath = `${targetPath}.tmp`;
|
||||
@@ -71,10 +108,35 @@ async function writeFileAtomic(targetPath: string, content: string, mode?: numbe
|
||||
await fs.rename(tmpPath, targetPath);
|
||||
}
|
||||
|
||||
async function installCommitMsgHook(input: {
|
||||
worktreePath: string;
|
||||
taskId: string;
|
||||
taskPrefix: string;
|
||||
trailerName: string;
|
||||
}): Promise<void> {
|
||||
const hookPath = await resolveGitPath(input.worktreePath, "hooks/commit-msg");
|
||||
const existing = await fs.readFile(hookPath, "utf-8").catch(() => null);
|
||||
if (existing && !existing.includes(COMMIT_MSG_HOOK_MARKER)) {
|
||||
console.warn(
|
||||
`[worktree-hooks] commit-msg hook already exists at ${hookPath}; skipping Fusion trailer hook install for ${input.taskId}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const hook = buildCommitMsgTrailerHook(input.taskId, {
|
||||
taskPrefix: input.taskPrefix,
|
||||
trailerName: input.trailerName,
|
||||
});
|
||||
await writeFileAtomic(hookPath, hook, 0o755);
|
||||
}
|
||||
|
||||
export async function installTaskWorktreeIdentityGuard(input: {
|
||||
worktreePath: string;
|
||||
taskId: string;
|
||||
allowedBranchPatterns?: readonly string[];
|
||||
commitMsgHookEnabled?: boolean;
|
||||
taskPrefix?: string;
|
||||
taskAttributionTrailerName?: string;
|
||||
}): Promise<void> {
|
||||
const hook = buildIdentityGuardHook(input.taskId, input.allowedBranchPatterns ?? DEFAULT_ALLOWED_BRANCH_PATTERNS);
|
||||
const metadataPath = await resolveGitPath(input.worktreePath, "fusion-task-id");
|
||||
@@ -82,4 +144,13 @@ export async function installTaskWorktreeIdentityGuard(input: {
|
||||
|
||||
await writeFileAtomic(metadataPath, `${input.taskId}\n`);
|
||||
await writeFileAtomic(hookPath, hook, 0o755);
|
||||
|
||||
if (input.commitMsgHookEnabled !== false) {
|
||||
await installCommitMsgHook({
|
||||
worktreePath: input.worktreePath,
|
||||
taskId: input.taskId,
|
||||
taskPrefix: input.taskPrefix ?? "FN",
|
||||
trailerName: input.taskAttributionTrailerName ?? "Fusion-Task-Id",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user