#!/usr/bin/env python3 import importlib import os from pathlib import Path from typing import Iterator import click import git import toml from clickusagelib.githook import install_hook, uninstall_hook def find_git_root(path) -> Path: git_repo = git.Repo(path, search_parent_directories=True) git_root = git_repo.git.rev_parse("--show-toplevel") return Path(git_root) def iter_commands( tool_name: str, cmd_chain: list, cliobj: click.Group | click.Command, depth: int = 1, ) -> Iterator[str]: assert isinstance(cliobj, click.Command) or isinstance(cliobj, click.Group) if isinstance(cliobj, click.Group): if depth > 1: yield f"{'#'*depth} {tool_name} {cliobj.name}\n" for name in sorted(cliobj.commands): yield from iter_commands( tool_name, cmd_chain + [name], cliobj.commands[name], depth=depth + 1 ) else: ctx = click.get_current_context() cmd = f"{tool_name} {' '.join(cmd_chain)}" help_message = cliobj.get_help(ctx).replace(ctx.command_path, cmd) yield f"{'#'*(depth)} {tool_name} {' '.join(cmd_chain)}\n```\n{help_message}\n```" def generate_usage_md(script: str, version: str): script_name, module = script assert ":" in module module_name, cliobj = module.split(":") mod = importlib.import_module(module_name) cli = getattr(mod, cliobj) with open("USAGE.md", "wt") as fd: if "." in module_name: module_name = module_name.split(".")[0] print( f"# {module_name.capitalize()} v{version} - {script_name} - Command Usage Overview\n", file=fd, ) for command in iter_commands(script_name, [], cli): print(command, file=fd) @click.group() def cli(): pass @cli.command(help="Generate markdown usage description.") @click.argument( "poetry_project_file", type=click.File(), default=os.path.join(find_git_root(os.getcwd()), "pyproject.toml"), ) @click.pass_context def run(ctx, poetry_project_file): contents = toml.loads(poetry_project_file.read()) try: scripts = contents["tool"]["poetry"]["scripts"].items() version = contents["tool"]["poetry"]["version"] for script in scripts: generate_usage_md(script, version) except KeyError: click.echo("[ERROR] File does not contain 'tool.poetry.scripts' definitions.") ctx.exit(1) @cli.command(help="Install clickusagemd as pre-push hook.") def install(): install_hook(find_git_root(os.getcwd())) @cli.command(help="Uninstall clickusagemd pre-push hook.") def uninstall(): uninstall_hook(find_git_root(os.getcwd())) cli.add_command(run) cli.add_command(install) cli.add_command(uninstall) if __name__ == "__main__": cli()