forked from clan/clan-core
Add package function-schema and module-schema. Add check for module jsonschema.
This commit is contained in:
parent
e08342a6f3
commit
a48df5b993
@ -46,7 +46,6 @@
|
|||||||
syncthing = import ./syncthing nixosTestArgs;
|
syncthing = import ./syncthing nixosTestArgs;
|
||||||
wayland-proxy-virtwl = import ./wayland-proxy-virtwl nixosTestArgs;
|
wayland-proxy-virtwl = import ./wayland-proxy-virtwl nixosTestArgs;
|
||||||
};
|
};
|
||||||
schemaTests = pkgs.callPackages ./schemas.nix { inherit self; };
|
|
||||||
|
|
||||||
flakeOutputs =
|
flakeOutputs =
|
||||||
lib.mapAttrs' (
|
lib.mapAttrs' (
|
||||||
@ -58,7 +57,7 @@
|
|||||||
self'.legacyPackages.homeConfigurations or { }
|
self'.legacyPackages.homeConfigurations or { }
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
{ inherit renderClanOptions; } // nixosTests // schemaTests // flakeOutputs;
|
{ inherit renderClanOptions; } // nixosTests // flakeOutputs;
|
||||||
legacyPackages = {
|
legacyPackages = {
|
||||||
nixosTests =
|
nixosTests =
|
||||||
let
|
let
|
||||||
|
@ -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
|
|
@ -21,7 +21,6 @@
|
|||||||
root-password = ./root-password;
|
root-password = ./root-password;
|
||||||
thelounge = ./thelounge.nix;
|
thelounge = ./thelounge.nix;
|
||||||
vm-user = ./vm-user.nix;
|
vm-user = ./vm-user.nix;
|
||||||
waypipe = ./waypipe.nix;
|
|
||||||
xfce = ./xfce.nix;
|
xfce = ./xfce.nix;
|
||||||
xfce-vm = ./xfce-vm.nix;
|
xfce-vm = ./xfce-vm.nix;
|
||||||
zt-tcp-relay = ./zt-tcp-relay.nix;
|
zt-tcp-relay = ./zt-tcp-relay.nix;
|
||||||
|
@ -34,6 +34,10 @@
|
|||||||
'';
|
'';
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
default = [ ];
|
default = [ ];
|
||||||
|
example = [
|
||||||
|
"folder1"
|
||||||
|
"folder2"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -68,7 +68,6 @@ nav:
|
|||||||
- reference/clanModules/syncthing.md
|
- reference/clanModules/syncthing.md
|
||||||
- reference/clanModules/thelounge.md
|
- reference/clanModules/thelounge.md
|
||||||
- reference/clanModules/vm-user.md
|
- reference/clanModules/vm-user.md
|
||||||
- reference/clanModules/waypipe.md
|
|
||||||
- reference/clanModules/xfce-vm.md
|
- reference/clanModules/xfce-vm.md
|
||||||
- reference/clanModules/xfce.md
|
- reference/clanModules/xfce.md
|
||||||
- reference/clanModules/zt-tcp-relay.md
|
- reference/clanModules/zt-tcp-relay.md
|
||||||
|
@ -38,20 +38,7 @@ let
|
|||||||
) clanModules;
|
) clanModules;
|
||||||
|
|
||||||
clanModulesReadmes = builtins.mapAttrs (
|
clanModulesReadmes = builtins.mapAttrs (
|
||||||
module_name: _module:
|
module_name: _module: self.lib.modules.getDescription module_name
|
||||||
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
|
|
||||||
) clanModules;
|
) clanModules;
|
||||||
|
|
||||||
# clanCore docs
|
# clanCore docs
|
||||||
|
@ -6,6 +6,6 @@
|
|||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
jsonschema = import ./jsonschema { inherit lib; };
|
jsonschema = import ./jsonschema { inherit lib; };
|
||||||
|
modules = import ./description.nix { inherit clan-core; };
|
||||||
buildClan = import ./build-clan { inherit clan-core lib nixpkgs; };
|
buildClan = import ./build-clan { inherit clan-core lib nixpkgs; };
|
||||||
}
|
}
|
||||||
|
19
lib/description.nix
Normal file
19
lib/description.nix
Normal 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;
|
||||||
|
}
|
@ -17,7 +17,10 @@ let
|
|||||||
location: ${lib.concatStringsSep "." option.loc}
|
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);
|
filterExcluded = lib.filter (opt: !isExcludedOption opt);
|
||||||
|
|
||||||
@ -67,6 +70,10 @@ rec {
|
|||||||
option:
|
option:
|
||||||
let
|
let
|
||||||
default = lib.optionalAttrs (option ? default) { inherit (option) default; };
|
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 = lib.optionalAttrs (option ? description) {
|
||||||
description = option.description.text or option.description;
|
description = option.description.text or option.description;
|
||||||
};
|
};
|
||||||
@ -93,7 +100,7 @@ rec {
|
|||||||
];
|
];
|
||||||
optionsList = filterExcluded optionsList';
|
optionsList = filterExcluded optionsList';
|
||||||
in
|
in
|
||||||
default // description // { anyOf = map parseOption optionsList; }
|
default // example // description // { anyOf = map parseOption optionsList; }
|
||||||
|
|
||||||
# handle nested options (not a submodule)
|
# handle nested options (not a submodule)
|
||||||
else if !option ? _type then
|
else if !option ? _type then
|
||||||
@ -116,6 +123,7 @@ rec {
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
default
|
default
|
||||||
|
// example
|
||||||
// description
|
// description
|
||||||
// {
|
// {
|
||||||
anyOf = [
|
anyOf = [
|
||||||
@ -128,63 +136,77 @@ rec {
|
|||||||
option.type.name == "bool"
|
option.type.name == "bool"
|
||||||
# return jsonschema property definition for bool
|
# return jsonschema property definition for bool
|
||||||
then
|
then
|
||||||
default // description // { type = "boolean"; }
|
default // example // description // { type = "boolean"; }
|
||||||
|
|
||||||
# parse float
|
# parse float
|
||||||
else if
|
else if
|
||||||
option.type.name == "float"
|
option.type.name == "float"
|
||||||
# return jsonschema property definition for float
|
# return jsonschema property definition for float
|
||||||
then
|
then
|
||||||
default // description // { type = "number"; }
|
default // example // description // { type = "number"; }
|
||||||
|
|
||||||
# parse int
|
# parse int
|
||||||
else if
|
else if
|
||||||
(option.type.name == "int" || option.type.name == "positiveInt")
|
(option.type.name == "int" || option.type.name == "positiveInt")
|
||||||
# return jsonschema property definition for int
|
# return jsonschema property definition for int
|
||||||
then
|
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
|
# parse string
|
||||||
else if
|
else if
|
||||||
option.type.name == "str"
|
option.type.name == "str"
|
||||||
# return jsonschema property definition for string
|
# return jsonschema property definition for string
|
||||||
then
|
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
|
# parse string
|
||||||
else if
|
else if
|
||||||
option.type.name == "path"
|
option.type.name == "path"
|
||||||
# return jsonschema property definition for path
|
# return jsonschema property definition for path
|
||||||
then
|
then
|
||||||
default // description // { type = "string"; }
|
default // example // description // { type = "string"; }
|
||||||
|
|
||||||
# parse anything
|
# parse anything
|
||||||
else if
|
else if
|
||||||
option.type.name == "anything"
|
option.type.name == "anything"
|
||||||
# return jsonschema property definition for anything
|
# return jsonschema property definition for anything
|
||||||
then
|
then
|
||||||
default // description // { type = allBasicTypes; }
|
default // example // description // { type = allBasicTypes; }
|
||||||
|
|
||||||
# parse unspecified
|
# parse unspecified
|
||||||
else if
|
else if
|
||||||
option.type.name == "unspecified"
|
option.type.name == "unspecified"
|
||||||
# return jsonschema property definition for unspecified
|
# return jsonschema property definition for unspecified
|
||||||
then
|
then
|
||||||
default // description // { type = allBasicTypes; }
|
default // example // description // { type = allBasicTypes; }
|
||||||
|
|
||||||
# parse raw
|
# parse raw
|
||||||
else if
|
else if
|
||||||
option.type.name == "raw"
|
option.type.name == "raw"
|
||||||
# return jsonschema property definition for raw
|
# return jsonschema property definition for raw
|
||||||
then
|
then
|
||||||
default // description // { type = allBasicTypes; }
|
default // example // description // { type = allBasicTypes; }
|
||||||
|
|
||||||
# parse enum
|
# parse enum
|
||||||
else if
|
else if
|
||||||
option.type.name == "enum"
|
option.type.name == "enum"
|
||||||
# return jsonschema property definition for enum
|
# return jsonschema property definition for enum
|
||||||
then
|
then
|
||||||
default // description // { enum = option.type.functor.payload; }
|
default // example // description // { enum = option.type.functor.payload; }
|
||||||
|
|
||||||
# parse listOf submodule
|
# parse listOf submodule
|
||||||
else if
|
else if
|
||||||
@ -192,6 +214,7 @@ rec {
|
|||||||
# return jsonschema property definition for listOf submodule
|
# return jsonschema property definition for listOf submodule
|
||||||
then
|
then
|
||||||
default
|
default
|
||||||
|
// example
|
||||||
// description
|
// description
|
||||||
// {
|
// {
|
||||||
type = "array";
|
type = "array";
|
||||||
@ -211,6 +234,7 @@ rec {
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
default
|
default
|
||||||
|
// example
|
||||||
// description
|
// description
|
||||||
// {
|
// {
|
||||||
type = "array";
|
type = "array";
|
||||||
@ -222,7 +246,7 @@ rec {
|
|||||||
(option.type.name == "listOf") && (option.type.functor.wrapped.name == "unspecified")
|
(option.type.name == "listOf") && (option.type.functor.wrapped.name == "unspecified")
|
||||||
# return jsonschema property definition for list
|
# return jsonschema property definition for list
|
||||||
then
|
then
|
||||||
default // description // { type = "array"; }
|
default // example // description // { type = "array"; }
|
||||||
|
|
||||||
# parse attrsOf submodule
|
# parse attrsOf submodule
|
||||||
else if
|
else if
|
||||||
@ -230,6 +254,7 @@ rec {
|
|||||||
# return jsonschema property definition for attrsOf submodule
|
# return jsonschema property definition for attrsOf submodule
|
||||||
then
|
then
|
||||||
default
|
default
|
||||||
|
// example
|
||||||
// description
|
// description
|
||||||
// {
|
// {
|
||||||
type = "object";
|
type = "object";
|
||||||
@ -242,6 +267,7 @@ rec {
|
|||||||
# return jsonschema property definition for attrs
|
# return jsonschema property definition for attrs
|
||||||
then
|
then
|
||||||
default
|
default
|
||||||
|
// example
|
||||||
// description
|
// description
|
||||||
// {
|
// {
|
||||||
type = "object";
|
type = "object";
|
||||||
@ -262,6 +288,7 @@ rec {
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
default
|
default
|
||||||
|
// example
|
||||||
// description
|
// description
|
||||||
// {
|
// {
|
||||||
type = "object";
|
type = "object";
|
||||||
|
@ -22,8 +22,8 @@
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
clanDir = lib.mkOption {
|
clanDir = lib.mkOption {
|
||||||
type = lib.types.either lib.types.path lib.types.str;
|
type = lib.types.path;
|
||||||
default = ".";
|
default = ./.;
|
||||||
description = ''
|
description = ''
|
||||||
the location of the flake repo, used to calculate the location of facts and secrets
|
the location of the flake repo, used to calculate the location of facts and secrets
|
||||||
'';
|
'';
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
./clan-cli/flake-module.nix
|
./clan-cli/flake-module.nix
|
||||||
./clan-vm-manager/flake-module.nix
|
./clan-vm-manager/flake-module.nix
|
||||||
./installer/flake-module.nix
|
./installer/flake-module.nix
|
||||||
|
./schemas/flake-module.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
perSystem =
|
perSystem =
|
||||||
|
66
pkgs/schemas/flake-module.nix
Normal file
66
pkgs/schemas/flake-module.nix
Normal 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
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user