docs/options: Add option reference generator

Evaluate custom module options through a synthetic system, stabilize
configuration-dependent defaults for rendering, and commit the generated
reference beside its updater.
This commit is contained in:
2026-08-01 23:59:17 +01:00
parent 4084dfe64e
commit b44095f1ed
7 changed files with 472 additions and 9 deletions
+45 -3
View File
@@ -100,6 +100,40 @@ let
] ++ defs;
};
# JSON dump of the custom `my.*` options, rendered to docs/ by `update-docs-options`.
# Built from a minimal synthetic system (the shared modules are applied to every system,
# so any eval exposes the same option declarations) rather than a real box, so defaults
# don't pick up a specific host's values.
optionsDoc =
let
eval = mkSystem {
name = "options-doc";
config' = {
system = "x86_64-linux";
nixpkgs = "mine";
home-manager = "mine";
hmNixpkgs = "mine";
docCustom = false;
assignments = { };
};
defs = [ ];
};
pkgs = pkgs'.mine."x86_64-linux";
in
(pkgs.nixosOptionsDoc {
options = { my = eval.options.my; };
warningsAreErrors = false;
# Rewrite store-path declarations to repo-relative so the reference can link module
# files. The internal `asX` build-target options are dropped by nixosOptionsDoc itself.
transformOptions = opt: opt // {
declarations = map
(decl:
let m = builtins.match ".*-source/(.*)" (toString decl); in
if m != null then builtins.head m else decl)
opt.declarations;
};
}).optionsJSON;
assignmentOpts = with lib.types; { name, config, ... }: {
options = {
name = mkOpt' str name "Name of assignment.";
@@ -154,8 +188,11 @@ let
options = {
inherit (commonOpts) system nixpkgs home-manager;
hmNixpkgs = commonOpts.nixpkgs;
# This causes a (very slow) docs rebuild on every change to a module's options it seems
# TODO: Currently broken with infinite recursion...
# Routes the custom modules into `baseModules` so the NixOS manual documents them. The old
# infinite-recursion is gone, but enabling this makes every system build regenerate the
# manual, and it documents everything the modules transitively import — including third-party
# modules that aren't doc-clean (e.g. `services.sharry`). Prefer the generated
# `nixos.optionsDoc` reference (`docs/reference/nixos-options.md`) instead.
docCustom = mkBoolOpt' false "Whether to document nixfiles' custom NixOS modules.";
assignments = mkOpt' (attrsOf (submoduleWith {
@@ -197,6 +234,11 @@ in
description = "All network assignments.";
readOnly = true;
};
optionsDoc = mkOption {
type = package;
description = "nixosOptionsDoc JSON dump of the custom `my.*` module options.";
readOnly = true;
};
vpns = {
l2 = mkOpt' (attrsOf (submodule l2MeshOpts)) { } "Layer 2 meshes.";
};
@@ -224,7 +266,7 @@ in
];
nixos = {
inherit allAssignments;
inherit allAssignments optionsDoc;
};
};
}
+19 -4
View File
@@ -3,7 +3,7 @@ let
inherit (builtins) substring match;
inherit (lib)
nameValuePair optional optionalString optionalAttrs mapAttrs' mapAttrsToList concatStringsSep
concatMapStringsSep mkIf;
concatMapStringsSep mkIf mkOption literalExpression;
inherit (lib.my) mkOpt' mkBoolOpt';
jobType = with lib.types; submodule ({ name, ... }@args:
@@ -15,12 +15,22 @@ let
repo = mkOpt' str null "borg repository URL";
passFile = mkOpt' (nullOr str) null "Path to file containing passphrase";
archivePrefix = mkOpt' str "${config.networking.hostName}-${name}-" "Prefix to start new archives with";
archivePrefix = mkOption {
type = str;
default = "${config.networking.hostName}-${name}-";
defaultText = literalExpression ''"''${config.networking.hostName}-''${name}-"'';
description = "Prefix to start new archives with";
};
dateFormat = mkOpt' str "+%Y-%m-%dT%H:%M:%S" "Format passed to the date command";
compression = mkOpt' str "zstd,3" "Compression options";
lvs = mkOpt' (listOf str) null "Thin LVs to backup (vg/lv format)";
prune = {
pattern = mkOpt' str "sh:${cfg.archivePrefix}*" "Borg pattern to select archives for pruning";
pattern = mkOption {
type = str;
default = "sh:${cfg.archivePrefix}*";
defaultText = literalExpression ''"sh:''${config.archivePrefix}*"'';
description = "Borg pattern to select archives for pruning";
};
keep = mkOpt' (attrsOf (either int str)) { } "Borg pruning params";
};
@@ -129,7 +139,12 @@ in
thinToolsPackage = mkOpt' package pkgs.thin-provisioning-tools "Package containing thin-provisioning-tools";
# Really we should use the version from the overlay, but the package is quite far behind...
# Not bothering to update until Borg 2.0 releases
package = mkOpt' package inputs.borgthin.packages.${config.nixpkgs.system}.borgthin "borgthin package";
package = mkOption {
type = package;
default = inputs.borgthin.packages.${config.nixpkgs.system}.borgthin;
defaultText = literalExpression "inputs.borgthin.packages.\${system}.borgthin";
description = "borgthin package";
};
jobs = mkOpt' (attrsOf jobType) { } "borgthin jobs";
};
+3
View File
@@ -190,6 +190,9 @@ let
type = unspecified;
default = ext;
visible = "shallow";
# Build-target plumbing: the default evaluates a whole alternate system, so keep
# it out of the generated option docs (see `optionsDoc` in flake.nix).
internal = true;
description = "Configuration as ${desc}.";
};
in
+7 -2
View File
@@ -1,6 +1,6 @@
{ lib, pkgs, config, ... }:
let
inherit (lib) mkMerge mkIf mkForce genAttrs concatMapStringsSep;
inherit (lib) mkMerge mkIf mkForce genAttrs concatMapStringsSep mkOption literalExpression;
inherit (lib.my) mkOpt' mkBoolOpt';
cfg = config.my.netboot;
@@ -47,7 +47,12 @@ in
server = {
enable = mkBoolOpt' false "Whether a netboot server should be enabled.";
ip = mkOpt' str null "IP clients should connect to via TFTP.";
host = mkOpt' str config.networking.fqdn "Hostname clients should connect to over HTTP / NFS.";
host = mkOption {
type = str;
default = config.networking.fqdn;
defaultText = literalExpression "config.networking.fqdn";
description = "Hostname clients should connect to over HTTP / NFS.";
};
allowedPrefixes = mkOpt' (listOf str) null "Prefixes clients should be allowed to connect from (NFS).";
installer = {
storeSize = mkOpt' str "16GiB" "Total allowed writable size of store.";