Add package function-schema and module-schema. Add check for module jsonschema.
checks / checks (pull_request) Successful in 49s Details
checks / checks-impure (pull_request) Successful in 2m4s Details

This commit is contained in:
Luis Hebendanz 2024-05-01 23:16:17 +02:00
parent e08342a6f3
commit a48df5b993
13 changed files with 134 additions and 81 deletions

View File

@ -46,7 +46,6 @@
syncthing = import ./syncthing nixosTestArgs;
wayland-proxy-virtwl = import ./wayland-proxy-virtwl nixosTestArgs;
};
schemaTests = pkgs.callPackages ./schemas.nix { inherit self; };
flakeOutputs =
lib.mapAttrs' (
@ -58,7 +57,7 @@
self'.legacyPackages.homeConfigurations or { }
);
in
{ inherit renderClanOptions; } // nixosTests // schemaTests // flakeOutputs;
{ inherit renderClanOptions; } // nixosTests // flakeOutputs;
legacyPackages = {
nixosTests =
let

View File

@ -1,48 +0,0 @@
{
self,
runCommand,
check-jsonschema,
pkgs,
lib,
...
}:
let
clanModules.clanCore = self.nixosModules.clanCore;
baseModule = {
imports = (import (pkgs.path + "/nixos/modules/module-list.nix")) ++ [
{
nixpkgs.hostPlatform = "x86_64-linux";
clanCore.clanName = "dummy";
}
];
};
optionsFromModule =
module:
let
evaled = lib.evalModules {
modules = [
module
baseModule
];
};
in
evaled.options.clan;
clanModuleSchemas = lib.mapAttrs (
_: module: self.lib.jsonschema.parseOptions (optionsFromModule module)
) clanModules;
mkTest =
name: schema:
runCommand "schema-${name}" { } ''
${check-jsonschema}/bin/check-jsonschema \
--check-metaschema ${builtins.toFile "schema-${name}" (builtins.toJSON schema)}
touch $out
'';
in
lib.mapAttrs' (name: schema: {
name = "schema-${name}";
value = mkTest name schema;
}) clanModuleSchemas

View File

@ -21,7 +21,6 @@
root-password = ./root-password;
thelounge = ./thelounge.nix;
vm-user = ./vm-user.nix;
waypipe = ./waypipe.nix;
xfce = ./xfce.nix;
xfce-vm = ./xfce-vm.nix;
zt-tcp-relay = ./zt-tcp-relay.nix;

View File

@ -34,6 +34,10 @@
'';
type = lib.types.listOf lib.types.str;
default = [ ];
example = [
"folder1"
"folder2"
];
};
};

View File

@ -68,7 +68,6 @@ nav:
- reference/clanModules/syncthing.md
- reference/clanModules/thelounge.md
- reference/clanModules/vm-user.md
- reference/clanModules/waypipe.md
- reference/clanModules/xfce-vm.md
- reference/clanModules/xfce.md
- reference/clanModules/zt-tcp-relay.md

View File

@ -38,20 +38,7 @@ let
) clanModules;
clanModulesReadmes = builtins.mapAttrs (
module_name: _module:
let
readme = "${self}/clanModules/${module_name}/README.md";
readmeContents =
if
builtins.trace "Trying to get Module README.md for ${module_name} from ${readme}"
# TODO: Edge cases
(builtins.pathExists readme)
then
(builtins.readFile readme)
else
null;
in
readmeContents
module_name: _module: self.lib.modules.getDescription module_name
) clanModules;
# clanCore docs

View File

@ -6,6 +6,6 @@
}:
{
jsonschema = import ./jsonschema { inherit lib; };
modules = import ./description.nix { inherit clan-core; };
buildClan = import ./build-clan { inherit clan-core lib nixpkgs; };
}

19
lib/description.nix Normal file
View File

@ -0,0 +1,19 @@
{ clan-core, ... }:
{
getDescription =
modulename:
let
readme = "${clan-core}/clanModules/${modulename}/README.md";
readmeContents =
if
builtins.trace "Trying to get Module README.md for ${modulename} from ${readme}"
# TODO: Edge cases
(builtins.pathExists readme)
then
(builtins.readFile readme)
else
null;
in
readmeContents;
}

View File

