From da3c1ceea3f6d4309a449a8081589c3245981f48 Mon Sep 17 00:00:00 2001 From: pinpox Date: Thu, 15 May 2025 22:20:01 +0200 Subject: [PATCH] Add clanServices to docs Renders the documentation for clanServices. Options for the modules are extracted and rendered the same way as for the existing clanModules. Additionally tweaks the typography for the documentation of options slightly --- clanServices/hello-world/default.nix | 15 ++++ docs/mkdocs.yml | 4 ++ docs/nix/flake-module.nix | 11 ++- docs/nix/get-module-docs.nix | 27 +++++++ docs/nix/render_options/__init__.py | 104 ++++++++++++++++++++++----- 5 files changed, 143 insertions(+), 18 deletions(-) diff --git a/clanServices/hello-world/default.nix b/clanServices/hello-world/default.nix index 86bfbdb1e..fca39d13d 100644 --- a/clanServices/hello-world/default.nix +++ b/clanServices/hello-world/default.nix @@ -3,6 +3,19 @@ { _class = "clan.service"; manifest.name = "clan-core/hello-word"; + manifest.description = "This is a test"; + + roles.test = { + interface = + { lib, ... }: + { + options.foo = lib.mkOption { + type = lib.types.str; + # default = ""; + description = "Some option"; + }; + }; + }; roles.peer = { interface = @@ -10,6 +23,8 @@ { options.foo = lib.mkOption { type = lib.types.str; + # default = ""; + description = "Some option"; }; }; }; diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 7512e3578..19c931449 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -85,6 +85,10 @@ nav: - 02-clan-api: decisions/02-clan-api.md - 03-adr-numbering-process: decisions/03-adr-numbering-process.md - Template: decisions/_template.md + - Clan Services: + - reference/clanServices/admin.md + - reference/clanServices/hello-world.md + - reference/clanServices/wifi.md - Clan Modules: - Overview: - reference/clanModules/index.md diff --git a/docs/nix/flake-module.nix b/docs/nix/flake-module.nix index 767d7c4e1..bfba3e72b 100644 --- a/docs/nix/flake-module.nix +++ b/docs/nix/flake-module.nix @@ -36,6 +36,9 @@ # Options available when imported via ` inventory.${moduleName}....${rolesName} ` clanModulesViaRoles = pkgs.writeText "info.json" (builtins.toJSON jsonDocs.clanModulesViaRoles); + # clan service options + clanModulesViaService = pkgs.writeText "info.json" (builtins.toJSON jsonDocs.clanModulesViaService); + # Simply evaluated options (JSON) renderOptions = pkgs.runCommand "render-options" @@ -85,6 +88,7 @@ export CLAN_CORE_DOCS=${jsonDocs.clanCore}/share/doc/nixos/options.json # A file that contains the links to all clanModule docs export CLAN_MODULES_VIA_ROLES=${clanModulesViaRoles} + export CLAN_MODULES_VIA_SERVICE=${clanModulesViaService} export CLAN_MODULES_VIA_NIX=${clanModulesViaNix} # Frontmatter format for clanModules export CLAN_MODULES_FRONTMATTER_DOCS=${clanModulesFrontmatter}/share/doc/nixos/options.json @@ -100,7 +104,12 @@ in { legacyPackages = { - inherit jsonDocs clanModulesViaNix clanModulesViaRoles; + inherit + jsonDocs + clanModulesViaNix + clanModulesViaRoles + clanModulesViaService + ; }; devShells.docs = pkgs.callPackage ./shell.nix { inherit (self'.packages) docs clan-cli-docs inventory-api-docs; diff --git a/docs/nix/get-module-docs.nix b/docs/nix/get-module-docs.nix index 076a7f650..13cd80b43 100644 --- a/docs/nix/get-module-docs.nix +++ b/docs/nix/get-module-docs.nix @@ -36,6 +36,33 @@ ) rolesOptions ) modulesRolesOptions; + # Test with: + # nix build .\#legacyPackages.x86_64-linux.clanModulesViaService + clanModulesViaService = lib.mapAttrs ( + _moduleName: moduleValue: + let + evaluatedService = clan-core.clanLib.inventory.evalClanService { + modules = [ moduleValue ]; + prefix = [ ]; + }; + in + { + roles = lib.mapAttrs ( + _roleName: role: + + (nixosOptionsDoc { + transformOptions = + opt: if lib.strings.hasPrefix "_" opt.name then opt // { visible = false; } else opt; + options = (lib.evalModules { modules = [ role.interface ]; }).options; + warningsAreErrors = true; + }).optionsJSON + ) evaluatedService.config.roles; + + manifest = evaluatedService.config.manifest; + + } + ) clan-core.clan.modules; + clanCore = (nixosOptionsDoc { options = diff --git a/docs/nix/render_options/__init__.py b/docs/nix/render_options/__init__.py index fa63c2592..57f56c280 100644 --- a/docs/nix/render_options/__init__.py +++ b/docs/nix/render_options/__init__.py @@ -30,7 +30,12 @@ from pathlib import Path from typing import Any from clan_cli.errors import ClanError -from clan_lib.api.modules import Frontmatter, extract_frontmatter, get_roles +from clan_lib.api.modules import ( + CategoryInfo, + Frontmatter, + extract_frontmatter, + get_roles, +) # Get environment variables CLAN_CORE_PATH = Path(os.environ["CLAN_CORE_PATH"]) @@ -44,6 +49,7 @@ CLAN_MODULES_VIA_NIX = os.environ.get("CLAN_MODULES_VIA_NIX") # Some modules can be imported via inventory CLAN_MODULES_VIA_ROLES = os.environ.get("CLAN_MODULES_VIA_ROLES") +CLAN_MODULES_VIA_SERVICE = os.environ.get("CLAN_MODULES_VIA_SERVICE") OUT = os.environ.get("out") @@ -58,7 +64,8 @@ def replace_store_path(text: str) -> tuple[str, str]: res = "https://git.clan.lol/clan/clan-core/src/branch/main/" + str( Path(*Path(text).parts[4:]) ) - name = Path(res).name + # name = Path(res).name + name = str(Path(*Path(text).parts[4:])) return (res, name) @@ -149,8 +156,12 @@ def render_option( decls = option.get("declarations", []) if decls: source_path, name = replace_store_path(decls[0]) + + name = name.split(",")[0] + source_path = source_path.split(",")[0] + res += f""" -:simple-git: [{name}]({source_path}) +:simple-git: Declared in: [{name}]({source_path}) """ res += "\n\n" @@ -221,7 +232,8 @@ def produce_clan_modules_frontmatter_docs() -> None: # header output = """# Frontmatter -Every clan module has a `frontmatter` section within its readme. It provides machine readable metadata about the module. +Every clan module has a `frontmatter` section within its readme. It provides +machine readable metadata about the module. !!! example @@ -246,7 +258,8 @@ Every clan module has a `frontmatter` section within its readme. It provides mac output += """## Overview -This provides an overview of the available attributes of the `frontmatter` within the `README.md` of a clan module. +This provides an overview of the available attributes of the `frontmatter` +within the `README.md` of a clan module. """ # for option_name, info in options.items(): @@ -331,7 +344,7 @@ def produce_clan_core_docs() -> None: def render_roles(roles: list[str] | None, module_name: str) -> str: if roles: - roles_list = "\n".join([f" - `{r}`" for r in roles]) + roles_list = "\n".join([f"- `{r}`" for r in roles]) return ( f""" ### Roles @@ -341,7 +354,7 @@ This module can be used via predefined roles {roles_list} """ """ -Every role has its own configuration options. Which are each listed below. +Every role has its own configuration options, which are each listed below. For more information, see the [inventory guide](../../manual/inventory.md). @@ -350,8 +363,10 @@ For more information, see the [inventory guide](../../manual/inventory.md). `clan.admin.allowedkeys` - This means there are two equivalent ways to set the `allowedkeys` option. Either via a nixos module or via the inventory interface. - **But it is recommended to keep together `imports` and `config` to preserve locality of the module configuration.** + This means there are two equivalent ways to set the `allowedkeys` option. + Either via a nixos module or via the inventory interface. + **But it is recommended to keep together `imports` and `config` to preserve + locality of the module configuration.** === "Inventory" @@ -383,7 +398,11 @@ For more information, see the [inventory guide](../../manual/inventory.md). return "" -clan_modules_descr = """Clan modules are [NixOS modules](https://wiki.nixos.org/wiki/NixOS_modules) which have been enhanced with additional features provided by Clan, with certain option types restricted to enable configuration through a graphical interface. +clan_modules_descr = """ +Clan modules are [NixOS modules](https://wiki.nixos.org/wiki/NixOS_modules) +which have been enhanced with additional features provided by Clan, with +certain option types restricted to enable configuration through a graphical +interface. !!! note "🔹" Modules with this indicator support the [inventory](../../manual/inventory.md) feature. @@ -391,12 +410,12 @@ clan_modules_descr = """Clan modules are [NixOS modules](https://wiki.nixos.org/ """ -def render_categories(categories: list[str], frontmatter: Frontmatter) -> str: - cat_info = frontmatter.categories_info +def render_categories( + categories: list[str], categories_info: dict[str, CategoryInfo] +) -> str: res = """
""" for cat in categories: - color = cat_info[cat]["color"] - # description = cat_info[cat]["description"] + color = categories_info[cat]["color"] res += f"""
{cat} @@ -406,6 +425,56 @@ def render_categories(categories: list[str], frontmatter: Frontmatter) -> str: return res +def produce_clan_service_docs() -> None: + if not CLAN_MODULES_VIA_SERVICE: + msg = f"Environment variables are not set correctly: $CLAN_MODULES_VIA_SERVICE={CLAN_MODULES_VIA_SERVICE}" + raise ClanError(msg) + + if not CLAN_CORE_PATH: + msg = f"Environment variables are not set correctly: $CLAN_CORE_PATH={CLAN_CORE_PATH}" + raise ClanError(msg) + + if not OUT: + msg = f"Environment variables are not set correctly: $out={OUT}" + raise ClanError(msg) + + with Path(CLAN_MODULES_VIA_SERVICE).open() as f3: + service_links: dict[str, dict[str, dict[str, Any]]] = json.load(f3) + + for module_name, module_info in service_links.items(): + output = f"# {module_name}\n\n" + # output += f"`clan.modules.{module_name}`\n" + output += f"*{module_info['manifest']['description']}*\n" + + fm = Frontmatter("") + # output += "## Categories\n\n" + output += render_categories( + module_info["manifest"]["categories"], fm.categories_info + ) + output += "\n---\n\n## Roles\n" + + output += f"The {module_name} module has the following roles:\n\n" + + for role_name, _ in module_info["roles"].items(): + output += f"- {role_name}\n" + + for role_name, role_filename in module_info["roles"].items(): + output += print_options( + role_filename, + f"## Options for the `{role_name}` role", + "This role has no configuration", + replace_prefix=f"clan.{module_name}", + ) + + outfile = Path(OUT) / f"clanServices/{module_name}.md" + outfile.parent.mkdir( + parents=True, + exist_ok=True, + ) + with outfile.open("w") as of: + of.write(output) + + def produce_clan_modules_docs() -> None: if not CLAN_MODULES_VIA_NIX: msg = f"Environment variables are not set correctly: $CLAN_MODULES_VIA_NIX={CLAN_MODULES_VIA_NIX}" @@ -456,11 +525,11 @@ def produce_clan_modules_docs() -> None: # 2. Description from README.md if frontmatter.description: - output += f"**{frontmatter.description}**\n\n" + output += f"*{frontmatter.description}*\n\n" # 3. Categories from README.md output += "## Categories\n\n" - output += render_categories(frontmatter.categories, frontmatter) + output += render_categories(frontmatter.categories, frontmatter.categories_info) output += "\n---\n\n" # 3. README.md content @@ -785,7 +854,7 @@ def options_docs_from_tree( root: Option, init_level: int = 1, prefix: list[str] | None = None ) -> str: """ - Render the options from the tree structure. + eender the options from the tree structure. Args: root (Option): The root option node. @@ -829,5 +898,6 @@ if __name__ == "__main__": # produce_inventory_docs() produce_clan_modules_docs() + produce_clan_service_docs() produce_clan_modules_frontmatter_docs()