Skip to content

Commit

Permalink
docs: automate cmd reference docs generation (#458)
Browse files Browse the repository at this point in the history
A script to help generate CLI CMD reference docs.
  • Loading branch information
IronCore864 authored Aug 1, 2024
1 parent 59cada8 commit d826556
Show file tree
Hide file tree
Showing 35 changed files with 840 additions and 27 deletions.
149 changes: 149 additions & 0 deletions docs/generate_command_docs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
"""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]]:
process = subprocess.run(
["go", "run", "../cmd/pebble", "help", "--all"],
text=True,
capture_output=True,
check=True,
)
return sorted(
line.split(maxsplit=1)
for line in process.stdout.splitlines()
if line.startswith(" ")
)


def get_command_help_output(cmd: typing.List[str]) -> str:
return subprocess.run(cmd, text=True, capture_output=True, check=True).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)


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]:
help_cmd = ["pebble", "help"] if cmd == "help" else ["pebble", cmd, "--help"]
help_cmd_str = " ".join(help_cmd)
help_cmd_output = get_command_help_output(help_cmd).strip()

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

return help_cmd, output


def process_command(cmd: str, description: str):
logger.info("Processing doc for command %s.", cmd)

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

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)

text = render_code_block_cmd(text, help_cmd)
text = render_code_block_output(text, help_cmd_output)

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)

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


if __name__ == "__main__":
main()
29 changes: 29 additions & 0 deletions docs/reference/cli-commands/add-identities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
(reference_pebble_add-identities_command)=
# add-identities command

The `add-identities` command is used to add new identities.

## Usage

<!-- START AUTOMATED OUTPUT -->
```{terminal}
:input: pebble add-identities --help
Usage:
pebble add-identities [add-identities-OPTIONS]
The add-identities command adds one or more new identities.
The named identities must not yet exist.
For example, to add a local admin named "bob", use YAML like this:
> identities:
> bob:
> access: admin
> local:
> user-id: 42
[add-identities command options]
--from= Path of YAML file to read identities from (required)
```
<!-- END AUTOMATED OUTPUT -->
22 changes: 22 additions & 0 deletions docs/reference/cli-commands/add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
(reference_pebble_add_command)=
# add command

The `add` command is used to dynamically add a layer to the plan's layers.

## Usage

<!-- START AUTOMATED OUTPUT -->
```{terminal}
:input: pebble add --help
Usage:
pebble add [add-OPTIONS] <label> <layer-path>
The add command reads the plan's layer YAML from the path specified and
appends a layer with the given label to the plan's layers. If --combine
is specified, combine the layer with an existing layer that has the given
label (or append if the label is not found).
[add command options]
--combine Combine the new layer with an existing layer that has the given label (default is to append)
```
<!-- END AUTOMATED OUTPUT -->
19 changes: 19 additions & 0 deletions docs/reference/cli-commands/changes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
(reference_pebble_changes_command)=
# changes command

The `changes` command is used to list system changes.

## Usage

<!-- START AUTOMATED OUTPUT -->
```{terminal}
:input: pebble changes --help
Usage:
pebble changes [changes-OPTIONS] [<service>]
The changes command displays a summary of system changes performed recently.
[changes command options]
--abs-time Display absolute times (in RFC 3339 format). Otherwise, display relative times up to 60 days, then YYYY-MM-DD.
```
<!-- END AUTOMATED OUTPUT -->
21 changes: 21 additions & 0 deletions docs/reference/cli-commands/checks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
(reference_pebble_checks_command)=
# checks command

The `checks` command is used to query the status of configured health checks.

## Usage

<!-- START AUTOMATED OUTPUT -->
```{terminal}
:input: pebble checks --help
Usage:
pebble checks [checks-OPTIONS] [<check>...]
The checks command lists status information about the configured health
checks, optionally filtered by level and check names provided as positional
arguments.
[checks command options]
--level=[alive|ready] Check level to filter for
```
<!-- END AUTOMATED OUTPUT -->
31 changes: 31 additions & 0 deletions docs/reference/cli-commands/cli-commands.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,45 @@
# CLI Commands

Pebble uses subcommands, like some other command-line tools such as go tool or git.

Subcommands are keywords that invoke a new set of options and features, and each Pebble subcommand has its own set of flags.

Here's the list of all Pebble subcommands:

```{toctree}
:titlesonly:
:maxdepth: 1
add <add>
add-identities <add-identities>
changes <changes>
checks <checks>
exec <exec>
health <health>
help <help>
identities <identities>
identity <identity>
logs <logs>
ls <ls>
mkdir <mkdir>
notice <notice>
notices <notices>
notify <notify>
okay <okay>
plan <plan>
pull <pull>
push <push>
remove-identities <remove-identities>
replan <replan>
restart <restart>
rm <rm>
run <run>
services <services>
signal <signal>
start <start>
stop <stop>
tasks <tasks>
update-identities <update-identities>
version <version>
warnings <warnings>
```
37 changes: 37 additions & 0 deletions docs/reference/cli-commands/exec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
(reference_pebble_exec_command)=
# exec command

The `exec` command is used to execute a remote command and wait for it to finish.

## Usage

<!-- START AUTOMATED OUTPUT -->
```{terminal}
:input: pebble exec --help
Usage:
pebble exec [exec-OPTIONS] <command>
The exec command runs a remote command and waits for it to finish. The local
stdin is sent as the input to the remote process, while the remote stdout and
stderr are output locally.
To avoid confusion, exec options may be separated from the command and its
arguments using "--", for example:
pebble exec --timeout 10s -- echo -n foo bar
[exec command options]
-w= Working directory to run command in
--env= Environment variable to set (in 'FOO=bar' format)
--uid= User ID to run command as
--user= Username to run command as (user's UID must match uid if both present)
--gid= Group ID to run command as
--group= Group name to run command as (group's GID must match gid if both present)
--timeout= Timeout after which to terminate command
--context= Inherit the context of the named service (overridden by -w, --env, --uid/user, --gid/group)
-t Allocate remote pseudo-terminal and connect stdout to it (default if stdout is a TTY)
-T Disable remote pseudo-terminal allocation
-i Interactive mode: connect stdin to the pseudo-terminal (default if stdin and stdout are TTYs)
-I Disable interactive mode and use a pipe for stdin
```
<!-- END AUTOMATED OUTPUT -->
22 changes: 22 additions & 0 deletions docs/reference/cli-commands/health.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
(reference_pebble_health_command)=
# health command

The `health` command is used to query health of checks.

## Usage

<!-- START AUTOMATED OUTPUT -->
```{terminal}
:input: pebble health --help
Usage:
pebble health [health-OPTIONS] [<check>...]
The health command queries the health of configured checks.
It returns an exit code 0 if all the requested checks are healthy, or
an exit code 1 if at least one of the requested checks are unhealthy.
[health command options]
--level=[alive|ready] Check level to filter for
```
<!-- END AUTOMATED OUTPUT -->
Loading

0 comments on commit d826556

Please sign in to comment.