mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-09 20:26:33 +00:00
make.md names 'make help' as the authoritative target list and the site never carried one, so a reader had to run the repository to see what it offers. The same held for the i8 aliases and for the infinito command tree.
Three generators join the ones the build already runs for the YAML index and the role glossary. make_targets reads the comment block above each target, the same block make help prints, and turns 'Param x:' into a field. aliases reads the trailing comment, falling back to the target an alias wraps, because a plain make wrapper carries no comment by repository rule; an unparseable line raises instead of vanishing from the page. cli_commands asks cli.core.discovery, the rule the dispatcher itself applies, so the page cannot promise a command the CLI rejects.
cli_commands parses each __main__.py with ast rather than importing it: rendering a page would otherwise run 65 modules' import side effects, and a broken one degrades to 'Undocumented.' instead of losing the page.
literal() escapes the inline markup starters in prose taken from those files. 3b81b4de2e fixed this class once already, where a role description reading (*.parent) opened an emphasis that never closed; a Makefile comment naming two globs does the same. Measured: the unescaped form reports three docutils warnings, the escaped form none.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
109 lines
3.3 KiB
Python
109 lines
3.3 KiB
Python
"""Render the repository's terminal aliases as a reference page.
|
|
|
|
The ``aliases`` file is the source. An alias that only wraps a make target
|
|
carries no comment by repository rule, so its description is taken from the
|
|
target it calls; every other alias documents itself in the trailing comment.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import re
|
|
from pathlib import Path
|
|
|
|
from . import literal
|
|
|
|
ALIAS = re.compile(
|
|
r"^alias\s+([^=]+)=(['\"])(?P<body>.*)\2(?:\s*#\s*(?P<comment>.*\S))?\s*$"
|
|
)
|
|
MAKE_CALL = re.compile(r"(?:^|[\s;&(])(?:m|make)\s+([a-z][a-z0-9-]*)")
|
|
|
|
HEADER = """\
|
|
Terminal Aliases
|
|
================
|
|
|
|
The ``i8*`` shell aliases installed by ``make install-alias``, read from the
|
|
repository's ``aliases`` file. An alias that only wraps a make target is listed
|
|
with that target; the rest carry their own description. Generated by the
|
|
documentation build; edit ``aliases`` rather than this page.
|
|
|
|
"""
|
|
|
|
|
|
def parse(aliases_file: Path) -> list[tuple[str, str, str | None]]:
|
|
"""Return every alias as ``(name, body, comment)``.
|
|
|
|
Args:
|
|
aliases_file: the ``aliases`` file to read.
|
|
|
|
Raises:
|
|
ValueError: a line starts with ``alias`` but does not parse, which
|
|
would otherwise drop it from the page without a trace.
|
|
"""
|
|
parsed: list[tuple[str, str, str | None]] = []
|
|
for line in aliases_file.read_text(encoding="utf-8").splitlines():
|
|
if not line.startswith("alias "):
|
|
continue
|
|
match = ALIAS.match(line)
|
|
if match is None:
|
|
raise ValueError(f"unparseable alias line: {line!r}")
|
|
parsed.append(
|
|
(match.group(1).strip(), match.group("body"), match.group("comment"))
|
|
)
|
|
return parsed
|
|
|
|
|
|
def _entry(name: str, body: str, comment: str | None) -> str:
|
|
"""Return one definition-list entry for an alias.
|
|
|
|
Args:
|
|
name: the alias name.
|
|
body: what the alias expands to.
|
|
comment: the trailing comment, absent for a plain make wrapper.
|
|
"""
|
|
targets = sorted(set(MAKE_CALL.findall(body)))
|
|
if comment:
|
|
description = literal(comment)
|
|
elif targets:
|
|
description = "Runs " + ", ".join(f"``make {t}``" for t in targets) + "."
|
|
else:
|
|
description = "Undocumented."
|
|
return f"``{name}``\n {description}"
|
|
|
|
|
|
def generate(aliases_file: Path, output_file: Path) -> int:
|
|
"""Write the alias reference page.
|
|
|
|
Args:
|
|
aliases_file: the ``aliases`` file to read.
|
|
output_file: the ``.rst`` page to write.
|
|
|
|
Returns:
|
|
How many aliases the page documents.
|
|
"""
|
|
entries = parse(aliases_file)
|
|
output_file.parent.mkdir(parents=True, exist_ok=True)
|
|
output_file.write_text(
|
|
HEADER + "\n\n".join(_entry(*entry) for entry in entries) + "\n",
|
|
encoding="utf-8",
|
|
)
|
|
return len(entries)
|
|
|
|
|
|
def main(argv=None):
|
|
parser = argparse.ArgumentParser(
|
|
description="Generate the alias reference from the aliases file."
|
|
)
|
|
parser.add_argument("--aliases-file", required=True, help="Path to aliases.")
|
|
parser.add_argument("--output-file", required=True, help="Path to the .rst page.")
|
|
args = parser.parse_args(argv)
|
|
|
|
output = Path(args.output_file)
|
|
count = generate(Path(args.aliases_file), output)
|
|
print(f"{count} alias(es) written to {output}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|