build-inventory: move inventory and inventoryClass into explizitly different folders
This commit is contained in:
173
lib/modules/inventory/frontmatter/default.nix
Normal file
173
lib/modules/inventory/frontmatter/default.nix
Normal file
@@ -0,0 +1,173 @@
|
||||
{ lib, clanLib }:
|
||||
let
|
||||
# Trim the .nix extension from a filename
|
||||
trimExtension = name: builtins.substring 0 (builtins.stringLength name - 4) name;
|
||||
|
||||
jsonWithoutHeader = clanLib.jsonschema {
|
||||
includeDefaults = true;
|
||||
header = { };
|
||||
};
|
||||
|
||||
getModulesSchema =
|
||||
{
|
||||
modules,
|
||||
clan-core,
|
||||
pkgs,
|
||||
}:
|
||||
lib.mapAttrs
|
||||
(
|
||||
_moduleName: rolesOptions:
|
||||
lib.mapAttrs (_roleName: options: jsonWithoutHeader.parseOptions options { }) rolesOptions
|
||||
)
|
||||
(
|
||||
clanLib.evalClan.evalClanModulesWithRoles {
|
||||
allModules = modules;
|
||||
inherit pkgs clan-core;
|
||||
}
|
||||
);
|
||||
|
||||
evalFrontmatter =
|
||||
{
|
||||
moduleName,
|
||||
instanceName,
|
||||
resolvedRoles,
|
||||
allModules,
|
||||
}:
|
||||
lib.evalModules {
|
||||
modules = [
|
||||
(getFrontmatter allModules.${moduleName} moduleName)
|
||||
./interface.nix
|
||||
{
|
||||
constraints.imports = [
|
||||
(lib.modules.importApply ../constraints {
|
||||
inherit moduleName resolvedRoles instanceName;
|
||||
allRoles = getRoles "inventory.modules" allModules moduleName;
|
||||
})
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
# For Documentation purposes only
|
||||
frontmatterOptions =
|
||||
(lib.evalModules {
|
||||
modules = [
|
||||
./interface.nix
|
||||
{
|
||||
constraints.imports = [
|
||||
(lib.modules.importApply ../constraints {
|
||||
resolvedRoles = { };
|
||||
moduleName = "{moduleName}";
|
||||
instanceName = "{instanceName}";
|
||||
allRoles = [ "{roleName}" ];
|
||||
})
|
||||
];
|
||||
}
|
||||
];
|
||||
}).options;
|
||||
|
||||
migratedModules = [ "admin" ];
|
||||
|
||||
makeModuleNotFoundError =
|
||||
serviceName:
|
||||
if builtins.elem serviceName migratedModules then
|
||||
''
|
||||
(Legacy) ClanModule not found: '${serviceName}'.
|
||||
|
||||
Please update your configuration to use this module via 'inventory.instances'
|
||||
See: https://docs.clan.lol/guides/clanServices/
|
||||
''
|
||||
else
|
||||
''
|
||||
(Legacy) ClanModule not found: '${serviceName}'.
|
||||
|
||||
Make sure the module is added to inventory.modules.${serviceName}
|
||||
'';
|
||||
# This is a legacy function
|
||||
# Old modules needed to define their roles by directory
|
||||
# This means if this function gets anything other than a string/path it will throw
|
||||
getRoles =
|
||||
_scope: allModules: serviceName:
|
||||
let
|
||||
module = allModules.${serviceName} or (throw (makeModuleNotFoundError serviceName));
|
||||
moduleType = (lib.typeOf module);
|
||||
checked =
|
||||
if
|
||||
builtins.elem moduleType [
|
||||
"string"
|
||||
"path"
|
||||
]
|
||||
then
|
||||
true
|
||||
else
|
||||
throw "(Legacy) ClanModule must be a 'path' or 'string' pointing to a directory: Got 'typeOf inventory.modules.${serviceName}' => ${moduleType} ";
|
||||
modulePath = lib.seq checked module + "/roles";
|
||||
checkedPath =
|
||||
if builtins.pathExists modulePath then
|
||||
modulePath
|
||||
else
|
||||
throw ''
|
||||
(Legacy) ClanModule must have a 'roles' directory'
|
||||
|
||||
Fixes:
|
||||
- Provide a 'roles' subdirectory
|
||||
- Use the newer 'clan.service' modules. (Recommended)
|
||||
'';
|
||||
in
|
||||
lib.seq checkedPath lib.mapAttrsToList (name: _value: trimExtension name) (
|
||||
lib.filterAttrs (name: type: type == "regular" && lib.hasSuffix ".nix" name) (
|
||||
builtins.readDir (checkedPath)
|
||||
)
|
||||
);
|
||||
|
||||
checkConstraints = args: (evalFrontmatter args).config.constraints.assertions;
|
||||
|
||||
getReadme =
|
||||
modulepath: modulename:
|
||||
let
|
||||
readme = modulepath + "/README.md";
|
||||
readmeContents =
|
||||
if (builtins.pathExists readme) then
|
||||
(builtins.readFile readme)
|
||||
else
|
||||
throw "No README.md found for module ${modulename} (expected at ${readme})";
|
||||
in
|
||||
readmeContents;
|
||||
|
||||
getFrontmatter =
|
||||
modulepath: modulename:
|
||||
let
|
||||
content = getReadme modulepath modulename;
|
||||
parts = lib.splitString "---" content;
|
||||
# Partition the parts into the first part (the readme content) and the rest (the metadata)
|
||||
parsed = builtins.partition ({ index, ... }: if index >= 2 then false else true) (
|
||||
lib.filter ({ index, ... }: index != 0) (lib.imap0 (index: part: { inherit index part; }) parts)
|
||||
);
|
||||
meta = builtins.fromTOML (builtins.head parsed.right).part;
|
||||
in
|
||||
if (builtins.length parts >= 3) then
|
||||
meta
|
||||
else
|
||||
throw ''
|
||||
TOML Frontmatter not found in README.md for module ${modulename}
|
||||
|
||||
Please add the following to the top of your README.md:
|
||||
|
||||
---
|
||||
description = "Your description here"
|
||||
categories = [ "Your categories here" ]
|
||||
features = [ "inventory" ]
|
||||
---
|
||||
...rest of your README.md...
|
||||
'';
|
||||
in
|
||||
{
|
||||
inherit
|
||||
frontmatterOptions
|
||||
getModulesSchema
|
||||
getFrontmatter
|
||||
|
||||
checkConstraints
|
||||
getRoles
|
||||
;
|
||||
}
|
||||
84
lib/modules/inventory/frontmatter/interface.nix
Normal file
84
lib/modules/inventory/frontmatter/interface.nix
Normal file
@@ -0,0 +1,84 @@
|
||||
{
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
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"
|
||||
"Audio"
|
||||
"Video"
|
||||
"Development"
|
||||
"Education"
|
||||
"Game"
|
||||
"Graphics"
|
||||
"Social"
|
||||
"Network"
|
||||
"Office"
|
||||
"Science"
|
||||
"System"
|
||||
"Settings"
|
||||
"Utility"
|
||||
"Uncategorized"
|
||||
]
|
||||
);
|
||||
};
|
||||
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 [
|
||||
"experimental"
|
||||
"inventory"
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
constraints = mkOption {
|
||||
default = { };
|
||||
description = ''
|
||||
Constraints 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 {
|
||||
modules = [
|
||||
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user