Repository: NixOS/nix (C++)

Issue:
Mark experimental configuration settings programmatically
## Problem

Descriptions of experimental settings need the information on "experimentality" baked into the documentation, but this is double book keeping.

Example: https://github.com/NixOS/nix/blob/d93c1514ebcf71390c8a8070759487e5a40dcc6f/src/libstore/globals.hh#L351-L359

Given we merge https://github.com/NixOS/nix/pull/7798, that information can be displayed automatically, and removed without maintainer intervention if the feature becomes stable. It would also allow more convenient control over presentation, ensuring it will be consistent.



[latest Nix manual]: https://nixos.org/manual/nix/unstable/
[source]: https://github.com/NixOS/nix/tree/master/doc/manual/src
[open documentation issues and pull requests]: https://github.com/NixOS/nix/labels/documentation

## Proposal

- merge https://github.com/NixOS/nix/pull/7798
- adapt the [rendering helper for settings](https://github.com/NixOS/nix/blob/d93c1514ebcf71390c8a8070759487e5a40dcc6f/doc/manual/utils.nix#L42-L59) and the code outputting the structured data it works on accordingly

## Checklist

<!-- make sure this issue is not redundant or obsolete -->

- [x] checked [latest Nix manual] \([source])
- [x] checked [open documentation issues and pull requests] for possible duplicates

## Priorities

Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).



Discussion: Whoever does this, please feel free to talk to me!

- In aa663b7e89d3d02248d37ee9f68b52770b247018 (already merged) I added the basic infrastructure to track this info on settings
- https://github.com/NixOS/nix/pull/7798 is now adapted so experimental features have `xp-feature-${name}` anchors, ready to be linked from elsewhere.

Between those too, this ought to be fairly straight-forward.



Fix (patch):
diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix
index 0b3a238019b..d04eecf5572 100644
--- a/doc/manual/generate-manpage.nix
+++ b/doc/manual/generate-manpage.nix
@@ -31,19 +31,18 @@ let
 
       showSynopsis = command: args:
         let
-          showArgument = arg: "*${arg.label}*" + (if arg ? arity then "" else "...");
+          showArgument = arg: "*${arg.label}*" + optionalString (! arg ? arity) "...";
           arguments = concatStringsSep " " (map showArgument args);
         in ''
          `${command}` [*option*...] ${arguments}
         '';
 
-      maybeSubcommands = if details ? commands && details.commands != {}
-        then ''
+      maybeSubcommands = optionalString (details ? commands && details.commands != {})
+         ''
            where *subcommand* is one of the following:
 
            ${subcommands}
-         ''
-        else "";
+         '';
 
       subcommands = if length categories > 1
         then listCategories
@@ -65,12 +64,11 @@ let
         * [`${command} ${name}`](./${appendName filename name}.md) - ${subcmd.description}
       '';
 
-      maybeDocumentation =
-        if details ? doc
-        then replaceStrings ["@stores@"] [storeDocs] details.doc
-        else "";
+      maybeDocumentation = optionalString
+        (details ? doc)
+        (replaceStrings ["@stores@"] [storeDocs] details.doc);
 
-      maybeOptions = if details.flags == {} then "" else ''
+      maybeOptions = optionalString (details.flags != {}) ''
         # Options
 
         ${showOptions details.flags toplevel.flags}
@@ -80,15 +78,19 @@ let
         let
           allOptions = options // commonOptions;
           showCategory = cat: ''
-            ${if cat != "" then "**${cat}:**" else ""}
+            ${optionalString (cat != "") "**${cat}:**"}
 
             ${listOptions (filterAttrs (n: v: v.category == cat) allOptions)}
             '';
           listOptions = opts: concatStringsSep "\n" (attrValues (mapAttrs showOption opts));
           showOption = name: option:
             let
-              shortName = if option ? shortName then "/ `-${option.shortName}`" else "";
-              labels = if option ? labels then (concatStringsSep " " (map (s: "*${s}*") option.labels)) else "";
+              shortName = optionalString
+                (option ? shortName)
+                ("/ `-${option.shortName}`");
+              labels = optionalString
+                (option ? labels)
+                (concatStringsSep " " (map (s: "*${s}*") option.labels));
             in trim ''
               - `--${name}` ${shortName} ${labels}
 
diff --git a/doc/manual/utils.nix b/doc/manual/utils.nix
index 82544935a49..9043dd8cdba 100644
--- a/doc/manual/utils.nix
+++ b/doc/manual/utils.nix
@@ -42,7 +42,9 @@ rec {
   filterAttrs = pred: set:
     listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set));
 
-  showSetting = { useAnchors }: name: { description, documentDefault, defaultValue, aliases, value }:
+  optionalString = cond: string: if cond then string else "";
+
+  showSetting = { useAnchors }: name: { description, documentDefault, defaultValue, aliases, value, experimentalFeature }:
     let
       result = squash ''
           - ${if useAnchors
@@ -52,10 +54,28 @@ rec {
           ${indent "  " body}
         '';
 
+      experimentalFeatureNote = optionalString (experimentalFeature != null) ''
+          > **Warning**
+          > This setting is part of an
+          > [experimental feature](@docroot@/contributing/experimental-features.md).
+
+          To change this setting, you need to make sure the corresponding experimental feature,
+          [`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}),
+          is enabled.
+          For example, include the following in [`nix.conf`](#):
+
+          ```
+          extra-experimental-features = ${experimentalFeature}
+          ${name} = ...
+          ```
+        '';
+
       # separate body to cleanly handle indentation
       body = ''
           ${description}
 
+          ${experimentalFeatureNote}
+
           **Default:** ${showDefault documentDefault defaultValue}
 
           ${showAliases aliases}
@@ -74,7 +94,7 @@ rec {
         else "*machine-specific*";
 
       showAliases = aliases:
-          if aliases == [] then "" else
+          optionalString (aliases != [])
             "**Deprecated alias:** ${(concatStringsSep ", " (map (s: "`${s}`") aliases))}";
 
     in result;
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 63c7389dabb..f598ed4a8bc 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -328,16 +328,6 @@ public:
           users in `build-users-group`.
 
           UIDs are allocated starting at 872415232 (0x34000000) on Linux and 56930 on macOS.
-
-          > **Warning**
-          > This is an experimental feature.
-
-          To enable it, add the following to [`nix.conf`](#):
-
-          ```
-          extra-experimental-features = auto-allocate-uids
-          auto-allocate-uids = true
-          ```
         )"};
 
     Setting<uint32_t> startId{this,
@@ -367,16 +357,6 @@ public:
 
           Cgroups are required and enabled automatically for derivations
           that require the `uid-range` system feature.
-
-          > **Warning**
-          > This is an experimental feature.
-
-          To enable it, add the following to [`nix.conf`](#):
-
-          ```
-          extra-experimental-features = cgroups
-          use-cgroups = true
-          ```
         )"};
     #endif
 
diff --git a/src/libutil/config.cc b/src/libutil/config.cc
index 5ff8d91baec..a42f3a8493e 100644
--- a/src/libutil/config.cc
+++ b/src/libutil/config.cc
@@ -191,6 +191,10 @@ std::map<std::string, nlohmann::json> AbstractSetting::toJSONObject()
     std::map<std::string, nlohmann::json> obj;
     obj.emplace("description", description);
     obj.emplace("aliases", aliases);
+    if (experimentalFeature)
+        obj.emplace("experimentalFeature", *experimentalFeature);
+    else
+        obj.emplace("experimentalFeature", nullptr);
     return obj;
 }