infrastructure/modules/nixos/extranix/default.nix
Tom Hubrecht 6d81001b85
All checks were successful
Build all the nodes / geo02 (push) Successful in 59s
Build all the nodes / geo01 (push) Successful in 1m0s
Build all the nodes / hypervisor02 (push) Successful in 1m0s
Build all the nodes / hypervisor01 (push) Successful in 1m0s
Build all the nodes / netaccess01 (push) Successful in 19s
Build all the nodes / netcore00 (push) Successful in 19s
Build all the nodes / netcore02 (push) Successful in 20s
Build all the nodes / netcore01 (push) Successful in 20s
Build all the nodes / ap01 (push) Successful in 1m24s
Build all the nodes / bridge01 (push) Successful in 2m5s
Build all the nodes / build01 (push) Successful in 2m5s
Build all the nodes / hypervisor03 (push) Successful in 2m5s
Build all the nodes / cof02 (push) Successful in 2m12s
Build all the nodes / storage01 (push) Successful in 58s
Build all the nodes / tower01 (push) Successful in 56s
Build all the nodes / rescue01 (push) Successful in 1m0s
Build the shell / build-shell (push) Successful in 44s
Run pre-commit on all files / pre-commit (push) Successful in 46s
Build all the nodes / compute01 (push) Successful in 3m10s
Build all the nodes / web01 (push) Successful in 2m3s
Build all the nodes / web02 (push) Successful in 1m29s
Build all the nodes / web03 (push) Successful in 1m35s
Build all the nodes / vault01 (push) Successful in 1m0s
feat(extranix): Add theme option and update
2025-04-17 12:58:44 +02:00

359 lines
9.1 KiB
Nix

