Skip to content

Commit

Permalink
Updated CLI reference generator script
Browse files Browse the repository at this point in the history
  • Loading branch information
dwilding committed Dec 4, 2024
1 parent cbd5cf3 commit 29f9036
Showing 1 changed file with 30 additions and 93 deletions.
123 changes: 30 additions & 93 deletions docs/generate_command_docs.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,23 @@
"""This module generates reference docs for pebble CLI commands."""

import logging
import os
import re
import subprocess
import typing


logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)

AUTOMATED_START_MARKER = "<!-- START AUTOMATED OUTPUT -->"
AUTOMATED_STOP_MARKER = "<!-- END AUTOMATED OUTPUT -->"

TEMPLATE = """\
(reference_pebble_{command}_command)=
# {command} command
{description}
## Usage
<!-- START AUTOMATED OUTPUT -->
```{{terminal}}
:input: command
```
<!-- END AUTOMATED OUTPUT -->
"""


def get_all_commands() -> typing.List[typing.Tuple[str, str]]:
def get_all_commands() -> typing.List[str]:
process = subprocess.run(
["go", "run", "../cmd/pebble", "help", "--all"],
text=True,
capture_output=True,
check=True,
)
return sorted(
line.split(maxsplit=1)
line.split(maxsplit=1)[0]
for line in process.stdout.splitlines()
if line.startswith(" ")
)
Expand All @@ -56,102 +36,59 @@ def get_command_help_output(cmd: str) -> str:
).stdout


def render_code_block_cmd(text: str, cmd: str) -> str:
return re.sub(r"(:input: ).*$", rf"\1{cmd}", text, count=1, flags=re.MULTILINE)


def render_code_block_output(text: str, output: str) -> str:
start_pos = text.find(AUTOMATED_START_MARKER)
end_pos = text.find(AUTOMATED_STOP_MARKER) + len(AUTOMATED_STOP_MARKER)
return text[:start_pos] + output + text[end_pos:]


def update_toc(cmds: typing.List[typing.Tuple[str, str]]):
index_page = "reference/cli-commands/cli-commands.md"
with open(index_page, "r") as file:
text = file.read()

start_index = text.find("```{toctree}")
end_index = text.find("```", start_index + 1) + 3
cmd_list = "\n".join(f"{cmd[0]} <{cmd[0]}>" for cmd in cmds)

toc_tree = f"""\
```{{toctree}}
:titlesonly:
:maxdepth: 1
{cmd_list}
```"""

text = text[:start_index] + toc_tree + text[end_index:]
with open(index_page, "w") as file:
file.write(text)
class Markers:
def __init__(self, cmd: str):
self.start = f"<!-- START AUTOMATED OUTPUT FOR {cmd} -->"
self.end = f"<!-- END AUTOMATED OUTPUT FOR {cmd} -->"


def create_file_if_not_exist(filepath: str, cmd: str) -> bool:
file_existed = os.path.exists(filepath)
if not file_existed:
logger.info(
"The doc for command %s doesn't exist, creating from the template.", cmd
)
with open(filepath, "w") as file:
file.write(TEMPLATE)
return file_existed


def generate_help_command_and_output(cmd: str) -> typing.Tuple[str, str]:
def generate_example(cmd: str, markers: Markers) -> str:
args = ["help"] if cmd == "help" else [cmd, "--help"]
help_cmd_str = " ".join(["pebble"] + args)
go_run_cmd = " ".join(["go", "run", "../cmd/pebble"] + args)
help_cmd_output = get_command_help_output(go_run_cmd).strip()
help_output = get_command_help_output(go_run_cmd).strip()

output = f"""\
<!-- START AUTOMATED OUTPUT -->
return f"""\
{markers.start}
```{{terminal}}
:input: {help_cmd_str}
{help_cmd_output}
{help_output}
```
<!-- END AUTOMATED OUTPUT -->"""

return help_cmd_str, output
{markers.end}"""


def process_command(cmd: str, description: str):
logger.info("Processing doc for command %s.", cmd)
def insert_example(text: str, markers: Markers, example: str) -> str:
start_pos = text.find(markers.start)
end_pos = text.find(markers.end) + len(markers.end)
return text[:start_pos] + example + text[end_pos:]

file_path = f"reference/cli-commands/{cmd}.md"
file_existed = create_file_if_not_exist(file_path, cmd)

def process_commands(cmds: typing.List[str]):
file_path = "reference/cli-commands.md"
with open(file_path, "r") as file:
text = file.read()

if AUTOMATED_START_MARKER not in text:
logger.info(
'The marker for automated doc generation is not found in the "%s" doc, ignore.',
cmd,
)
return

help_cmd, help_cmd_output = generate_help_command_and_output(cmd)
description = f"The `{cmd}` command is used to {description.lower()}."

if not file_existed:
text = text.format(command=cmd, description=description)
for cmd in cmds:
logger.info("Processing doc for command %s.", cmd)
markers = Markers(cmd)
if markers.start not in text:
logger.info(
'The marker for automated doc generation is not found for command "%s", ignore.',
cmd,
)
continue

text = render_code_block_cmd(text, help_cmd)
text = render_code_block_output(text, help_cmd_output)
example = generate_example(cmd, markers)
text = insert_example(text, markers, example)

with open(file_path, "w") as file:
file.write(text)


def main():
cmds = get_all_commands()
for cmd, description in cmds:
process_command(cmd, description)
process_commands(cmds)

logger.info("Update toc tree.")
update_toc(cmds)
logger.info("Done!")


Expand Down

0 comments on commit 29f9036

Please sign in to comment.