Docs/frontmatter: init automatic reference for frontmatter

This commit is contained in:
Johannes Kirschbauer
2024-11-19 10:35:13 +01:00
parent 2ac2cc2aff
commit cfe9dbf117
8 changed files with 123 additions and 77 deletions

View File

@@ -104,6 +104,7 @@ nav:
- reference/clanModules/zerotier-static-peers.md
- reference/clanModules/zerotier.md
- reference/clanModules/zt-tcp-relay.md
- reference/clanModules/frontmatter/index.md
- CLI:
- reference/cli/index.md

View File

@@ -16,6 +16,13 @@
evalClanModules = self.lib.evalClanModules;
};
# Frontmatter for clanModules
clanModulesFrontmatter =
let
docs = pkgs.nixosOptionsDoc { options = self.lib.modules.frontmatterOptions; };
in
docs.optionsJSON;
clanModulesFileInfo = pkgs.writeText "info.json" (builtins.toJSON jsonDocs.clanModules);
# clanModulesReadmes = pkgs.writeText "info.json" (builtins.toJSON jsonDocs.clanModulesReadmes);
# clanModulesMeta = pkgs.writeText "info.json" (builtins.toJSON jsonDocs.clanModulesMeta);
@@ -56,8 +63,6 @@
buildInputs = [
pkgs.python3
self'.packages.clan-cli
# TODO: see postFixup clan-cli/default.nix:L188
self'.packages.clan-cli.propagatedBuildInputs
];
}
''
@@ -65,6 +70,8 @@
export CLAN_CORE_DOCS=${jsonDocs.clanCore}/share/doc/nixos/options.json
# A file that contains the links to all clanModule docs
export CLAN_MODULES=${clanModulesFileInfo}
# Frontmatter format for clanModules
export CLAN_MODULES_FRONTMATTER_DOCS=${clanModulesFrontmatter}/share/doc/nixos/options.json
# buildClan options
export BUILD_CLAN_PATH=${buildClanOptions}/share/doc/nixos/options.json

View File

