import fs from "node:fs"; import path from "node:path"; import { spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; const DEFAULT_HOOKS_PATH = "git-hooks"; const PRE_COMMIT_HOOK = "pre-commit"; function getRepoRoot() { const here = path.dirname(fileURLToPath(import.meta.url)); return path.resolve(here, ".."); } function runGitCommand(args, options = {}) { return spawnSync("git", args, { cwd: options.cwd, encoding: "utf-8", stdio: options.stdio ?? "pipe", }); } function ensureExecutable(targetPath) { if (process.platform === "win32") return; if (!fs.existsSync(targetPath)) return; try { const mode = fs.statSync(targetPath).mode & 0o777; if (mode & 0o100) return; fs.chmodSync(targetPath, 0o755); } catch (err) { console.warn(`[setup-git-hooks] chmod failed: ${err}`); } } function isGitAvailable({ repoRoot = getRepoRoot(), runGit = runGitCommand } = {}) { const result = runGit(["--version"], { cwd: repoRoot, stdio: "ignore" }); return result.status === 0; } function isGitRepo({ repoRoot = getRepoRoot(), runGit = runGitCommand } = {}) { const result = runGit(["rev-parse", "--is-inside-work-tree"], { cwd: repoRoot, stdio: "pipe", }); if (result.status !== 0) return false; return String(result.stdout ?? "").trim() === "true"; } function setHooksPath({ repoRoot = getRepoRoot(), hooksPath = DEFAULT_HOOKS_PATH, runGit = runGitCommand, } = {}) { const result = runGit(["config", "core.hooksPath", hooksPath], { cwd: repoRoot, stdio: "ignore", }); return result.status === 0; } function setupGitHooks({ repoRoot = getRepoRoot(), hooksPath = DEFAULT_HOOKS_PATH, runGit = runGitCommand, } = {}) { if (!isGitAvailable({ repoRoot, runGit })) { return { ok: false, reason: "git-missing" }; } if (!isGitRepo({ repoRoot, runGit })) { return { ok: false, reason: "not-repo" }; } if (!setHooksPath({ repoRoot, hooksPath, runGit })) { return { ok: false, reason: "config-failed" }; } ensureExecutable(path.join(repoRoot, hooksPath, PRE_COMMIT_HOOK)); return { ok: true }; } export { DEFAULT_HOOKS_PATH, PRE_COMMIT_HOOK, ensureExecutable, getRepoRoot, isGitAvailable, isGitRepo, runGitCommand, setHooksPath, setupGitHooks, }; if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { setupGitHooks(); }