cli-docs: add automatic markdown cli docs #1300
@ -2,9 +2,8 @@
|
|||||||
{
|
{
|
||||||
options.clan.disk-layouts.singleDiskExt4 = {
|
options.clan.disk-layouts.singleDiskExt4 = {
|
||||||
device = lib.mkOption {
|
device = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.str;
|
type = lib.types.str;
|
||||||
example = "/dev/disk/by-id/ata-Samsung_SSD_850_EVO_250GB_S21PNXAGB12345";
|
example = "/dev/disk/by-id/ata-Samsung_SSD_850_EVO_250GB_S21PNXAGB12345";
|
||||||
default = null;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
config = {
|
config = {
|
||||||
@ -16,7 +15,6 @@
|
|||||||
type = "disk";
|
type = "disk";
|
||||||
device = config.clan.disk-layouts.singleDiskExt4.device;
|
device = config.clan.disk-layouts.singleDiskExt4.device;
|
||||||
content = {
|
content = {
|
||||||
device = lib.mkDefault config.clan.diskLayouts.singleDiskExt4.device;
|
|
||||||
type = "gpt";
|
type = "gpt";
|
||||||
partitions = {
|
partitions = {
|
||||||
boot = {
|
boot = {
|
||||||
|
@ -13,6 +13,7 @@ markdown_extensions:
|
|||||||
- admonition
|
- admonition
|
||||||
- attr_list
|
- attr_list
|
||||||
- footnotes
|
- footnotes
|
||||||
|
- md_in_html
|
||||||
- meta
|
- meta
|
||||||
- plantuml_markdown
|
- plantuml_markdown
|
||||||
- pymdownx.emoji:
|
- pymdownx.emoji:
|
||||||
@ -46,13 +47,19 @@ nav:
|
|||||||
- Flake-parts: getting-started/flake-parts.md
|
- Flake-parts: getting-started/flake-parts.md
|
||||||
- Templates: templates/index.md
|
- Templates: templates/index.md
|
||||||
- Reference:
|
- Reference:
|
||||||
- clan-core:
|
- CLI:
|
||||||
- reference/clan-core/index.md
|
- reference/cli/index.md
|
||||||
- reference/clan-core/backups.md
|
- reference/cli/backups.md
|
||||||
- reference/clan-core/facts.md
|
- reference/cli/config.md
|
||||||
- reference/clan-core/sops.md
|
- reference/cli/facts.md
|
||||||
- reference/clan-core/state.md
|
- reference/cli/flakes.md
|
||||||
- clanModules:
|
- reference/cli/flash.md
|
||||||
|
- reference/cli/history.md
|
||||||
|
- reference/cli/machines.md
|
||||||
|
- reference/cli/secrets.md
|
||||||
|
- reference/cli/ssh.md
|
||||||
|
- reference/cli/vms.md
|
||||||
|
- Clan Modules:
|
||||||
- reference/clanModules/borgbackup.md
|
- reference/clanModules/borgbackup.md
|
||||||
- reference/clanModules/deltachat.md
|
- reference/clanModules/deltachat.md
|
||||||
- reference/clanModules/disk-layouts.md
|
- reference/clanModules/disk-layouts.md
|
||||||
@ -68,6 +75,12 @@ nav:
|
|||||||
- reference/clanModules/thelounge.md
|
- reference/clanModules/thelounge.md
|
||||||
- reference/clanModules/xfce.md
|
- reference/clanModules/xfce.md
|
||||||
- reference/clanModules/zt-tcp-relay.md
|
- reference/clanModules/zt-tcp-relay.md
|
||||||
|
- Clan Core:
|
||||||
|
- reference/clan-core/index.md
|
||||||
|
- reference/clan-core/backups.md
|
||||||
|
- reference/clan-core/facts.md
|
||||||
|
- reference/clan-core/sops.md
|
||||||
|
- reference/clan-core/state.md
|
||||||
- Contributing: contributing/contributing.md
|
- Contributing: contributing/contributing.md
|
||||||
|
|
||||||
docs_dir: site
|
docs_dir: site
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
{ pkgs, module-docs, ... }:
|
{
|
||||||
|
pkgs,
|
||||||
|
module-docs,
|
||||||
|
clan-cli-docs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
let
|
let
|
||||||
uml-c4 = pkgs.python3Packages.plantuml-markdown.override { plantuml = pkgs.plantuml-c4; };
|
uml-c4 = pkgs.python3Packages.plantuml-markdown.override { plantuml = pkgs.plantuml-c4; };
|
||||||
in
|
in
|
||||||
@ -17,9 +22,9 @@ pkgs.stdenv.mkDerivation {
|
|||||||
mkdocs-material
|
mkdocs-material
|
||||||
]);
|
]);
|
||||||
configurePhase = ''
|
configurePhase = ''
|
||||||
mkdir -p ./site/reference
|
mkdir -p ./site/reference/cli
|
||||||
cp -af ${module-docs}/* ./site/reference/
|
cp -af ${module-docs}/* ./site/reference/
|
||||||
|
cp -af ${clan-cli-docs}/* ./site/reference/cli/
|
||||||
'';
|
'';
|
||||||
|
|
||||||
buildPhase = ''
|
buildPhase = ''
|
||||||
|
@ -54,19 +54,17 @@
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
devShells.docs = pkgs.callPackage ./shell.nix {
|
devShells.docs = pkgs.callPackage ./shell.nix {
|
||||||
inherit (self'.packages) docs;
|
inherit (self'.packages) docs clan-cli-docs;
|
||||||
inherit module-docs;
|
inherit module-docs;
|
||||||
};
|
};
|
||||||
packages = {
|
packages = {
|
||||||
docs = pkgs.python3.pkgs.callPackage ./default.nix {
|
docs = pkgs.python3.pkgs.callPackage ./default.nix {
|
||||||
|
inherit (self'.packages) clan-cli-docs;
|
||||||
inherit (inputs) nixpkgs;
|
inherit (inputs) nixpkgs;
|
||||||
inherit module-docs;
|
inherit module-docs;
|
||||||
};
|
};
|
||||||
deploy-docs = pkgs.callPackage ./deploy-docs.nix { inherit (config.packages) docs; };
|
deploy-docs = pkgs.callPackage ./deploy-docs.nix { inherit (config.packages) docs; };
|
||||||
inherit module-docs;
|
inherit module-docs;
|
||||||
};
|
};
|
||||||
legacyPackages = {
|
|
||||||
foo = jsonDocs;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,15 @@
|
|||||||
docs,
|
docs,
|
||||||
pkgs,
|
pkgs,
|
||||||
module-docs,
|
module-docs,
|
||||||
|
clan-cli-docs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
pkgs.mkShell {
|
pkgs.mkShell {
|
||||||
inputsFrom = [ docs ];
|
inputsFrom = [ docs ];
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
mkdir -p ./site/reference
|
mkdir -p ./site/reference/cli
|
||||||
cp -af ${module-docs}/* ./site/reference/
|
cp -af ${module-docs}/* ./site/reference/
|
||||||
|
cp -af ${clan-cli-docs}/* ./site/reference/cli/
|
||||||
chmod +w ./site/reference/*
|
chmod +w ./site/reference/*
|
||||||
|
|
||||||
echo "Generated API documentation in './site/reference/' "
|
echo "Generated API documentation in './site/reference/' "
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import argparse
|
import argparse
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from clan_cli import create_parser
|
from clan_cli import create_parser
|
||||||
|
|
||||||
@ -12,6 +13,18 @@ class Option:
|
|||||||
metavar: str | None = None
|
metavar: str | None = None
|
||||||
epilog: str | None = None
|
epilog: str | None = None
|
||||||
|
|
||||||
|
def to_md_li(self, delim: str = "-") -> str:
|
||||||
|
# - **--example, `--e`**: <PATH> {description} (Default: `default` {epilog})
|
||||||
|
md_li = f"{delim} **{self.name}**: "
|
||||||
|
md_li += f"`<{self.metavar}>` " if self.metavar else ""
|
||||||
|
md_li += f"(Default: `{self.default}`) " if self.default else ""
|
||||||
|
md_li += indent_next(
|
||||||
|
f"\n{self.description.strip()}" if self.description else ""
|
||||||
|
)
|
||||||
|
md_li += indent_next(f"\n{self.epilog.strip()}" if self.epilog else "")
|
||||||
|
|
||||||
|
return md_li
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Subcommand:
|
class Subcommand:
|
||||||
@ -19,6 +32,27 @@ class Subcommand:
|
|||||||
description: str | None = None
|
description: str | None = None
|
||||||
epilog: str | None = None
|
epilog: str | None = None
|
||||||
|
|
||||||
|
def to_md_li(self, parent: "Category") -> str:
|
||||||
|
md_li = f"""- **[{self.name}](#{"-".join(parent.title.split(" "))}-{self.name})**: """
|
||||||
|
md_li += indent_next(f"{self.description.strip()} " if self.description else "")
|
||||||
|
md_li += indent_next(f"\n{self.epilog.strip()}" if self.epilog else "")
|
||||||
|
|
||||||
|
return md_li
|
||||||
|
|
||||||
|
|
||||||
|
icon_table = {
|
||||||
|
"backups": ":material-backup-restore: ",
|
||||||
|
"config": ":material-shape-outline: ",
|
||||||
|
"facts": ":simple-databricks: ",
|
||||||
|
"flakes": ":material-snowflake: ",
|
||||||
|
"flash": ":material-flash: ",
|
||||||
|
"history": ":octicons-history-24: ",
|
||||||
|
"machines": ":octicons-devices-24: ",
|
||||||
|
"secrets": ":octicons-passkey-fill-24: ",
|
||||||
|
"ssh": ":material-ssh: ",
|
||||||
|
"vms": ":simple-virtualbox: ",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Category:
|
class Category:
|
||||||
@ -38,6 +72,21 @@ class Category:
|
|||||||
# What level of depth the category is at (i.e. 'backups list' is 2, 'backups' is 1, 'clan' is 0)
|
# What level of depth the category is at (i.e. 'backups list' is 2, 'backups' is 1, 'clan' is 0)
|
||||||
level: int = 0
|
level: int = 0
|
||||||
|
|
||||||
|
def to_md_li(self, level: int = 1) -> str:
|
||||||
|
md_li = ""
|
||||||
|
if level == self.level:
|
||||||
|
icon = icon_table.get(self.title, "")
|
||||||
|
md_li += f"""- **[{icon}{self.title}](./{"-".join(self.title.split(" "))}.md)**\n\n"""
|
||||||
|
md_li += f"""{indent_all("---", 4)}\n\n"""
|
||||||
|
md_li += indent_all(
|
||||||
|
f"{self.description.strip()}\n" if self.description else "", 4
|
||||||
|
)
|
||||||
|
md_li += "\n"
|
||||||
|
md_li += indent_all(f"{self.epilog.strip()}\n" if self.epilog else "", 4)
|
||||||
|
md_li += "\n"
|
||||||
|
|
||||||
|
return md_li
|
||||||
|
|
||||||
|
|
||||||
def indent_next(text: str, indent_size: int = 4) -> str:
|
def indent_next(text: str, indent_size: int = 4) -> str:
|
||||||
"""
|
"""
|
||||||
@ -176,38 +225,98 @@ def collect_commands() -> list[Category]:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def weight_cmd_groups(c: Category) -> tuple[str, int, str]:
|
def weight_cmd_groups(c: Category) -> tuple[str, str, int]:
|
||||||
sub = [o for o in result if o.title.startswith(c.title) and o.title != c.title]
|
sub = [o for o in result if o.title.startswith(c.title) and o.title != c.title]
|
||||||
weight = len(c.title.split(" "))
|
weight = 10 - len(c.title.split(" "))
|
||||||
if sub:
|
if sub:
|
||||||
weight = len(sub[0].title.split(" "))
|
weight = 10 - len(sub[0].title.split(" "))
|
||||||
|
|
||||||
# 1. Sort by toplevel name alphabetically
|
# 1. Sort by toplevel name alphabetically
|
||||||
# 2. sort by custom weight to keep groups together
|
# 2. sort by custom weight to keep groups together
|
||||||
# 3. sort by title alphabetically
|
# 3. sort by title alphabetically
|
||||||
return (c.title.split(" ")[0], weight, c.title)
|
return (c.title.split(" ")[0], c.title, weight)
|
||||||
|
|
||||||
result = sorted(result, key=weight_cmd_groups)
|
result = sorted(result, key=weight_cmd_groups)
|
||||||
|
|
||||||
# for c in result:
|
|
||||||
# print(c.title)
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
cmds = collect_commands()
|
cmds = collect_commands()
|
||||||
|
|
||||||
# TODO: proper markdown
|
folder = Path("out")
|
||||||
markdown = ""
|
folder.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Index file
|
||||||
|
markdown = "# CLI Overview\n\n"
|
||||||
|
categories_fmt = ""
|
||||||
|
for cat in cmds:
|
||||||
|
categories_fmt += f"{cat.to_md_li()}\n\n" if cat.to_md_li() else ""
|
||||||
|
|
||||||
|
if categories_fmt:
|
||||||
|
markdown += """## Overview\n\n"""
|
||||||
|
markdown += '<div class="grid cards" markdown>\n\n'
|
||||||
|
markdown += categories_fmt
|
||||||
|
markdown += "</div>"
|
||||||
|
markdown += "\n"
|
||||||
|
|
||||||
|
with open(folder / "index.md", "w") as f:
|
||||||
|
f.write(markdown)
|
||||||
|
|
||||||
|
# Each top level category is a separate file
|
||||||
|
files: dict[Path, str] = {}
|
||||||
|
|
||||||
|
for t in [cmd.title for cmd in cmds]:
|
||||||
|
print(t)
|
||||||
|
|
||||||
for cmd in cmds:
|
for cmd in cmds:
|
||||||
markdown += f"## {cmd.title}\n\n"
|
# Collect all commands starting with the same name into one file
|
||||||
markdown += f"{cmd.description}\n" if cmd.description else ""
|
filename = cmd.title.split(" ")[0]
|
||||||
markdown += f"{cmd.options}\n" if cmd.description else ""
|
markdown = files.get(folder / f"{filename}.md", "")
|
||||||
markdown += f"{cmd.subcommands}\n" if cmd.description else ""
|
|
||||||
markdown += f"{cmd.positionals}\n" if cmd.description else ""
|
|
||||||
markdown += f"{cmd.epilog}\n" if cmd.description else ""
|
|
||||||
|
|
||||||
break
|
markdown += f"{'#'*(cmd.level)} {cmd.title.capitalize()}\n\n"
|
||||||
|
markdown += f"{cmd.description}\n\n" if cmd.description else ""
|
||||||
|
|
||||||
print(markdown)
|
# usage: clan vms run [-h] machine
|
||||||
|
markdown += f"""Usage: `clan {cmd.title}`\n\n"""
|
||||||
|
|
||||||
|
# options:
|
||||||
|
# -h, --help show this help message and exit
|
||||||
|
|
||||||
|
# Positional arguments
|
||||||
|
positionals_fmt = ""
|
||||||
|
for option in cmd.positionals:
|
||||||
|
positionals_fmt += f"""{option.to_md_li("1.")}\n"""
|
||||||
|
|
||||||
|
if len(cmd.positionals):
|
||||||
|
markdown += """!!! info "Positional arguments"\n"""
|
||||||
|
markdown += indent_all(positionals_fmt)
|
||||||
|
markdown += "\n"
|
||||||
|
|
||||||
|
options_fmt = ""
|
||||||
|
for option in cmd.options:
|
||||||
|
options_fmt += f"{option.to_md_li()}\n"
|
||||||
|
|
||||||
|
# options:
|
||||||
|
if len(cmd.options):
|
||||||
|
markdown += """??? info "Options"\n"""
|
||||||
|
markdown += indent_all(options_fmt)
|
||||||
|
markdown += "\n"
|
||||||
|
|
||||||
|
def asort(s: Subcommand) -> str:
|
||||||
|
return s.name
|
||||||
|
|
||||||
|
commands_fmt = ""
|
||||||
|
for sub_cmd in sorted(cmd.subcommands, key=asort):
|
||||||
|
commands_fmt += f"{sub_cmd.to_md_li(cmd)}\n"
|
||||||
|
|
||||||
|
if commands_fmt:
|
||||||
|
markdown += """!!! info "Commands"\n"""
|
||||||
|
markdown += indent_all(commands_fmt)
|
||||||
|
markdown += "\n"
|
||||||
|
|
||||||
|
files[folder / f"{filename}.md"] = markdown
|
||||||
|
|
||||||
|
for fname, content in files.items():
|
||||||
|
with open(fname, "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
@ -44,6 +44,19 @@
|
|||||||
inherit (inputs) nixpkgs;
|
inherit (inputs) nixpkgs;
|
||||||
clan-core-path = clanCoreWithVendoredDeps;
|
clan-core-path = clanCoreWithVendoredDeps;
|
||||||
};
|
};
|
||||||
|
clan-cli-docs = pkgs.stdenv.mkDerivation {
|
||||||
|
name = "clan-cli-docs";
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
buildInputs = [ pkgs.python3 ];
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
python docs.py
|
||||||
|
mkdir -p $out
|
||||||
|
cp -r out/* $out
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
default = self'.packages.clan-cli;
|
default = self'.packages.clan-cli;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user