From 5a6eeda125528e574e9a1cd0d9ee86ecf59e2e54 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Tue, 30 Apr 2024 20:05:05 +0200 Subject: [PATCH 1/2] disko module: add default for singleDiskExt4 --- clanModules/disk-layouts/default.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clanModules/disk-layouts/default.nix b/clanModules/disk-layouts/default.nix index 78f2295a..c05dcc1c 100644 --- a/clanModules/disk-layouts/default.nix +++ b/clanModules/disk-layouts/default.nix @@ -2,8 +2,9 @@ { options.clan.disk-layouts.singleDiskExt4 = { device = lib.mkOption { - type = lib.types.str; + type = lib.types.nullOr lib.types.str; example = "/dev/disk/by-id/ata-Samsung_SSD_850_EVO_250GB_S21PNXAGB12345"; + default = null; }; }; config = { @@ -15,6 +16,7 @@ type = "disk"; device = config.clan.disk-layouts.singleDiskExt4.device; content = { + device = lib.mkDefault config.clan.diskLayouts.singleDiskExt4.device; type = "gpt"; partitions = { boot = { -- 2.45.2 From 15dd4ea25faee0783edaffaeaeae5d04958572c5 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Tue, 7 May 2024 13:23:03 +0200 Subject: [PATCH 2/2] cli-docs: add automatic markdown cli docs --- clanModules/disk-layouts/default.nix | 4 +- docs/mkdocs.yml | 27 +++-- docs/nix/default.nix | 11 ++- docs/nix/flake-module.nix | 6 +- docs/nix/shell.nix | 4 +- pkgs/clan-cli/docs.py | 143 +++++++++++++++++++++++---- pkgs/clan-cli/flake-module.nix | 13 +++ 7 files changed, 173 insertions(+), 35 deletions(-) diff --git a/clanModules/disk-layouts/default.nix b/clanModules/disk-layouts/default.nix index c05dcc1c..78f2295a 100644 --- a/clanModules/disk-layouts/default.nix +++ b/clanModules/disk-layouts/default.nix @@ -2,9 +2,8 @@ { options.clan.disk-layouts.singleDiskExt4 = { 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"; - default = null; }; }; config = { @@ -16,7 +15,6 @@ type = "disk"; device = config.clan.disk-layouts.singleDiskExt4.device; content = { - device = lib.mkDefault config.clan.diskLayouts.singleDiskExt4.device; type = "gpt"; partitions = { boot = { diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 4016dbcc..0f578572 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -13,6 +13,7 @@ markdown_extensions: - admonition - attr_list - footnotes + - md_in_html - meta - plantuml_markdown - pymdownx.emoji: @@ -46,13 +47,19 @@ nav: - Flake-parts: getting-started/flake-parts.md - Templates: templates/index.md - Reference: - - 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 - - clanModules: + - CLI: + - reference/cli/index.md + - reference/cli/backups.md + - reference/cli/config.md + - reference/cli/facts.md + - reference/cli/flakes.md + - 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/deltachat.md - reference/clanModules/disk-layouts.md @@ -68,6 +75,12 @@ nav: - reference/clanModules/thelounge.md - reference/clanModules/xfce.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 docs_dir: site diff --git a/docs/nix/default.nix b/docs/nix/default.nix index 6d0afd3c..fa675bcb 100644 --- a/docs/nix/default.nix +++ b/docs/nix/default.nix @@ -1,4 +1,9 @@ -{ pkgs, module-docs, ... }: +{ + pkgs, + module-docs, + clan-cli-docs, + ... +}: let uml-c4 = pkgs.python3Packages.plantuml-markdown.override { plantuml = pkgs.plantuml-c4; }; in @@ -17,9 +22,9 @@ pkgs.stdenv.mkDerivation { mkdocs-material ]); configurePhase = '' - mkdir -p ./site/reference + mkdir -p ./site/reference/cli cp -af ${module-docs}/* ./site/reference/ - + cp -af ${clan-cli-docs}/* ./site/reference/cli/ ''; buildPhase = '' diff --git a/docs/nix/flake-module.nix b/docs/nix/flake-module.nix index a0560e4f..be468b4e 100644 --- a/docs/nix/flake-module.nix +++ b/docs/nix/flake-module.nix @@ -54,19 +54,17 @@ in { devShells.docs = pkgs.callPackage ./shell.nix { - inherit (self'.packages) docs; + inherit (self'.packages) docs clan-cli-docs; inherit module-docs; }; packages = { docs = pkgs.python3.pkgs.callPackage ./default.nix { + inherit (self'.packages) clan-cli-docs; inherit (inputs) nixpkgs; inherit module-docs; }; deploy-docs = pkgs.callPackage ./deploy-docs.nix { inherit (config.packages) docs; }; inherit module-docs; }; - legacyPackages = { - foo = jsonDocs; - }; }; } diff --git a/docs/nix/shell.nix b/docs/nix/shell.nix index 49658f30..ee593a94 100644 --- a/docs/nix/shell.nix +++ b/docs/nix/shell.nix @@ -2,13 +2,15 @@ docs, pkgs, module-docs, + clan-cli-docs, ... }: pkgs.mkShell { inputsFrom = [ docs ]; shellHook = '' - mkdir -p ./site/reference + mkdir -p ./site/reference/cli cp -af ${module-docs}/* ./site/reference/ + cp -af ${clan-cli-docs}/* ./site/reference/cli/ chmod +w ./site/reference/* echo "Generated API documentation in './site/reference/' " diff --git a/pkgs/clan-cli/docs.py b/pkgs/clan-cli/docs.py index c6b4ac93..7ab8cd7b 100644 --- a/pkgs/clan-cli/docs.py +++ b/pkgs/clan-cli/docs.py @@ -1,5 +1,6 @@ import argparse from dataclasses import dataclass +from pathlib import Path from clan_cli import create_parser @@ -12,6 +13,18 @@ class Option: metavar: str | None = None epilog: str | None = None + def to_md_li(self, delim: str = "-") -> str: + # - **--example, `--e`**: {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 class Subcommand: @@ -19,6 +32,27 @@ class Subcommand: description: 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 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) 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: """ @@ -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] - weight = len(c.title.split(" ")) + weight = 10 - len(c.title.split(" ")) if sub: - weight = len(sub[0].title.split(" ")) + weight = 10 - len(sub[0].title.split(" ")) # 1. Sort by toplevel name alphabetically # 2. sort by custom weight to keep groups together # 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) - # for c in result: - # print(c.title) - return result if __name__ == "__main__": cmds = collect_commands() - # TODO: proper markdown - markdown = "" + folder = Path("out") + 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 += '
\n\n' + markdown += categories_fmt + markdown += "
" + 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: - markdown += f"## {cmd.title}\n\n" - markdown += f"{cmd.description}\n" if cmd.description else "" - markdown += f"{cmd.options}\n" if cmd.description else "" - 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 "" + # Collect all commands starting with the same name into one file + filename = cmd.title.split(" ")[0] + markdown = files.get(folder / f"{filename}.md", "") - 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) diff --git a/pkgs/clan-cli/flake-module.nix b/pkgs/clan-cli/flake-module.nix index ba8cb7f0..e8d43064 100644 --- a/pkgs/clan-cli/flake-module.nix +++ b/pkgs/clan-cli/flake-module.nix @@ -44,6 +44,19 @@ inherit (inputs) nixpkgs; 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; }; -- 2.45.2