@ -17,7 +17,10 @@ let
location: ${lib.concatStringsSep "." option.loc}
'';
isExcludedOption = option: (lib.elem (option.type.name or null) excludedTypes);
# Exclude the option if it's type is in the excludedTypes list
# or if the option has a defaultText attribute
isExcludedOption =
option: ((lib.elem (option.type.name or null) excludedTypes) || (option ? defaultText));
filterExcluded = lib.filter (opt: !isExcludedOption opt);
@ -67,6 +70,10 @@ rec {
option:
let
default = lib.optionalAttrs (option ? default) { inherit (option) default; };
example = lib.optionalAttrs (option ? example) {
examples =
if (builtins.typeOf option.example) == "list" then option.example else [ option.example ];
};
description = lib.optionalAttrs (option ? description) {
description = option.description.text or option.description;
};
@ -93,7 +100,7 @@ rec {
];
optionsList = filterExcluded optionsList';
in
default // description // { anyOf = map parseOption optionsList; }
default // example // description // { anyOf = map parseOption optionsList; }
# handle nested options (not a submodule)
else if !option ? _type then
@ -116,6 +123,7 @@ rec {
};
in
default
// example
// description
// {
anyOf = [
@ -128,63 +136,77 @@ rec {
option.type.name == "bool"
# return jsonschema property definition for bool
then
default // description // { type = "boolean"; }
default // example // description // { type = "boolean"; }
# parse float
else if
option.type.name == "float"
# return jsonschema property definition for float
then
default // description // { type = "number"; }
default // example // description // { type = "number"; }
# parse int
else if
(option.type.name == "int" || option.type.name == "positiveInt")
# return jsonschema property definition for int
then
default // description // { type = "integer"; }
default // example // description // { type = "integer"; }
# TODO: Add support for intMatching in jsonschema
# parse port type aka. "unsignedInt16"
else if option.type.name == "unsignedInt16" then
default // example // description // { type = "integer"; }
# parse string
else if
option.type.name == "str"
# return jsonschema property definition for string
then
default // description // { type = "string"; }
default // example // description // { type = "string"; }
# TODO: Add support for stringMatching in jsonschema
# parse stringMatching
else if lib.strings.hasPrefix "strMatching" option.type.name then
default // example // description // { type = "string"; }
# TODO: Add support for separatedString in jsonschema
else if lib.strings.hasPrefix "separatedString" option.type.name then
default // example // description // { type = "string"; }
# parse string
else if
option.type.name == "path"
# return jsonschema property definition for path
then
default // description // { type = "string"; }
default // example // description // { type = "string"; }
# parse anything
else if
option.type.name == "anything"
# return jsonschema property definition for anything
then
default // description // { type = allBasicTypes; }
default // example // description // { type = allBasicTypes; }
# parse unspecified
else if
option.type.name == "unspecified"
# return jsonschema property definition for unspecified
then
default // description // { type = allBasicTypes; }
default // example // description // { type = allBasicTypes; }
# parse raw
else if
option.type.name == "raw"
# return jsonschema property definition for raw
then
default // description // { type = allBasicTypes; }
default // example // description // { type = allBasicTypes; }
# parse enum
else if
option.type.name == "enum"
# return jsonschema property definition for enum
then
default // description // { enum = option.type.functor.payload; }
default // example // description // { enum = option.type.functor.payload; }
# parse listOf submodule
else if
@ -192,6 +214,7 @@ rec {
# return jsonschema property definition for listOf submodule
then
default
// example
// description
// {
type = "array";
@ -211,6 +234,7 @@ rec {
};
in
default
// example
// description
// {
type = "array";
@ -222,7 +246,7 @@ rec {
(option.type.name == "listOf") && (option.type.functor.wrapped.name == "unspecified")
# return jsonschema property definition for list
then
default // description // { type = "array"; }
default // example // description // { type = "array"; }
# parse attrsOf submodule
else if
@ -230,6 +254,7 @@ rec {
# return jsonschema property definition for attrsOf submodule
then
default
// example
// description
// {
type = "object";
@ -242,6 +267,7 @@ rec {
# return jsonschema property definition for attrs
then
default
// example
// description
// {
type = "object";
@ -262,6 +288,7 @@ rec {
};
in
default
// example
// description
// {
type = "object";

View File

@ -22,8 +22,8 @@
'';
};
clanDir = lib.mkOption {
type = lib.types.either lib.types.path lib.types.str;
default = ".";
type = lib.types.path;
default = ./.;
description = ''
the location of the flake repo, used to calculate the location of facts and secrets
'';

View File

@ -4,6 +4,7 @@
./clan-cli/flake-module.nix
./clan-vm-manager/flake-module.nix
./installer/flake-module.nix
./schemas/flake-module.nix
];
perSystem =

View File

@ -0,0 +1,66 @@
{ self, ... }:
{
perSystem =
{ pkgs, lib, ... }:
let
clanModules = self.clanModules;
# Uncomment if you only want one module to be available
# clanModules = {
# syncthing = self.clanModules.syncthing;
# };
baseModule = {
imports = (import (pkgs.path + "/nixos/modules/module-list.nix")) ++ [
{
nixpkgs.hostPlatform = "x86_64-linux";
clanCore.clanName = "dummy";
}
];
};
optionsFromModule =
modulename: module:
let
evaled = lib.evalModules {
modules = [
module
baseModule
self.nixosModules.clanCore
];
};
in
if (evaled.options.clan ? "${modulename}") then evaled.options.clan.${modulename} else { };
clanModuleSchemas = lib.mapAttrs (
modulename: module: self.lib.jsonschema.parseOptions (optionsFromModule modulename module)
) clanModules;
clanModuleFunctionSchemas = lib.mapAttrsFlatten (modulename: module: {
name = modulename;
description = self.lib.modules.getDescription modulename;
parameters = self.lib.jsonschema.parseOptions (optionsFromModule modulename module);
}) clanModules;
in
rec {
checks = {
module-schema = pkgs.runCommand "schema-checks" { } ''
${pkgs.check-jsonschema}/bin/check-jsonschema \
--check-metaschema ${packages.module-schema}
touch $out
'';
};
packages = {
module-schema = pkgs.runCommand "jsonschema" { } ''
MSCHEMA=${builtins.toFile "module-schemas.json" (builtins.toJSON clanModuleSchemas)}
cp "$MSCHEMA" $out
'';
function-schema = pkgs.runCommand "function-schema" { } ''
FSCHEMA=${builtins.toFile "function-schemas.json" (builtins.toJSON clanModuleFunctionSchemas)}
cp "$FSCHEMA" $out
'';
};
};
}