@@ -34,6 +34,7 @@ from clan_cli.errors import ClanError
# Get environment variables
CLAN_CORE_PATH = Path(os.environ["CLAN_CORE_PATH"])
CLAN_CORE_DOCS = Path(os.environ["CLAN_CORE_DOCS"])
CLAN_MODULES_FRONTMATTER_DOCS = os.environ.get("CLAN_MODULES_FRONTMATTER_DOCS")
CLAN_MODULES = os.environ.get("CLAN_MODULES")
BUILD_CLAN_PATH = os.environ.get("BUILD_CLAN_PATH")
@@ -155,6 +156,63 @@ Your can customize your machines behavior with the configuration [options](#modu
options_head = "\n## Module Options\n"
def produce_clan_modules_frontmatter_docs() -> None:
if not CLAN_MODULES_FRONTMATTER_DOCS:
msg = f"Environment variables are not set correctly: $CLAN_CORE_DOCS={CLAN_CORE_DOCS}"
raise ClanError(msg)
if not OUT:
msg = f"Environment variables are not set correctly: $out={OUT}"
raise ClanError(msg)
with Path(CLAN_MODULES_FRONTMATTER_DOCS).open() as f:
options: dict[str, dict[str, Any]] = json.load(f)
# header
output = """# Frontmatter
Every clan module has a `frontmatter` section within its readme. It provides machine readable metadata about the module.
!!! example
The used format is `TOML`
The content is separated by `---` and the frontmatter must be placed at the very top of the `README.md` file.
```toml
---
description = "A description of the module"
categories = ["category1", "category2"]
[constraints]
roles.client.max = 10
roles.server.min = 1
---
# Readme content
...
```
"""
output += """"## Overview
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():
if option_name == "_module.args":
continue
output += render_option(option_name, info)
outfile = Path(OUT) / "clanModules/frontmatter/index.md"
outfile.parent.mkdir(
parents=True,
exist_ok=True,
)
with outfile.open("w") as of:
of.write(output)
def produce_clan_core_docs() -> None:
if not CLAN_CORE_DOCS:
msg = f"Environment variables are not set correctly: $CLAN_CORE_DOCS={CLAN_CORE_DOCS}"
@@ -259,12 +317,10 @@ def produce_clan_modules_docs() -> None:
for module_name, options_file in links.items():
readme_file = CLAN_CORE_PATH / "clanModules" / module_name / "README.md"
print(module_name, readme_file)
with readme_file.open() as f:
readme = f.read()
frontmatter: Frontmatter
frontmatter, readme_content = extract_frontmatter(readme, str(readme_file))
print(frontmatter, readme_content)
modules_index += build_option_card(module_name, frontmatter)
@@ -386,7 +442,6 @@ Each attribute is documented below
"""
with Path(BUILD_CLAN_PATH).open() as f:
options: dict[str, dict[str, Any]] = json.load(f)
# print(options)
for option_name, info in options.items():
# Skip underscore options
if option_name.startswith("_"):
@@ -479,3 +534,5 @@ if __name__ == "__main__": #
produce_clan_core_docs()
produce_clan_modules_docs()
produce_clan_modules_frontmatter_docs()

View File

@@ -63,7 +63,7 @@ description = "Module A"
This is the example module that does xyz.
```
See the [frontmatter reference](#frontmatter-reference) for all supported attributes.
See the [Full Frontmatter reference](../reference/clanModules/frontmatter/index.md) further details and all supported attributes.
## Roles
@@ -149,60 +149,3 @@ Assuming that there is a common code path or a common interface between `server`
imports = [ ../common.nix ];
}
```
## Frontmatter Reference
`description` (**Required** `String`)
: Short description of the module
`categories` (Optional `[ String ]`)
: default `[ "Uncategorized" ]`
Categories are used for Grouping and searching.
While initial oriented on [freedesktop](https://specifications.freedesktop.org/menu-spec/latest/category-registry.html) the following categories are allowed
- AudioVideo
- Audio
- Video
- Development
- Education
- Game
- Graphics
- Social
- Network
- Office
- Science
- System
- Settings
- Utility
- Uncategorized
`features` (Optional `[ String ]`)
: default `[]`
Clans Features that the module implements support for.
Available feature flags are:
- `inventory`
!!! warning "Important"
Every ClanModule, that specifies `features = [ "inventory" ]` MUST have at least one role.
Many modules use `roles/default.nix` which registers the role `default`.
If you are a clan module author and your module has only one role where you cannot determine the name, then we would like you to follow the convention.
`constraints.roles.<roleName>.<constraintType>` (Optional `int`) (Experimental)
: Contraints for the module
The following example requires exactly one `server`
and supports up to `7` clients
```md
---
constraints.roles.server.eq = 1
constraints.roles.client.max = 7
---
```

View File

@@ -18,6 +18,9 @@ in
options.roles = lib.mapAttrs (
_name: _:
mkOption {
description = ''
Sub-attributes of `${_name}` are constraints for the role.
'';
default = { };
type = types.submoduleWith {
modules = [
@@ -26,10 +29,16 @@ in
max = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
Maximum number of instances of this role that can be assigned to a module of this type.
'';
};
min = mkOption {
type = types.int;
default = 0;
description = ''
Minimum number of instances of this role that must at least be assigned to a module of this type.
'';
};
};
}

View File

@@ -20,8 +20,7 @@ let
];
};
frontmatterDocsOptions =
lib.optionAttrSetToDocList
frontmatterOptions =
(lib.evalModules {
specialArgs = {
moduleName = "{moduleName}";
@@ -91,7 +90,7 @@ in
{
inherit
evalFrontmatter
frontmatterDocsOptions
frontmatterOptions
getFrontmatter
getReadme

View File

@@ -11,11 +11,16 @@ in
description = mkOption {
type = types.str;
description = ''
A Short description of the module.
'';
};
categories = mkOption {
default = [ "Uncategorized" ];
description = ''
Categories are used for Grouping and searching.
While initial oriented on [freedesktop](https://specifications.freedesktop.org/menu-spec/latest/category-registry.html) the following categories are allowed
'';
type = types.listOf (
types.enum [
"AudioVideo"
@@ -38,6 +43,15 @@ in
};
features = mkOption {
default = [ ];
description = ''
Clans Features that the module implements support for.
!!! warning "Important"
Every ClanModule, that specifies `features = [ "inventory" ]` MUST have at least one role.
Many modules use `roles/default.nix` which registers the role `default`.
If you are a clan module author and your module has only one role where you cannot determine the name, then we would like you to follow the convention.
'';
type = types.listOf (
types.enum [
"inventory"
@@ -47,6 +61,19 @@ in
constraints = mkOption {
default = { };
description = ''
Contraints for the module
The following example requires exactly one `server`
and supports up to `7` clients
```md
---
constraints.roles.server.eq = 1
constraints.roles.client.max = 7
---
```
'';
type = types.submoduleWith {
inherit specialArgs;
modules = [

View File

@@ -22,6 +22,8 @@ let
header = { };
};
frontMatterSchema = jsonLib.parseOptions self.lib.modules.frontmatterOptions { };
inventorySchema = jsonLib.parseModule (import ../build-inventory/interface.nix);
renderSchema = pkgs.writers.writePython3Bin "render-schema" {
@@ -48,6 +50,7 @@ let
in
{
inherit
frontMatterSchema
inventorySchema
modulesSchema
renderSchema