# SPDX-FileCopyrightText: 2024 Lubin Bailly <lubin.bailly@dgnum.eu>
# SPDX-FileCopyrightText: 2025 Tom Hubrecht <tom.hubrecht@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{
lib,
config,
pkgs,
...
}:
let
inherit (lib)
attrNames
concatStringsSep
escapeXML
evalModules
filter
flip
getAttr
hasPrefix
head
listToAttrs
mapAttrsToList
literalExpression
mkEnableOption
mkIf
mkOption
nameValuePair
optionAttrSetToDocList
optionalString
pathIsDirectory
pipe
removeAttrs
removePrefix
removeSuffix
;
inherit (lib.strings)
sanitizeDerivationName
;
inherit (lib.types)
attrs
attrsOf
deferredModule
listOf
nullOr
path
submodule
str
;
mkDocJSON =
module:
{
ignored-modules,
path-translations,
paths,
specialArgs,
...
}:
let
# NOTE: A big simplification of nixpkgs' make-options-doc
# It turns out that we only need the json output, and have
# no `baseOptionsJSON`, so the transformation done is `id`
mkOpts = (flip pipe) [
# Evaluate the modules
(modules: evalModules { inherit modules specialArgs; })
# Extract the options
(getAttr "options")
# Transform into a list of options
optionAttrSetToDocList
# Remove invisible and internal options
(filter (o: o.visible && !o.internal))
# Remove extraneous attributes
(builtins.map (
o:
nameValuePair o.name (
removeAttrs o [
"name"
"visible"
"internal"
]
)
))
# Transform back to an attribute set
listToAttrs
];
# INFO: Subtract options of the ignored modules from
# the complete set of options
ignored = attrNames (mkOpts ignored-modules);
options = removeAttrs (mkOpts (paths ++ ignored-modules)) ignored;
directories = builtins.map (getAttr "base") path-translations;
mkTranslation =
path:
let
# Get the file associated to the module
file = if pathIsDirectory path then path + "/default.nix" else path;
# Get the parent module set
matching = filter (m: hasPrefix m.base path) path-translations;
parent = head matching;
filePath = removePrefix parent.base file;
in
if matching == [ ] then
(throw "${file} is not a descendant of ${module}. Declared parents are: \n ${concatStringsSep "\n " directories}")
else
{
name = "<${filePath}>";
url = "${parent.url}/${filePath}";
};
in
pkgs.runCommand "options-extranix"
{
fileName = sanitizeDerivationName "options-${module}.json";
nativeBuildInputs = [ pkgs.jq ];
passAsFile = [ "result" ];
result = builtins.toJSON {
last_update = "-/-";
options = mapAttrsToList (
title:
{
default ? {
text = "";
},
description,
example ? {
text = "";
},
loc,
readOnly,
type,
declarations,
...
}:
{
inherit
loc
readOnly
title
type
;
descriptionHTML = pkgs.runCommandLocal "option-${title}.html" {
inherit description;
passAsFile = [ "description" ];
nativeBuildInputs = [ pkgs.pandoc ];
} "pandoc -f markdown-raw_html $descriptionPath > $out";
description = escapeXML description;
example = escapeXML example.text;
default = escapeXML default.text;
declarations = builtins.map mkTranslation declarations;
}
) options;
};
}
''
mkdir -p $out
jq -r '.options[].descriptionHTML | "--rawfile\n" + . + "\n" + .' $resultPath | xargs \
jq -c '.options |= map(.descriptionHTML as $desc | .descriptionHTML |= $ARGS.named.[$desc])' $resultPath \
> $out/$fileName
'';
website =
pkgs.runCommand "search-infra"
{
inherit (cfg) theme;
nativeBuildInputs = [ pkgs.hugo ];
config = builtins.toJSON cfg.settings;
passAsFile = [ "config" ];
data = pkgs.symlinkJoin {
name = "options-data";
paths = mapAttrsToList mkDocJSON cfg.modules;
};
}
''
# Setup the directory structure
mkdir themes && ln -s $theme themes/options-search
ln -s $configPath hugo.json
mkdir static
${optionalString (cfg.static != null) "cp -R ${cfg.static} static"}
ln -s $data static/data
# Build the website
hugo -d $out
'';
cfg = config.services.extranix;
in
{
options.services.extranix = {
enable = mkEnableOption "extranix documentation";
theme = mkOption {
type = path;
description = ''
Path to a hugo theme for the search website.
'';
};
modules = mkOption {
type = attrsOf (submodule {
options = {
specialArgs = mkOption {
type = attrs;
default = { };
description = ''
Special arguments to give to evalModules.
'';
};
paths = mkOption {
type = listOf deferredModule;
description = ''
Modules to from which to document options.
'';
};
ignored-modules = mkOption {
type = listOf deferredModule;
default = [ ];
description = ''
Modules required to make modules of `paths` valid.
'';
example = ''
import "''${infra-modulesPath}/module-list.nix"
'';
};
path-translations = mkOption {
type = listOf (submodule {
options = {
base = mkOption {
type = path;
description = ''
Base path of some module files to be documented.
'';
apply = v: "${removeSuffix "/" (builtins.toString v)}/";
};
url = mkOption {
type = str;
description = ''
Url root to use for files on this path.
'';
apply = removeSuffix "/";
};
};
});
description = ''
Rules to convert file paths to urls in the documentation to indicate where
the option is declared.
'';
};
};
});
description = ''
Sets of modules to be documented separately. The identifier to give for
`settings.params.release_current_stable` (which is the default module shown) is the key after
passing through `sanitizeDerivationName`.
'';
};
host = mkOption {
type = str;
description = ''
Hostname of the service.
'';
};
index = mkOption {
type = str;
default = head (attrNames cfg.modules);
defaultText = literalExpression "head (attrNames config.services.extranix.modules)";
description = ''
The main module to show when loading the website.
'';
apply = sanitizeDerivationName;
};
static = mkOption {
type = nullOr path;
default = null;
description = ''
Path to extra static files.
'';
};
settings = mkOption {
inherit (pkgs.formats.json { }) type;
description = ''
Settings of the hugo website.
'';
example = {
baseUrl = "https://example.org/";
title = "Module documentation";
languageCode = "en-us";
params = {
release_current_stable = "Some-Module";
logo = "logo.png";
footer_credits_line = ''
Based on <a href="https://github.com/mipmip/home-manager-option-search">Home Manager Option Search</a>
'';
footer_copyright_line = ''
Made by catvayor for the <a href="https://dgnum.eu">DGNum</a>.
'';
main_menu = [
{
name = ''Sources'';
url = "https://git.example.org/";
}
];
};
};
};
};
config = mkIf cfg.enable {
services = {
extranix.settings = {
theme = "options-search";
params = {
releases = mapAttrsToList (name: _: {
inherit name;
value = sanitizeDerivationName name;
}) cfg.modules;
release_current_stable = cfg.index;
};
};
nginx = {
enable = true;
virtualHosts.${cfg.host}.locations."/".root = website;
};
};
assertions = [
{
assertion = cfg.modules != { };
message = ''
`services.extranix` can't be enabled without any modules to document.
'';
}
];
};
}