Skip to content

update_readme_help

rhiza_hooks.update_readme_help

Script to update README with Makefile help output.

This hook runs 'make help' and embeds the output into README.md between special marker comments.

Migrated from rhiza's local pre-commit hook that runs 'make readme'. This is a Python wrapper that provides the same functionality.

get_make_help_output()

Run 'make help' and capture the output.

Returns:

Type Description
str | None

The output from 'make help', or None if the command fails.

Source code in rhiza_hooks/update_readme_help.py
def get_make_help_output() -> str | None:
    """Run 'make help' and capture the output.

    Returns:
        The output from 'make help', or None if the command fails.
    """
    try:
        result = subprocess.run(  # nosec B603 B607
            ["make", "help"],  # noqa: S607
            capture_output=True,
            text=True,
            check=True,
            timeout=30,
        )
    except subprocess.CalledProcessError as e:
        print(f"Error running 'make help': {e}")
        return None
    except subprocess.TimeoutExpired:
        print("Error: 'make help' timed out")
        return None
    except FileNotFoundError:
        print("Error: 'make' command not found")
        return None
    else:
        return result.stdout

main(argv=None)

Execute the script.

Source code in rhiza_hooks/update_readme_help.py
def main(argv: list[str] | None = None) -> int:
    """Execute the script."""
    # This hook doesn't use filenames, it always operates on the repo root
    _ = argv  # Unused  # pragma: no mutate  # equivalent: value is never read

    repo_root = find_repo_root()
    readme_path = repo_root / "README.md"

    help_output = get_make_help_output()
    if help_output is None:
        # If make help fails, we don't fail the hook
        # This allows the hook to be used in repos without a Makefile
        return 0

    if update_readme_with_help(readme_path, help_output):
        # File was modified, fail so pre-commit knows to re-stage
        return 1

    return 0

update_readme_with_help(readme_path, help_output)

Update README.md with the make help output.

Parameters:

Name Type Description Default
readme_path Path

Path to the README.md file.

required
help_output str

The output from 'make help'.

required

Returns:

Type Description
bool

True if the file was modified, False otherwise.

Source code in rhiza_hooks/update_readme_help.py
def update_readme_with_help(readme_path: Path, help_output: str) -> bool:
    """Update README.md with the make help output.

    Args:
        readme_path: Path to the README.md file.
        help_output: The output from 'make help'.

    Returns:
        True if the file was modified, False otherwise.
    """
    if not readme_path.exists():
        print(f"Warning: {readme_path} not found, skipping update")
        return False

    content = readme_path.read_text(encoding="utf-8")

    # Check if markers exist
    # pragma below: equivalent mutant — with only one marker present the substitution
    # pattern (START.*?END) cannot match either way, so `or`->`and` changes no observable
    # behaviour (return value and file contents are identical).
    if START_MARKER not in content or END_MARKER not in content:  # pragma: no mutate
        # No markers, nothing to update
        return False

    # Build the new content between markers
    new_section = f"{START_MARKER}\n```\n{help_output}```\n{END_MARKER}"

    # Replace the content between markers
    pattern = re.compile(
        re.escape(START_MARKER) + r".*?" + re.escape(END_MARKER),
        re.DOTALL,
    )
    new_content = pattern.sub(new_section, content)

    if new_content != content:
        # newline="" suppresses the \n -> os.linesep translation. `content` came back
        # from a universal-newline read and `help_output` from a text-mode subprocess,
        # so `new_content` is all \n; without this the write would emit CRLF on Windows
        # and reflow the entire README instead of just the help block.
        readme_path.write_text(new_content, encoding="utf-8", newline="")
        print(f"Updated {readme_path} with make help output")
        return True

    return False