Install generated-commit guard in existing shared hooks without replacing owner hooks
This commit is contained in:
parent
4292a5078a
commit
828c18f8f9
3 changed files with 121 additions and 0 deletions
59
scripts/install_shared_generated_guard.py
Normal file
59
scripts/install_shared_generated_guard.py
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Add the generated-commit guard to an existing shared hook directory.
|
||||
|
||||
Never changes core.hooksPath, other hooks, or a pre-existing different guard.
|
||||
The repo argument discovers the already-configured hook path; it is not a new
|
||||
fleet routing or authority configuration.
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shlex
|
||||
import subprocess
|
||||
|
||||
|
||||
CHECKER = Path(__file__).with_name("check_generated_only_commit.py").resolve()
|
||||
|
||||
|
||||
def install(repo, execute=False):
|
||||
repo = Path(repo).resolve()
|
||||
configured = subprocess.check_output(
|
||||
["git", "-C", str(repo), "config", "--get", "core.hooksPath"], text=True).strip()
|
||||
directory = Path(configured)
|
||||
if not directory.is_absolute():
|
||||
directory = repo / directory
|
||||
if not directory.is_dir() or directory.is_symlink():
|
||||
raise ValueError("existing hook directory must be real and present")
|
||||
target = directory / "pre-commit"
|
||||
content = ("#!/bin/sh\n# Custodian shared generated-commit guard; preserve other hooks.\n"
|
||||
"set -eu\nexec python3 " + shlex.quote(str(CHECKER)) + " --staged\n")
|
||||
if target.is_symlink():
|
||||
raise ValueError("refuse redirected existing pre-commit hook")
|
||||
if target.exists():
|
||||
if target.read_text() != content:
|
||||
raise ValueError("existing pre-commit hook differs; owner integration required")
|
||||
return {"repo": str(repo), "hook": str(target), "status": "installed", "changed": False}
|
||||
if execute:
|
||||
fd = os.open(target, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o755)
|
||||
with os.fdopen(fd, "w") as handle:
|
||||
handle.write(content)
|
||||
handle.flush()
|
||||
os.fsync(handle.fileno())
|
||||
return {"repo": str(repo), "hook": str(target),
|
||||
"status": "installed" if execute else "missing", "changed": execute}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--repo", type=Path, required=True)
|
||||
parser.add_argument("--install", action="store_true")
|
||||
args = parser.parse_args()
|
||||
import json
|
||||
try:
|
||||
print(json.dumps(install(args.repo, args.install), sort_keys=True))
|
||||
except (ValueError, OSError, subprocess.SubprocessError):
|
||||
parser.exit(1, "Shared hook unverified; existing files and routing are not replaced.\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue