Coverage for src / rhiza_hooks / update_readme_help.py: 100%
54 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-08 08:53 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-08 08:53 +0000
1#!/usr/bin/env python3
2"""Script to update README with Makefile help output.
4This hook runs 'make help' and embeds the output into README.md
5between special marker comments.
7Migrated from rhiza's local pre-commit hook that runs 'make readme'.
8This is a Python wrapper that provides the same functionality.
9"""
11from __future__ import annotations
13import re
14import subprocess # nosec B404
15import sys
16from pathlib import Path
18# Markers used to identify the section to update in README
19START_MARKER = "<!-- MAKE_HELP_START -->"
20END_MARKER = "<!-- MAKE_HELP_END -->"
23def get_make_help_output() -> str | None:
24 """Run 'make help' and capture the output.
26 Returns:
27 The output from 'make help', or None if the command fails.
28 """
29 try:
30 result = subprocess.run( # nosec B603 B607
31 ["make", "help"],
32 capture_output=True,
33 text=True,
34 check=True,
35 timeout=30,
36 )
37 except subprocess.CalledProcessError as e:
38 print(f"Error running 'make help': {e}")
39 return None
40 except subprocess.TimeoutExpired:
41 print("Error: 'make help' timed out")
42 return None
43 except FileNotFoundError:
44 print("Error: 'make' command not found")
45 return None
46 else:
47 return result.stdout
50def update_readme_with_help(readme_path: Path, help_output: str) -> bool:
51 """Update README.md with the make help output.
53 Args:
54 readme_path: Path to the README.md file.
55 help_output: The output from 'make help'.
57 Returns:
58 True if the file was modified, False otherwise.
59 """
60 if not readme_path.exists():
61 print(f"Warning: {readme_path} not found, skipping update")
62 return False
64 content = readme_path.read_text()
66 # Check if markers exist
67 if START_MARKER not in content or END_MARKER not in content:
68 # No markers, nothing to update
69 return False
71 # Build the new content between markers
72 new_section = f"{START_MARKER}\n```\n{help_output}```\n{END_MARKER}"
74 # Replace the content between markers
75 pattern = re.compile(
76 re.escape(START_MARKER) + r".*?" + re.escape(END_MARKER),
77 re.DOTALL,
78 )
79 new_content = pattern.sub(new_section, content)
81 if new_content != content:
82 readme_path.write_text(new_content)
83 print(f"Updated {readme_path} with make help output")
84 return True
86 return False
89def find_repo_root() -> Path:
90 """Find the repository root directory.
92 Returns:
93 Path to the repository root.
94 """
95 current = Path.cwd()
96 while current != current.parent:
97 if (current / ".git").exists():
98 return current
99 current = current.parent
100 return Path.cwd()
103def main(argv: list[str] | None = None) -> int:
104 """Execute the script."""
105 # This hook doesn't use filenames, it always operates on the repo root
106 _ = argv # Unused
108 repo_root = find_repo_root()
109 readme_path = repo_root / "README.md"
111 help_output = get_make_help_output()
112 if help_output is None:
113 # If make help fails, we don't fail the hook
114 # This allows the hook to be used in repos without a Makefile
115 return 0
117 if update_readme_with_help(readme_path, help_output):
118 # File was modified, fail so pre-commit knows to re-stage
119 return 1
121 return 0
124if __name__ == "__main__":
125 sys.exit(main())